Permalink
Fetching contributors…
Cannot retrieve contributors at this time
1137 lines (1063 sloc) 30.8 KB
/**
\file dmxtsPacket
\brief Packet demuxer for mpeg TS
copyright : (C) 2005-2009 by mean
email : fixounet@free.fr
Warning : For PSI packet (PAT & PMT) we assume they fit into one TS packet
Else we fail miserably.
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "ADM_default.h"
#include "dmxTSPacket.h"
#include "dmx_mpegstartcode.h"
#include "ADM_getbits.h"
#include "ADM_tsCrc.cpp"
/**
\fn tsPacket
\brief ctor
*/
tsPacket::tsPacket(void)
{
extraCrap=0; // =4 for ts2
}
/**
\fn tsPacket
\brief dtor
*/
tsPacket::~tsPacket()
{
close();
}
/**
\fn score
\return the number of TS packet of size TS_PACKET_SIZE+extraData in a raw, nbTry tries
*/
static int score(fileParser *parser, int nbTry, int extraData)
{
int count=0;
int round=nbTry;
while(round--)
{
if(parser->read8i()!=TS_MARKER) break;
count++;
parser->forward(TS_PACKET_LEN-1+extraData);
}
return count;
}
/**
\fn open
\brief dtor
*/
bool tsPacket::open(const char *filenames,FP_TYPE append)
{
_file=new fileParser();
if(!_file->open(filenames,&append))
{
printf("[DmxPS] cannot open %s\n",filenames);
delete _file;
_file=NULL;
return false;
}
_size=_file->getSize();
// Detect TS1/TS2...
printf("[TsPacket] Detecting TS/TS2...\n");
uint8_t r;
int tryMe=250;
while(tryMe--)
{
r=_file->read8i();
if(r==TS_MARKER) break;
if(_file->end())
{
tryMe=0;
}
}
if(!tryMe)
{
printf("[TsPacket] Cannot sync ???\n");
return true;
}
uint64_t startPos=getPos()-1;
int score1,score2;
setPos(startPos);
score1=score(_file,20,0);
setPos(startPos);
score2=score(_file,20,4);
printf("[TsPacket] Score : 188:%d, 192:%d out of 20\n",score1,score2);
if(score2 && score2>score1)
{
printf("[TsPacket] Probably TS2 (192)...\n");
extraCrap=4;
}else
{
printf("[TsPacket] Probably TS1 (188)...\n");
}
setPos(0);
return true;
}
/**
\fn close
\brief dtor
*/
bool tsPacket::close(void)
{
if(_file)
{
delete _file;
_file=NULL;
}
return true;
}
/**
\fn getPos
*/
uint64_t tsPacket::getPos(void)
{
uint64_t pos;
_file->getpos(&pos);
return pos;
}
/**
\fn setPos
*/
bool tsPacket::setPos(uint64_t pos)
{
if(!_file->setpos(pos))
{
printf("[tsPacket] Cannot seek to %" PRIx64"\n", pos);
return false;
}
return true;
}
/**
\fn getSinglePacket
\brief Read s TS packet, make sure it starts & ends by TS marker (0x47)
*/
bool tsPacket::getSinglePacket(uint8_t *buffer)
{
#define MAX_SKIPPED_PACKET 300*100 // 150*180*100=~ 2.2 MBytes
#define MAX_SEARCH 2048
int count=0;
again:
while(1)
{
uint8_t r=_file->read8i();
if(r==TS_MARKER || _file->end()) break;
count++;
if(count>MAX_SEARCH)
{
printf("[Mpeg TS] Sync definitevly lost\n");
return false;
}
#ifdef TS_DEBUG1
uint64_t pos=_file->getpos(&pos);
printf("[%02d] count=%d at 0x%x\n",r,count,pos);
#endif
}
if(_file->end()==true)
{
//printf("[Mpeg Ts] End of file reached\n");
return false;
}
_file->read32(TS_PACKET_LEN-1,buffer); // 184-1
if(extraCrap) _file->forward(extraCrap);
uint8_t r=_file->peek8i();
if(r!=TS_MARKER)
{
printf("[tsPacket] Sync lost (0x%x)\n",r);
#ifdef TS_DEBUG1
#endif
goto again;
}
return true;
}
/**
\fn getNextPsiPacket
\brief Take a raw packet of type pid & remove the header
*/
bool tsPacket::getNextPacket_NoHeader(uint32_t pid,TSpacketInfo *pkt,bool psi)
{
uint8_t scratch[188+4];
int count=0;
nextPack:
if(false==getSinglePacket(scratch)) return false;
uint32_t id=scratch[1]+((scratch[0]&0x1F)<<8);
//#define TS_DEBUG2
#ifdef TS_DEBUG2
printf("[Demuxer] Looking for 0x%x found 0x%x\n",pid,id);
#endif
count++;
if(count>MAX_SKIPPED_PACKET) return false;
if(id!=pid)
{
updateStats(scratch);
goto nextPack;
}
pkt->pid=pid;
#ifdef TS_DEBUG2
printf("[**************> Found matching pid 0x%x\n",pid);
#endif
int payloadUnitStart=scratch[0]&0x40;
int fieldControl=(scratch[2]>>4)&3;
int continuity=(scratch[2]&0xf);
int len=TS_PACKET_LEN-4; // useful datas
pkt->continuityCounter=continuity;
pkt->payloadStart=payloadUnitStart;
// Adaptation field
// 11 Adapt+payload
// 10 Adapt only
// 01 Payload only
// 00 forbidden
if(!(fieldControl & 1))
{
// No payload, continue
#ifdef TS_DEBUG2
printf("[Demuxer] No payload\n");
#endif
goto nextPack;
}
uint8_t *start,*end;
start=scratch+3;
end=scratch+TS_PACKET_LEN-1;
if((fieldControl & 2)|| psi) // Adaptation layer
{
int payloadSize=*start++;
start+=payloadSize;
}
int size=(int)(end-start);
if(size<=0)
{
#ifdef TS_DEBUG2
printf("[Demuxer] size=%d\n",size);
#endif
goto nextPack;
}
memcpy(pkt->payload,start,size);
pkt->payloadSize=size;
uint64_t pos;
_file->getpos(&pos);
pkt->startAt=pos-extraCrap-TS_PACKET_LEN;
return true;
}
/**
\fn updateStats
\brief Hook for tracker
*/
bool tsPacket::updateStats(uint8_t *data)
{
return true;
}
/**
\fn getNextPsiPacket
\brief Take a raw packet of type pid & remove the header (PSI)
*/
#ifdef VERBOSE_PSI
#define DUMMY(x,n) {dummy=bits.get(n);printf("[TS]: "#x" =0x%x %d\n",dummy,dummy);}
#else
#define DUMMY(x,n) {dummy=bits.get(n);}
#endif
bool tsPacket::getNextPSI(uint32_t pid,TS_PSIpacketInfo *psi)
{
int multiPacketPsi=0;
int nbRetries=0;
uint64_t startOffset=0;
TSpacketInfo pkt;
nextPack2:
if(nbRetries && pkt.startAt-startOffset>(1<<25)) // max. 32 MiB
{
ADM_warning("Giving up after %d retries, consumed %" PRId64" bytes\n",nbRetries,pkt.startAt-startOffset);
return false;
}
if(false==getNextPacket_NoHeader(pid,&pkt,true)) return false;
if(!nbRetries)
startOffset=pkt.startAt;
nbRetries++;
uint32_t tableId;
uint32_t sectionLength=0;
uint32_t transportStreamId=0;
uint32_t dummy;
getBits bits(pkt.payloadSize,pkt.payload);
DUMMY(tableId,8);
int section_syntax_indicator=bits.get(1);
#ifdef VERBOSE_PSI
if(!section_syntax_indicator)
ADM_warning("Syntax section indicator not set\n");
#endif
if(bits.get(1)) // Private bit
{
ADM_warning("Section syntax is set to private\n");
goto nextPack2;
}
bits.get(2); // 2 Reserved bits
sectionLength=bits.get(12); // Section Length
#if 1
if(sectionLength+3>pkt.payloadSize)
{
if(!multiPacketPsi)
ADM_warning("[MpegTs] Multi Packet PSI ? sectionLength=%d, len=%d\n",sectionLength,pkt.payloadSize);
multiPacketPsi++;
goto nextPack2;
}
if(multiPacketPsi>1)
ADM_warning("Multi packet PSI warning repeated %d times\n",multiPacketPsi);
multiPacketPsi=0;
#endif
transportStreamId=bits.get(16);// transportStreamId
#ifdef VERBOSE_PSI
printf("[MpegTs] Section length =%d\n",sectionLength);
printf("[MpegTs] transportStreamId =%d\n",transportStreamId);
#endif
bits.skip(2); // ignored
DUMMY(VersionNumber,5); // Version number
DUMMY(CurrentNext,1); // Current Next indicator
psi->count=bits.get(8); // Section number
psi->countMax=bits.get(8); // Section last number
#ifdef VERBOSE_PSI
printf("[MpegTs] Count=%d CountMax=%d\n",psi->count,psi->countMax);
#endif
if(psi->count!=psi->countMax) return false; // we dont handle split psi at the moment
uint8_t *c=pkt.payload+sectionLength-4+3;
// Verify CRC
uint32_t crc1=mpegTsCRC(pkt.payload,sectionLength-4+3);
uint32_t crc2=(c[0]<<24)+(c[1]<<16)+(c[2]<<8)+c[3];
if(crc1!=crc2)
{
printf("[MpegTs] getNextPSI bad checksum :%04x vs %04x\n",crc1,crc2);
goto nextPack2;
}
int hdr=(8+16+16+8+8+8)/8;
int payloadsize=sectionLength-4-5; // Remove checksum & header
if(payloadsize<4) goto nextPack2;
psi->payloadSize=payloadsize;
memcpy(psi->payload,pkt.payload+hdr,psi->payloadSize);
return true;
}
/**
\fn getNextPES
\brief Returns the next PES packet
*/
bool tsPacket::getNextPES(TS_PESpacket *pes)
{
#if 1
#define zprintf(...) {}
#else
#define zprintf ADM_info
#endif
int nbRetries=0,counter=0;
uint64_t startOffset=0;
TSpacketInfo pkt;
pkt.startAt=0;
pes->fresh=false;
nextPack3:
if(pkt.startAt-startOffset>(1<<25)) // max. 32 MiB
{
ADM_warning("Giving up after %d retries, consumed %" PRId64" bytes\n",nbRetries,pkt.startAt-startOffset);
return false;
}
// Sync at source
if(false==getNextPacket_NoHeader(pes->pid,&pkt,false)) return false;
if(!nbRetries)
startOffset=pkt.startAt;
nbRetries++;
// If it does not contain a payload start continue
bool mark=false;
uint32_t code=(pkt.payload[0]<<24)+(pkt.payload[1]<<16)+(pkt.payload[2]<<8)+pkt.payload[3];
if((code&0xffffffC0)==0x1C0) mark=true;
zprintf("Mark=%x\n",code);
if(!pkt.payloadStart && !mark)
{
#if 0
printf("[Ts Demuxer] Pes for Pid = 0x%x (%d) does not contain payload start\n",pes->pid,pes->pid);
#endif
counter++;
goto nextPack3;
}
if(counter>1)
zprintf("Payload start for Pid = 0x%x (%d) found after %d retries\n",pes->pid,pes->pid,counter);
counter=0;
//____________________
//____________________
// 1- Start Headers
//____________________
//____________________
zprintf("[TS Demuxer] Code=0x%x pid=0x%x\n",code,pes->pid);
if((code&0xffffff00)!=0x100)
{
#if 0
printf("[Ts Demuxer] No PES startcode at 0x%" PRIx64"\n",pkt.startAt);
printf("0x:%02x %02x %02x %02x\n",pkt.payload[4],pkt.payload[5],pkt.payload[6],pkt.payload[7]);
#endif
goto nextPack3;
}
if(nbRetries>1)
ADM_info("PES startcode found at 0x%" PRIx64" after %d retries\n",pkt.startAt,nbRetries);
//mixDump(pkt.payload,pkt.payloadSize);
uint32_t pesPacketLen=(pkt.payload[4]<<8)+(pkt.payload[5]);
zprintf("[TS Demuxer] Pes Packet Len=%d\n",pesPacketLen);
pes->payloadSize=0;
pes->addData(pkt.payloadSize,pkt.payload);
pes->startAt=pkt.startAt;
while(1)
{
uint64_t pos;
_file->getpos(&pos);
if(false==getNextPacket_NoHeader(pes->pid,&pkt,false)) return false;
if(!pkt.payloadStart)
{
pes->addData(pkt.payloadSize,pkt.payload);
}else
{
_file->setpos(pos);
break;
}
if(pes->payloadLimit>TS_PES_MAX_LIMIT)
{
printf("[Ts Demuxer] Pes Packet too big\n");
goto nextPack3;
}
}
//____________________
// Now decode PES header (extra memcpy)
//____________________
zprintf("[Ts Demuxer] Full size :%d\n",pes->payloadSize);
if(false==decodePesHeader(pes))
goto nextPack3;
//
zprintf("[Ts Demuxer] Found PES of len %d, offset=%d\n",pes->payloadSize, pes->offset);
pes->fresh=true;
return true;
}
/**
\fn decodePesHeader
*/
#define fail(x) {ADM_warning("[Ts Demuxer]*********" x"*******\n");return false;}
bool tsPacket::decodePesHeader(TS_PESpacket *pes)
{
uint8_t *start=pes->payload+6;
uint8_t *end=pes->payload+pes->payloadSize;
uint8_t stream=pes->payload[3];
uint32_t packLen=(pes->payload[4]<<8)+(pes->payload[5]);
int align=0,c;
pes->dts=ADM_NO_PTS;
pes->pts=ADM_NO_PTS;
if(pes->payloadSize<(4+2+1+2))
{
ADM_warning("[Ts] Pes size too small\n");
return false;
}
while(*start==0xff && start<end) start++; // Padding
if(start>=end) fail("too much padding");
c=*start++;
if((c&0xc0)!=0x80) fail("No Mpeg2 marker");
uint32_t ptsdts,len;
if(c & 4) align=1;
c=*start++; // PTS/DTS
//printf("%x ptsdts\n",c
ptsdts=c>>6;
// header len
len=*start++;
int available=(int)(end-start);
switch(ptsdts)
{
case 2: // PTS=1 DTS=0
{
if(available<5) fail("Not enough bytes for PTS");
uint64_t pts1,pts2,pts0;
// printf("\n PTS10\n");
pts0=start[0];
pts1=(start[1]<<8)+start[2];
pts2=(start[3]<<8)+start[4];
pes->pts=(pts1>>1)<<15;
pes->pts+=pts2>>1;
pes->pts+=(((pts0&6)>>1)<<30);
}
break;
case 3: // PTS=1 DTS=1
if(available<10) fail("Not enough bytes for PTS/DTS");
#define PTS11_ADV 10 // nut monkey
if(len>=PTS11_ADV)
{
uint32_t skip=PTS11_ADV;
uint64_t pts1,pts2,dts,pts0;
// printf("\n PTS10\n");
pts0=start[0];
pts1=(start[1]<<8)+start[2];
pts2=(start[3]<<8)+start[4];
pes->pts=(pts1>>1)<<15;
pes->pts+=pts2>>1;
pes->pts+=(((pts0&6)>>1)<<30);
pts0=start[5];
pts1=(start[6]<<8)+start[7];
pts2=(start[8]<<8)+start[9];
pes->dts=(pts1>>1)<<15;
pes->dts+=pts2>>1;
pes->dts+=(((pts0&6)>>1)<<30);
}
break;
case 1:
fail("unvalid pts/dts");
break;
case 0:
// printf("\n PTS00\n");
break; // no pts nor dts
}
start+=len;
int sizeCheck=pes->payloadSize-6;
int tail=0;
pes->offset=start-pes->payload;
if(packLen)
{
//printf("***Zimbla***\n");
if(packLen<sizeCheck)
{
tail=sizeCheck-packLen;
pes->payloadSize-=tail;
ADM_warning("[TS Packet]extra crap at the end %d\n",tail);
}
else
if(packLen>sizeCheck)
{
ADM_warning("[TS Packet] PackLen=%d, avalailble=%d\n",packLen,sizeCheck);
fail("Pes too long");
}
}
zprintf("[decodePesHeader] payloadSize=%d, offset=%d \n",pes->payloadSize,pes->offset);
if(pes->offset>pes->payloadSize)
{
ADM_warning("[decodePesHeader] Inconsistent size, dropping\n");
return false;
}
return true;
}
/**
\fn getPacket
*/
bool tsPacket::getPacket(uint32_t maxSize, uint8_t *pid, uint32_t *packetSize,uint64_t *opts,uint64_t *odts,uint8_t *buffer,uint64_t *startAt)
{
return true;
}
/**
\fn getNextPid
*/
bool tsPacket::getNextPid(int *pid)
{
uint8_t scratch[188+4];
int count=0;
nextPackx:
if(false==getSinglePacket(scratch)) return false;
uint32_t id=scratch[1]+((scratch[0]&0x1F)<<8);
count++;
if(count>MAX_SKIPPED_PACKET) return false;
*pid=id;
int payloadUnitStart=scratch[0]&0x40;
int fieldControl=(scratch[2]>>4)&3;
int continuity=(scratch[2]&0xf);
int len=TS_PACKET_LEN-4; // useful datas
if(!(fieldControl & 1))
{
goto nextPackx;
}
uint8_t *start,*end;
start=scratch+3;
end=scratch+TS_PACKET_LEN-1;
if((fieldControl & 2)) // Adaptation layer
{
int payloadSize=*start++;
start+=payloadSize;
}
int size=(int)(end-start);
if(size<=0)
{
goto nextPackx;
}
return true;
}
/**
\fn tsPacket
*/
tsPacketLinear::tsPacketLinear(uint32_t pid) : tsPacket()
{
oldStartAt=0xfffffff;
oldBufferLen=0;
pesPacket=new TS_PESpacket(pid);
consumed=0;
eof=false;
}
/**
\fn ~tsPacket
*/
tsPacketLinear::~tsPacketLinear()
{
if(pesPacket) delete pesPacket;
pesPacket=NULL;
}
/**
\fn refill
*/
bool tsPacketLinear::refill(void)
{
// In case a startcode spawns across 2 packets
// we have to keep track of the old one
oldBufferDts=pesPacket->dts;
oldBufferPts=pesPacket->pts;
oldStartAt=pesPacket->startAt;
oldBufferLen=pesPacket->payloadSize;
if(false==getNextPES(pesPacket))
{
printf("[tsPacketLinear] Refill failed for pid : 0x%x (%d)\n",pesPacket->pid,pesPacket->pid);
return false;
}
return true;
}
#ifndef TS_PACKET_INLINE
/**
\fn readi8
*/
uint8_t tsPacketLinear::readi8(void)
{
consumed++;
if(pesPacket->offset<pesPacket->payloadSize)
{
return pesPacket->payload[pesPacket->offset++];
}
if(false==refill())
{
eof=1;
return 0;
}
return pesPacket->payload[pesPacket->offset++];
}
/**
\fn readi16
*/
uint16_t tsPacketLinear::readi16(void)
{
if(pesPacket->offset+1<pesPacket->payloadSize)
{
uint8_t *r=pesPacket->payload+pesPacket->offset;
uint16_t v=(r[0]<<8)+r[1];;
pesPacket->offset+=2;
consumed+=2;
return v;
}
return (readi8()<<8)+readi8();
}
#endif
/**
\fn readi32
*/
uint32_t tsPacketLinear::readi32(void)
{
if(pesPacket->offset+3<pesPacket->payloadSize)
{
uint8_t *p=pesPacket->payload+pesPacket->offset;
uint32_t v=(p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3];
pesPacket->offset+=4;
consumed+=4;
return v;
}
return (readi16()<<16)+readi16();
}
/**
\fn forward
*/
bool tsPacketLinear::forward(uint32_t v)
{
next:
uint32_t delta=pesPacket->payloadSize-pesPacket->offset;
if(v>100*1000)
{
ADM_assert(0);
}
if(v<=delta)
{
pesPacket->offset+=v;
consumed+=v;
return true;
}
// v>delta
v-=delta;
consumed+=delta;
if(!refill()) return false;
goto next;
}
/**
\fn bool read(uint32_t len, uint8_t *buffer);
\brief
*/
bool tsPacketLinear::read(uint32_t len, uint8_t *out)
{
#if 0
printf("[tsRead] Size 0x%x %d\n",len,len);
#endif
// Enough already ?
while(len)
{
uint32_t avail=pesPacket->payloadSize-pesPacket->offset;
uint32_t chunk=avail;
if(chunk>len) chunk=len;
#if 0
printf("len:%ld avail:%ld chunk %ld index:%d size:%d\n",
len,avail,chunk,bufferIndex,bufferLen);
#endif
memcpy(out,pesPacket->payload+pesPacket->offset,chunk);
pesPacket->offset+=chunk;
len-=chunk;
out+=chunk;
consumed+=chunk;
if(pesPacket->payloadSize==pesPacket->offset)
{
//printf("Refill\n");
if(false==refill()) return false;
}
}
return true;
}
/**
\fn getInfo
\brief Returns info about the current (or previous if it spawns) packet.
It is expected that the caller will do -4 to the index to get the start of the
startCode
*/
bool tsPacketLinear::getInfo(dmxPacketInfo *info,int startCodeLength)
{
//#warning FIXME
if(pesPacket->offset<startCodeLength)
{
info->startAt=this->oldStartAt;
info->offset=oldBufferLen;
info->pts=oldBufferPts;
info->dts=oldBufferDts;
}else
{
info->startAt=pesPacket->startAt;
info->offset=pesPacket->offset;
info->pts=pesPacket->pts;
info->dts=pesPacket->dts;
}
return true;
};
/**
\fn invalidatePtsDts
\brief in some cases (HDRunner/HDCPro), a pes contains multiple video frames. This call consumes pts
and dts so that the multiple frames do not have the same pts/dts.
*/
bool tsPacketLinear::invalidatePtsDts(int startCodeLength)
{
if(pesPacket->offset<startCodeLength)
{
oldBufferPts=ADM_NO_PTS;
oldBufferDts=ADM_NO_PTS;
}else
{
pesPacket->pts=ADM_NO_PTS;
pesPacket->dts=ADM_NO_PTS;
}
return true;
}
/**
\fn seek
\brief Async jump
*/
bool tsPacketLinear::seek(uint64_t packetStart, uint32_t offset)
{
if(!_file->setpos(packetStart))
{
printf("[tsPacket] Cannot seek to %" PRIx64"\n",packetStart);
return 0;
}
if(!refill())
{
printf("[tsPacketLinear] Seek to %" PRIx64":%" PRIx32" failed\n",packetStart,offset);
return false;
}
ADM_assert(offset<pesPacket->payloadSize);
pesPacket->offset=offset;
return true;
}
/**
\fn getConsumed
\brief returns the # of bytes consumed since the last call
*/
uint32_t tsPacketLinear::getConsumed(void)
{
uint32_t c=consumed;
return c;
}
/**
\fn setConsumed
\brief set consumed bytes
*/
bool tsPacketLinear::setConsumed(uint32_t v)
{
consumed=v;
return true;
}
/**
\fn changePid
\brief change the pid of the stream we read (used when probing all tracks)
*/
bool tsPacketLinear::changePid(uint32_t pid)
{
pesPacket->pid=pid;
pesPacket->offset=pesPacket->payloadSize;
return true;
}
/* ********************************************************* */
/**
\fn tsPacketLinearTracker
*/
tsPacketLinearTracker::tsPacketLinearTracker(uint32_t pid,listOfTsAudioTracks *audio) : tsPacketLinear(pid)
{
int nb=audio->size();
otherPes=new TS_PESpacket(0);
totalTracks=nb;
if(!nb)
{
this->stats=NULL;
return;
}
// Convert ADM_TS_TRACKS to ststa
stats=new packetTSStats[nb];
memset(stats,0,sizeof(packetTSStats)*nb);
for(int i=0;i<nb;i++)
{
stats[i].pid=(*audio)[i].esId;
stats[i].startDts=ADM_NO_PTS;
}
}
/**
\fn findStartCode
\brief Must check stillOk after calling this
*/
int tsPacketLinearTracker::findStartCode(void)
{
#define likely(x) x
#define unlikely(x) x
unsigned int last=0xfffff;
unsigned int cur=0xffff;
int startCode=0;
while(this->stillOk())
{
last=cur;
cur=this->readi16();
if(likely(last&0xff)) continue;
if(unlikely(!last)) // 00 00 , need 01 xx
{
if((cur>>8)==1)
{
startCode=cur&0xff;
break;
}
}
if(unlikely(!(last&0xff))) // xx 00 need 00 01
{
if(cur==1)
{
startCode=this->readi8();
break;
}
}
}
return startCode;
}
/**
* \fn fourByteStartCode
* @param four
* @return
*/
int tsPacketLinearTracker::findStartCode2(bool &fourByteStartCode)
{
#define likely(x) x
#define unlikely(x) x
unsigned int prev=0xfffff;
unsigned int last=0xfffff;
unsigned int cur=0xffff;
int startCode=0;
fourByteStartCode=false;
while(this->stillOk())
{
prev=last;
last=cur;
cur=this->readi16();
if(likely(last&0xff)) continue;
if(unlikely(!last)) // 00 00 , need 01 xx
{
if((cur>>8)==1)
{
startCode=cur&0xff;
if(!(prev&0xff))
fourByteStartCode=true;
break;
}
}
if(unlikely(!(last&0xff))) // xx 00 need 00 01
{
if(cur==1)
{
startCode=this->readi8();
if(!(last>>8))
fourByteStartCode=true;
break;
}
}
}
return startCode;
}
/**
\fn ~tsPacketLinearTracker
*/
tsPacketLinearTracker::~tsPacketLinearTracker()
{
if(otherPes) delete otherPes;
otherPes=NULL;
if(stats) delete [] stats;
stats=NULL;
}
/**
\fn tsPacketLinearTracker
*/
bool tsPacketLinearTracker::getStats(uint32_t *nb,packetTSStats **stats)
{
*nb=totalTracks;
*stats=this->stats;
return true;
}
/**
\fn updateStats
\brief Decode a bit of PES header, just enough to get PTS or DTS
if only PTS is there we assume PTS=DTS
*/
bool tsPacketLinearTracker::updateStats(uint8_t *scratch)
{
uint32_t id=scratch[1]+((scratch[0]&0x1F)<<8);
int found=-1;
// Look if it is a pid we are interested in
for(int i=0;i<totalTracks;i++)
if(id==stats[i].pid) found=i;
if(found==-1) return false;
int payloadUnitStart=scratch[0]&0x40;
int fieldControl=(scratch[2]>>4)&3;
int continuity=(scratch[2]&0xf);
if(!payloadUnitStart) return false; // no PES start in here...
// Adaptation field
// 11 Adapt+payload
// 10 Adapt only
// 01 Payload only
// 00 forbidden
if(!(fieldControl & 1))
{
// No payload, continue
#ifdef TS_DEBUG2
printf("[Demuxer] No payload\n");
#endif
return true;
}
uint8_t *start,*end;
start=scratch+3;
end=scratch+TS_PACKET_LEN-1;
if((fieldControl & 2)) // Adaptation layer
{
int payloadSize=*start++;
start+=payloadSize;
}
int size=(int)(end-start);
if(size<=0)
{
#ifdef TS_DEBUG2
printf("[Demuxer] size=%d\n",size);
#endif
return true;
}
// Look into pes packet starting at "start"
otherPes->payloadSize=size;
uint64_t pos;
_file->getpos(&pos);
//*************************
// ENTER PES
//*************************
otherPes->startAt=pos-extraCrap-TS_PACKET_LEN;
// PES startcode ?
if( start[0] || start[1] || start[2]!=1) return false;
int stream=start[3];
// Skip startcode + length
start+=6;
// Update our stats
stats[found].startAt=otherPes->startAt;
stats[found].count++;
//stats[found].startDts=ADM_NO_PTS;
// Get PTS, DTS
#define LEFT (int)(end-start)
int c,available;
if(LEFT<(4+2+1+2)) return false;
while(*start==0xff && start<end) start++; // Padding
if(LEFT<5) fail("Not enough data in OES");
c=*start++;
if((c&0xc0)!=0x80)
{
printf("[TS Demuxer] stream=0x%x pid=%d PES header :0x%x no mpeg2 PES marker\n",stream,id,c);
return false;
}
uint32_t ptsdts,len;
c=*start++; // PTS/DTS
//printf("%x ptsdts\n",c
ptsdts=c>>6;
// header len
len=*start++;
available=LEFT;
if(len>available) fail("Not enough data for PES header");
switch(ptsdts)
{
case 2: // PTS=1 DTS=0
{
if(available<5) return false;
uint64_t pts1,pts2,pts0;
// printf("\n PTS10\n");
pts0=start[0];
pts1=(start[1]<<8)+start[2];
pts2=(start[3]<<8)+start[4];
start+=5;
stats[found].startDts=(pts1>>1)<<15;
stats[found].startDts+=pts2>>1;
stats[found].startDts+=(((pts0&6)>>1)<<30);
}
break;
case 3: // PTS=1 DTS=1
if(available<10) return false;
#define PTS11_ADV 10 // nut monkey
if(len>=PTS11_ADV)
{
uint32_t skip=PTS11_ADV;
uint64_t pts1,pts2,dts,pts0;
// printf("\n PTS10\n");
pts0=start[0];
pts1=(start[1]<<8)+start[2];
pts2=(start[3]<<8)+start[4];
start+=5;
// Assume PTS=DTS
pts0=start[0];
pts1=(start[1]<<8)+start[2];
pts2=(start[3]<<8)+start[4];
start+=5;
stats[found].startDts=(pts1>>1)<<15;
stats[found].startDts+=pts2>>1;
stats[found].startDts+=(((pts0&6)>>1)<<30);
}
break;
case 1:
fail("unvalid pts/dts");
break;
case 0:
// printf("\n PTS00\n");
break; // no pts nor dts
}
return true;
}
// EOF