2001-03-31 08:42:27 +02:00
/*
2010-04-05 09:38:13 +02:00
* remux . c : Tools for detecting frames and handling PAT / PMT
2001-03-31 08:42:27 +02:00
*
* See the main source file ' vdr . c ' for copyright information and
* how to reach the author .
*
2012-09-14 09:11:18 +02:00
* $ Id : remux . c 2.65 2012 / 09 / 14 09 : 06 : 14 kls Exp $
2001-03-31 08:42:27 +02:00
*/
# include "remux.h"
2008-08-15 14:49:34 +02:00
# include "device.h"
# include "libsi/si.h"
# include "libsi/section.h"
# include "libsi/descriptor.h"
2011-06-12 14:24:09 +02:00
# include "recording.h"
2007-02-25 10:56:29 +01:00
# include "shutdown.h"
2001-03-31 08:42:27 +02:00
# include "tools.h"
2009-01-06 14:41:11 +01:00
// Set these to 'true' for debug output:
2008-08-15 14:49:34 +02:00
static bool DebugPatPmt = false ;
2009-01-06 14:41:11 +01:00
static bool DebugFrames = false ;
2008-08-15 14:49:34 +02:00
# define dbgpatpmt(a...) if (DebugPatPmt) fprintf(stderr, a)
2009-01-06 14:41:11 +01:00
# define dbgframes(a...) if (DebugFrames) fprintf(stderr, a)
2008-08-15 14:49:34 +02:00
2005-08-26 13:34:07 +02:00
ePesHeader AnalyzePesHeader ( const uchar * Data , int Count , int & PesPayloadOffset , bool * ContinuationHeader )
2005-07-30 10:25:03 +02:00
{
if ( Count < 7 )
2005-08-26 13:34:07 +02:00
return phNeedMoreData ; // too short
2005-07-30 10:25:03 +02:00
if ( ( Data [ 6 ] & 0xC0 ) = = 0x80 ) { // MPEG 2
if ( Count < 9 )
2005-08-26 13:34:07 +02:00
return phNeedMoreData ; // too short
2005-07-30 10:25:03 +02:00
PesPayloadOffset = 6 + 3 + Data [ 8 ] ;
if ( Count < PesPayloadOffset )
2005-08-26 13:34:07 +02:00
return phNeedMoreData ; // too short
2005-07-30 10:25:03 +02:00
if ( ContinuationHeader )
* ContinuationHeader = ( ( Data [ 6 ] = = 0x80 ) & & ! Data [ 7 ] & & ! Data [ 8 ] ) ;
2005-08-26 13:34:07 +02:00
return phMPEG2 ; // MPEG 2
2005-07-30 10:25:03 +02:00
}
// check for MPEG 1 ...
PesPayloadOffset = 6 ;
// skip up to 16 stuffing bytes
for ( int i = 0 ; i < 16 ; i + + ) {
if ( Data [ PesPayloadOffset ] ! = 0xFF )
break ;
if ( Count < = + + PesPayloadOffset )
2005-08-26 13:34:07 +02:00
return phNeedMoreData ; // too short
2005-07-30 10:25:03 +02:00
}
// skip STD_buffer_scale/size
if ( ( Data [ PesPayloadOffset ] & 0xC0 ) = = 0x40 ) {
PesPayloadOffset + = 2 ;
if ( Count < = PesPayloadOffset )
2005-08-26 13:34:07 +02:00
return phNeedMoreData ; // too short
2005-07-30 10:25:03 +02:00
}
if ( ContinuationHeader )
* ContinuationHeader = false ;
if ( ( Data [ PesPayloadOffset ] & 0xF0 ) = = 0x20 ) {
// skip PTS only
PesPayloadOffset + = 5 ;
}
else if ( ( Data [ PesPayloadOffset ] & 0xF0 ) = = 0x30 ) {
// skip PTS and DTS
PesPayloadOffset + = 10 ;
}
else if ( Data [ PesPayloadOffset ] = = 0x0F ) {
// continuation header
PesPayloadOffset + + ;
if ( ContinuationHeader )
* ContinuationHeader = true ;
}
else
2005-08-26 13:34:07 +02:00
return phInvalid ; // unknown
2005-07-30 10:25:03 +02:00
if ( Count < PesPayloadOffset )
2005-08-26 13:34:07 +02:00
return phNeedMoreData ; // too short
2005-07-30 10:25:03 +02:00
2005-08-26 13:34:07 +02:00
return phMPEG1 ; // MPEG 1
2005-07-30 10:25:03 +02:00
}
2001-06-02 10:47:40 +02:00
# define VIDEO_STREAM_S 0xE0
2007-11-17 13:59:08 +01:00
2001-06-02 10:47:40 +02:00
// --- cRemux ----------------------------------------------------------------
2003-04-26 15:11:17 +02:00
void cRemux : : SetBrokenLink ( uchar * Data , int Length )
{
2005-08-26 13:34:07 +02:00
int PesPayloadOffset = 0 ;
if ( AnalyzePesHeader ( Data , Length , PesPayloadOffset ) > = phMPEG1 & & ( Data [ 3 ] & 0xF0 ) = = VIDEO_STREAM_S ) {
for ( int i = PesPayloadOffset ; i < Length - 7 ; i + + ) {
2003-04-26 15:11:17 +02:00
if ( Data [ i ] = = 0 & & Data [ i + 1 ] = = 0 & & Data [ i + 2 ] = = 1 & & Data [ i + 3 ] = = 0xB8 ) {
if ( ! ( Data [ i + 7 ] & 0x40 ) ) // set flag only if GOP is not closed
Data [ i + 7 ] | = 0x20 ;
return ;
}
}
dsyslog ( " SetBrokenLink: no GOP header found in video packet " ) ;
}
else
dsyslog ( " SetBrokenLink: no video packet in frame " ) ;
}
2008-08-15 14:49:34 +02:00
2009-03-13 14:45:12 +01:00
// --- Some TS handling tools ------------------------------------------------
int64_t TsGetPts ( const uchar * p , int l )
{
// Find the first packet with a PTS and use it:
while ( l > 0 ) {
const uchar * d = p ;
if ( TsPayloadStart ( d ) & & TsGetPayload ( & d ) & & PesHasPts ( d ) )
return PesGetPts ( d ) ;
p + = TS_SIZE ;
l - = TS_SIZE ;
}
return - 1 ;
}
2009-04-19 11:07:07 +02:00
void TsSetTeiOnBrokenPackets ( uchar * p , int l )
{
bool Processed [ MAXPID ] = { false } ;
while ( l > = TS_SIZE ) {
if ( * p ! = TS_SYNC_BYTE )
break ;
int Pid = TsPid ( p ) ;
if ( ! Processed [ Pid ] ) {
if ( ! TsPayloadStart ( p ) )
p [ 1 ] | = TS_ERROR ;
2011-08-15 09:52:43 +02:00
else {
2009-04-19 11:07:07 +02:00
Processed [ Pid ] = true ;
2011-08-15 09:52:43 +02:00
int offs = TsPayloadOffset ( p ) ;
cRemux : : SetBrokenLink ( p + offs , TS_SIZE - offs ) ;
}
2009-04-19 11:07:07 +02:00
}
l - = TS_SIZE ;
p + = TS_SIZE ;
}
}
2008-08-15 14:49:34 +02:00
// --- cPatPmtGenerator ------------------------------------------------------
2010-01-30 11:10:25 +01:00
cPatPmtGenerator : : cPatPmtGenerator ( const cChannel * Channel )
2008-08-15 14:49:34 +02:00
{
numPmtPackets = 0 ;
patCounter = pmtCounter = 0 ;
patVersion = pmtVersion = 0 ;
2009-01-23 16:44:46 +01:00
pmtPid = 0 ;
2008-08-15 14:49:34 +02:00
esInfoLength = NULL ;
2009-01-23 16:44:46 +01:00
SetChannel ( Channel ) ;
2008-08-15 14:49:34 +02:00
}
void cPatPmtGenerator : : IncCounter ( int & Counter , uchar * TsPacket )
{
TsPacket [ 3 ] = ( TsPacket [ 3 ] & 0xF0 ) | Counter ;
if ( + + Counter > 0x0F )
Counter = 0x00 ;
}
void cPatPmtGenerator : : IncVersion ( int & Version )
{
if ( + + Version > 0x1F )
Version = 0x00 ;
}
void cPatPmtGenerator : : IncEsInfoLength ( int Length )
{
if ( esInfoLength ) {
Length + = ( ( * esInfoLength & 0x0F ) < < 8 ) | * ( esInfoLength + 1 ) ;
* esInfoLength = 0xF0 | ( Length > > 8 ) ;
* ( esInfoLength + 1 ) = Length ;
}
}
int cPatPmtGenerator : : MakeStream ( uchar * Target , uchar Type , int Pid )
{
int i = 0 ;
Target [ i + + ] = Type ; // stream type
Target [ i + + ] = 0xE0 | ( Pid > > 8 ) ; // dummy (3), pid hi (5)
Target [ i + + ] = Pid ; // pid lo
esInfoLength = & Target [ i ] ;
Target [ i + + ] = 0xF0 ; // dummy (4), ES info length hi
Target [ i + + ] = 0x00 ; // ES info length lo
return i ;
}
2010-06-05 13:50:56 +02:00
int cPatPmtGenerator : : MakeAC3Descriptor ( uchar * Target , uchar Type )
2008-08-15 14:49:34 +02:00
{
int i = 0 ;
2010-06-05 13:50:56 +02:00
Target [ i + + ] = Type ;
2008-08-15 14:49:34 +02:00
Target [ i + + ] = 0x01 ; // length
Target [ i + + ] = 0x00 ;
IncEsInfoLength ( i ) ;
return i ;
}
2009-08-16 15:32:39 +02:00
int cPatPmtGenerator : : MakeSubtitlingDescriptor ( uchar * Target , const char * Language , uchar SubtitlingType , uint16_t CompositionPageId , uint16_t AncillaryPageId )
2008-08-15 14:49:34 +02:00
{
int i = 0 ;
Target [ i + + ] = SI : : SubtitlingDescriptorTag ;
Target [ i + + ] = 0x08 ; // length
Target [ i + + ] = * Language + + ;
Target [ i + + ] = * Language + + ;
Target [ i + + ] = * Language + + ;
2009-08-16 15:32:39 +02:00
Target [ i + + ] = SubtitlingType ;
Target [ i + + ] = CompositionPageId > > 8 ;
Target [ i + + ] = CompositionPageId & 0xFF ;
Target [ i + + ] = AncillaryPageId > > 8 ;
Target [ i + + ] = AncillaryPageId & 0xFF ;
2008-08-15 14:49:34 +02:00
IncEsInfoLength ( i ) ;
return i ;
}
int cPatPmtGenerator : : MakeLanguageDescriptor ( uchar * Target , const char * Language )
{
int i = 0 ;
Target [ i + + ] = SI : : ISO639LanguageDescriptorTag ;
2010-04-18 13:44:56 +02:00
int Length = i + + ;
Target [ Length ] = 0x00 ; // length
for ( const char * End = Language + strlen ( Language ) ; Language < End ; ) {
Target [ i + + ] = * Language + + ;
Target [ i + + ] = * Language + + ;
Target [ i + + ] = * Language + + ;
2011-02-26 15:53:12 +01:00
Target [ i + + ] = 0x00 ; // audio type
2010-04-18 13:44:56 +02:00
Target [ Length ] + = 0x04 ; // length
if ( * Language = = ' + ' )
Language + + ;
}
2008-08-15 14:49:34 +02:00
IncEsInfoLength ( i ) ;
return i ;
}
int cPatPmtGenerator : : MakeCRC ( uchar * Target , const uchar * Data , int Length )
{
int crc = SI : : CRC32 : : crc32 ( ( const char * ) Data , Length , 0xFFFFFFFF ) ;
int i = 0 ;
Target [ i + + ] = crc > > 24 ;
Target [ i + + ] = crc > > 16 ;
Target [ i + + ] = crc > > 8 ;
Target [ i + + ] = crc ;
return i ;
}
# define P_TSID 0x8008 // pseudo TS ID
# define P_PMT_PID 0x0084 // pseudo PMT pid
2009-01-23 16:44:46 +01:00
# define MAXPID 0x2000 // the maximum possible number of pids
2010-01-30 11:10:25 +01:00
void cPatPmtGenerator : : GeneratePmtPid ( const cChannel * Channel )
2009-01-23 16:44:46 +01:00
{
bool Used [ MAXPID ] = { false } ;
# define SETPID(p) { if ((p) >= 0 && (p) < MAXPID) Used[p] = true; }
# define SETPIDS(l) { const int *p = l; while (*p) { SETPID(*p); p++; } }
SETPID ( Channel - > Vpid ( ) ) ;
SETPID ( Channel - > Ppid ( ) ) ;
SETPID ( Channel - > Tpid ( ) ) ;
SETPIDS ( Channel - > Apids ( ) ) ;
SETPIDS ( Channel - > Dpids ( ) ) ;
SETPIDS ( Channel - > Spids ( ) ) ;
for ( pmtPid = P_PMT_PID ; Used [ pmtPid ] ; pmtPid + + )
;
}
2008-08-15 14:49:34 +02:00
void cPatPmtGenerator : : GeneratePat ( void )
{
memset ( pat , 0xFF , sizeof ( pat ) ) ;
uchar * p = pat ;
int i = 0 ;
2009-01-23 16:43:23 +01:00
p [ i + + ] = TS_SYNC_BYTE ; // TS indicator
2009-11-22 11:30:27 +01:00
p [ i + + ] = TS_PAYLOAD_START | ( PATPID > > 8 ) ; // flags (3), pid hi (5)
p [ i + + ] = PATPID & 0xFF ; // pid lo
2008-08-15 14:49:34 +02:00
p [ i + + ] = 0x10 ; // flags (4), continuity counter (4)
2008-12-20 10:46:53 +01:00
p [ i + + ] = 0x00 ; // pointer field (payload unit start indicator is set)
2008-08-15 14:49:34 +02:00
int PayloadStart = i ;
p [ i + + ] = 0x00 ; // table id
p [ i + + ] = 0xB0 ; // section syntax indicator (1), dummy (3), section length hi (4)
int SectionLength = i ;
p [ i + + ] = 0x00 ; // section length lo (filled in later)
p [ i + + ] = P_TSID > > 8 ; // TS id hi
p [ i + + ] = P_TSID & 0xFF ; // TS id lo
p [ i + + ] = 0xC1 | ( patVersion < < 1 ) ; // dummy (2), version number (5), current/next indicator (1)
p [ i + + ] = 0x00 ; // section number
p [ i + + ] = 0x00 ; // last section number
2009-01-23 16:44:46 +01:00
p [ i + + ] = pmtPid > > 8 ; // program number hi
p [ i + + ] = pmtPid & 0xFF ; // program number lo
p [ i + + ] = 0xE0 | ( pmtPid > > 8 ) ; // dummy (3), PMT pid hi (5)
p [ i + + ] = pmtPid & 0xFF ; // PMT pid lo
2008-08-15 14:49:34 +02:00
pat [ SectionLength ] = i - SectionLength - 1 + 4 ; // -2 = SectionLength storage, +4 = length of CRC
MakeCRC ( pat + i , pat + PayloadStart , i - PayloadStart ) ;
IncVersion ( patVersion ) ;
}
2010-01-30 11:10:25 +01:00
void cPatPmtGenerator : : GeneratePmt ( const cChannel * Channel )
2008-08-15 14:49:34 +02:00
{
// generate the complete PMT section:
uchar buf [ MAX_SECTION_SIZE ] ;
memset ( buf , 0xFF , sizeof ( buf ) ) ;
numPmtPackets = 0 ;
if ( Channel ) {
int Vpid = Channel - > Vpid ( ) ;
2010-01-24 15:20:49 +01:00
int Ppid = Channel - > Ppid ( ) ;
2008-08-15 14:49:34 +02:00
uchar * p = buf ;
int i = 0 ;
p [ i + + ] = 0x02 ; // table id
int SectionLength = i ;
p [ i + + ] = 0xB0 ; // section syntax indicator (1), dummy (3), section length hi (4)
p [ i + + ] = 0x00 ; // section length lo (filled in later)
2009-01-23 16:44:46 +01:00
p [ i + + ] = pmtPid > > 8 ; // program number hi
p [ i + + ] = pmtPid & 0xFF ; // program number lo
2008-08-15 14:49:34 +02:00
p [ i + + ] = 0xC1 | ( pmtVersion < < 1 ) ; // dummy (2), version number (5), current/next indicator (1)
p [ i + + ] = 0x00 ; // section number
p [ i + + ] = 0x00 ; // last section number
2009-11-01 15:35:34 +01:00
p [ i + + ] = 0xE0 | ( Ppid > > 8 ) ; // dummy (3), PCR pid hi (5)
p [ i + + ] = Ppid ; // PCR pid lo
2008-08-15 14:49:34 +02:00
p [ i + + ] = 0xF0 ; // dummy (4), program info length hi (4)
p [ i + + ] = 0x00 ; // program info length lo
if ( Vpid )
i + = MakeStream ( buf + i , Channel - > Vtype ( ) , Vpid ) ;
for ( int n = 0 ; Channel - > Apid ( n ) ; n + + ) {
2010-05-16 13:36:55 +02:00
i + = MakeStream ( buf + i , Channel - > Atype ( n ) , Channel - > Apid ( n ) ) ;
2008-08-15 14:49:34 +02:00
const char * Alang = Channel - > Alang ( n ) ;
i + = MakeLanguageDescriptor ( buf + i , Alang ) ;
}
for ( int n = 0 ; Channel - > Dpid ( n ) ; n + + ) {
i + = MakeStream ( buf + i , 0x06 , Channel - > Dpid ( n ) ) ;
2010-06-05 13:50:56 +02:00
i + = MakeAC3Descriptor ( buf + i , Channel - > Dtype ( n ) ) ;
2008-08-15 14:49:34 +02:00
i + = MakeLanguageDescriptor ( buf + i , Channel - > Dlang ( n ) ) ;
}
for ( int n = 0 ; Channel - > Spid ( n ) ; n + + ) {
i + = MakeStream ( buf + i , 0x06 , Channel - > Spid ( n ) ) ;
2009-08-16 15:32:39 +02:00
i + = MakeSubtitlingDescriptor ( buf + i , Channel - > Slang ( n ) , Channel - > SubtitlingType ( n ) , Channel - > CompositionPageId ( n ) , Channel - > AncillaryPageId ( n ) ) ;
2008-08-15 14:49:34 +02:00
}
int sl = i - SectionLength - 2 + 4 ; // -2 = SectionLength storage, +4 = length of CRC
buf [ SectionLength ] | = ( sl > > 8 ) & 0x0F ;
buf [ SectionLength + 1 ] = sl ;
MakeCRC ( buf + i , buf , i ) ;
// split the PMT section into several TS packets:
uchar * q = buf ;
2008-12-20 10:46:53 +01:00
bool pusi = true ;
2008-08-15 14:49:34 +02:00
while ( i > 0 ) {
uchar * p = pmt [ numPmtPackets + + ] ;
int j = 0 ;
2009-01-23 16:43:23 +01:00
p [ j + + ] = TS_SYNC_BYTE ; // TS indicator
2009-01-23 16:44:46 +01:00
p [ j + + ] = ( pusi ? TS_PAYLOAD_START : 0x00 ) | ( pmtPid > > 8 ) ; // flags (3), pid hi (5)
p [ j + + ] = pmtPid & 0xFF ; // pid lo
2008-08-15 14:49:34 +02:00
p [ j + + ] = 0x10 ; // flags (4), continuity counter (4)
2008-12-20 10:46:53 +01:00
if ( pusi ) {
p [ j + + ] = 0x00 ; // pointer field (payload unit start indicator is set)
pusi = false ;
}
2008-08-15 14:49:34 +02:00
int l = TS_SIZE - j ;
memcpy ( p + j , q , l ) ;
q + = l ;
i - = l ;
}
IncVersion ( pmtVersion ) ;
}
2009-01-23 16:44:46 +01:00
}
2009-05-24 15:11:28 +02:00
void cPatPmtGenerator : : SetVersions ( int PatVersion , int PmtVersion )
{
patVersion = PatVersion & 0x1F ;
pmtVersion = PmtVersion & 0x1F ;
}
2010-01-30 11:10:25 +01:00
void cPatPmtGenerator : : SetChannel ( const cChannel * Channel )
2009-01-23 16:44:46 +01:00
{
if ( Channel ) {
GeneratePmtPid ( Channel ) ;
GeneratePat ( ) ;
GeneratePmt ( Channel ) ;
}
2008-08-15 14:49:34 +02:00
}
uchar * cPatPmtGenerator : : GetPat ( void )
{
IncCounter ( patCounter , pat ) ;
return pat ;
}
uchar * cPatPmtGenerator : : GetPmt ( int & Index )
{
if ( Index < numPmtPackets ) {
2009-01-16 15:28:18 +01:00
IncCounter ( pmtCounter , pmt [ Index ] ) ;
2008-08-15 14:49:34 +02:00
return pmt [ Index + + ] ;
}
return NULL ;
}
// --- cPatPmtParser ---------------------------------------------------------
2009-06-06 13:26:23 +02:00
cPatPmtParser : : cPatPmtParser ( bool UpdatePrimaryDevice )
2009-01-24 13:47:46 +01:00
{
2009-06-06 13:26:23 +02:00
updatePrimaryDevice = UpdatePrimaryDevice ;
2009-01-24 13:47:46 +01:00
Reset ( ) ;
}
void cPatPmtParser : : Reset ( void )
2008-08-15 14:49:34 +02:00
{
pmtSize = 0 ;
2009-01-24 13:47:46 +01:00
patVersion = pmtVersion = - 1 ;
2008-08-15 14:49:34 +02:00
pmtPid = - 1 ;
vpid = vtype = 0 ;
2010-01-30 11:10:25 +01:00
ppid = 0 ;
2008-08-15 14:49:34 +02:00
}
void cPatPmtParser : : ParsePat ( const uchar * Data , int Length )
{
2009-01-23 16:43:23 +01:00
// Unpack the TS packet:
int PayloadOffset = TsPayloadOffset ( Data ) ;
Data + = PayloadOffset ;
Length - = PayloadOffset ;
2008-08-15 14:49:34 +02:00
// The PAT is always assumed to fit into a single TS packet
2009-01-23 14:34:05 +01:00
if ( ( Length - = Data [ 0 ] + 1 ) < = 0 )
return ;
2008-12-20 10:46:53 +01:00
Data + = Data [ 0 ] + 1 ; // process pointer_field
2008-08-15 14:49:34 +02:00
SI : : PAT Pat ( Data , false ) ;
if ( Pat . CheckCRCAndParse ( ) ) {
dbgpatpmt ( " PAT: TSid = %d, c/n = %d, v = %d, s = %d, ls = %d \n " , Pat . getTransportStreamId ( ) , Pat . getCurrentNextIndicator ( ) , Pat . getVersionNumber ( ) , Pat . getSectionNumber ( ) , Pat . getLastSectionNumber ( ) ) ;
2009-01-24 13:47:46 +01:00
if ( patVersion = = Pat . getVersionNumber ( ) )
return ;
2008-08-15 14:49:34 +02:00
SI : : PAT : : Association assoc ;
for ( SI : : Loop : : Iterator it ; Pat . associationLoop . getNext ( assoc , it ) ; ) {
dbgpatpmt ( " isNITPid = %d \n " , assoc . isNITPid ( ) ) ;
if ( ! assoc . isNITPid ( ) ) {
pmtPid = assoc . getPid ( ) ;
dbgpatpmt ( " service id = %d, pid = %d \n " , assoc . getServiceId ( ) , assoc . getPid ( ) ) ;
}
}
2009-01-24 13:47:46 +01:00
patVersion = Pat . getVersionNumber ( ) ;
2008-08-15 14:49:34 +02:00
}
else
esyslog ( " ERROR: can't parse PAT " ) ;
}
void cPatPmtParser : : ParsePmt ( const uchar * Data , int Length )
{
2009-01-23 16:43:23 +01:00
// Unpack the TS packet:
bool PayloadStart = TsPayloadStart ( Data ) ;
int PayloadOffset = TsPayloadOffset ( Data ) ;
Data + = PayloadOffset ;
Length - = PayloadOffset ;
2008-08-15 14:49:34 +02:00
// The PMT may extend over several TS packets, so we need to assemble them
2009-01-23 16:43:23 +01:00
if ( PayloadStart ) {
pmtSize = 0 ;
2009-01-23 14:34:05 +01:00
if ( ( Length - = Data [ 0 ] + 1 ) < = 0 )
return ;
2009-01-23 14:19:58 +01:00
Data + = Data [ 0 ] + 1 ; // this is the first packet
2008-08-15 14:49:34 +02:00
if ( SectionLength ( Data , Length ) > Length ) {
if ( Length < = int ( sizeof ( pmt ) ) ) {
memcpy ( pmt , Data , Length ) ;
pmtSize = Length ;
}
else
esyslog ( " ERROR: PMT packet length too big (%d byte)! " , Length ) ;
return ;
}
// the packet contains the entire PMT section, so we run into the actual parsing
}
2009-01-23 16:43:23 +01:00
else if ( pmtSize > 0 ) {
2008-08-15 14:49:34 +02:00
// this is a following packet, so we add it to the pmt storage
if ( Length < = int ( sizeof ( pmt ) ) - pmtSize ) {
memcpy ( pmt + pmtSize , Data , Length ) ;
pmtSize + = Length ;
}
else {
esyslog ( " ERROR: PMT section length too big (%d byte)! " , pmtSize + Length ) ;
pmtSize = 0 ;
}
if ( SectionLength ( pmt , pmtSize ) > pmtSize )
return ; // more packets to come
// the PMT section is now complete, so we run into the actual parsing
Data = pmt ;
}
2009-01-23 16:43:23 +01:00
else
return ; // fragment of broken packet - ignore
2008-08-15 14:49:34 +02:00
SI : : PMT Pmt ( Data , false ) ;
if ( Pmt . CheckCRCAndParse ( ) ) {
dbgpatpmt ( " PMT: sid = %d, c/n = %d, v = %d, s = %d, ls = %d \n " , Pmt . getServiceId ( ) , Pmt . getCurrentNextIndicator ( ) , Pmt . getVersionNumber ( ) , Pmt . getSectionNumber ( ) , Pmt . getLastSectionNumber ( ) ) ;
dbgpatpmt ( " pcr = %d \n " , Pmt . getPCRPid ( ) ) ;
2009-01-24 13:47:46 +01:00
if ( pmtVersion = = Pmt . getVersionNumber ( ) )
return ;
2009-06-06 13:26:23 +02:00
if ( updatePrimaryDevice )
cDevice : : PrimaryDevice ( ) - > ClrAvailableTracks ( false , true ) ;
2008-08-15 14:49:34 +02:00
int NumApids = 0 ;
int NumDpids = 0 ;
int NumSpids = 0 ;
2009-01-06 14:47:53 +01:00
vpid = vtype = 0 ;
2010-01-30 11:10:25 +01:00
ppid = 0 ;
2009-12-29 15:46:12 +01:00
apids [ 0 ] = 0 ;
dpids [ 0 ] = 0 ;
spids [ 0 ] = 0 ;
2009-12-31 15:35:37 +01:00
atypes [ 0 ] = 0 ;
dtypes [ 0 ] = 0 ;
2008-08-15 14:49:34 +02:00
SI : : PMT : : Stream stream ;
for ( SI : : Loop : : Iterator it ; Pmt . streamLoop . getNext ( stream , it ) ; ) {
dbgpatpmt ( " stream type = %02X, pid = %d " , stream . getStreamType ( ) , stream . getPid ( ) ) ;
switch ( stream . getStreamType ( ) ) {
2009-05-10 14:32:03 +02:00
case 0x01 : // STREAMTYPE_11172_VIDEO
2008-08-15 14:49:34 +02:00
case 0x02 : // STREAMTYPE_13818_VIDEO
case 0x1B : // MPEG4
vpid = stream . getPid ( ) ;
vtype = stream . getStreamType ( ) ;
2010-01-30 11:10:25 +01:00
ppid = Pmt . getPCRPid ( ) ;
2008-08-15 14:49:34 +02:00
break ;
2009-12-24 11:56:50 +01:00
case 0x03 : // STREAMTYPE_11172_AUDIO
2008-08-15 14:49:34 +02:00
case 0x04 : // STREAMTYPE_13818_AUDIO
2012-03-02 10:56:49 +01:00
case 0x0F : // ISO/IEC 13818-7 Audio with ADTS transport syntax
2010-05-16 13:36:55 +02:00
case 0x11 : // ISO/IEC 14496-3 Audio with LATM transport syntax
2008-08-15 14:49:34 +02:00
{
if ( NumApids < MAXAPIDS ) {
2009-12-24 12:28:01 +01:00
apids [ NumApids ] = stream . getPid ( ) ;
2009-12-31 15:35:37 +01:00
atypes [ NumApids ] = stream . getStreamType ( ) ;
2009-12-24 12:28:01 +01:00
* alangs [ NumApids ] = 0 ;
2008-08-15 14:49:34 +02:00
SI : : Descriptor * d ;
for ( SI : : Loop : : Iterator it ; ( d = stream . streamDescriptors . getNext ( it ) ) ; ) {
switch ( d - > getDescriptorTag ( ) ) {
case SI : : ISO639LanguageDescriptorTag : {
SI : : ISO639LanguageDescriptor * ld = ( SI : : ISO639LanguageDescriptor * ) d ;
SI : : ISO639LanguageDescriptor : : Language l ;
2009-12-24 12:28:01 +01:00
char * s = alangs [ NumApids ] ;
2008-08-15 14:49:34 +02:00
int n = 0 ;
for ( SI : : Loop : : Iterator it ; ld - > languageLoop . getNext ( l , it ) ; ) {
if ( * ld - > languageCode ! = ' - ' ) { // some use "---" to indicate "none"
dbgpatpmt ( " '%s' " , l . languageCode ) ;
if ( n > 0 )
* s + + = ' + ' ;
strn0cpy ( s , I18nNormalizeLanguageCode ( l . languageCode ) , MAXLANGCODE1 ) ;
s + = strlen ( s ) ;
if ( n + + > 1 )
break ;
}
}
}
break ;
default : ;
}
delete d ;
}
2009-06-06 13:26:23 +02:00
if ( updatePrimaryDevice )
2009-12-24 12:28:01 +01:00
cDevice : : PrimaryDevice ( ) - > SetAvailableTrack ( ttAudio , NumApids , apids [ NumApids ] , alangs [ NumApids ] ) ;
2008-08-15 14:49:34 +02:00
NumApids + + ;
2009-12-29 15:46:12 +01:00
apids [ NumApids ] = 0 ;
2008-08-15 14:49:34 +02:00
}
}
break ;
case 0x06 : // STREAMTYPE_13818_PES_PRIVATE
{
int dpid = 0 ;
2010-06-05 13:50:56 +02:00
int dtype = 0 ;
2008-08-15 14:49:34 +02:00
char lang [ MAXLANGCODE1 ] = " " ;
SI : : Descriptor * d ;
for ( SI : : Loop : : Iterator it ; ( d = stream . streamDescriptors . getNext ( it ) ) ; ) {
switch ( d - > getDescriptorTag ( ) ) {
case SI : : AC3DescriptorTag :
2010-06-05 13:50:56 +02:00
case SI : : EnhancedAC3DescriptorTag :
2008-08-15 14:49:34 +02:00
dbgpatpmt ( " AC3 " ) ;
dpid = stream . getPid ( ) ;
2010-06-05 13:50:56 +02:00
dtype = d - > getDescriptorTag ( ) ;
2008-08-15 14:49:34 +02:00
break ;
case SI : : SubtitlingDescriptorTag :
dbgpatpmt ( " subtitling " ) ;
if ( NumSpids < MAXSPIDS ) {
2009-12-24 12:28:01 +01:00
spids [ NumSpids ] = stream . getPid ( ) ;
* slangs [ NumSpids ] = 0 ;
subtitlingTypes [ NumSpids ] = 0 ;
compositionPageIds [ NumSpids ] = 0 ;
ancillaryPageIds [ NumSpids ] = 0 ;
2008-08-15 14:49:34 +02:00
SI : : SubtitlingDescriptor * sd = ( SI : : SubtitlingDescriptor * ) d ;
SI : : SubtitlingDescriptor : : Subtitling sub ;
2009-12-24 12:28:01 +01:00
char * s = slangs [ NumSpids ] ;
2008-08-15 14:49:34 +02:00
int n = 0 ;
for ( SI : : Loop : : Iterator it ; sd - > subtitlingLoop . getNext ( sub , it ) ; ) {
if ( sub . languageCode [ 0 ] ) {
dbgpatpmt ( " '%s' " , sub . languageCode ) ;
2009-12-24 12:28:01 +01:00
subtitlingTypes [ NumSpids ] = sub . getSubtitlingType ( ) ;
compositionPageIds [ NumSpids ] = sub . getCompositionPageId ( ) ;
ancillaryPageIds [ NumSpids ] = sub . getAncillaryPageId ( ) ;
2008-08-15 14:49:34 +02:00
if ( n > 0 )
* s + + = ' + ' ;
strn0cpy ( s , I18nNormalizeLanguageCode ( sub . languageCode ) , MAXLANGCODE1 ) ;
s + = strlen ( s ) ;
if ( n + + > 1 )
break ;
}
}
2009-06-06 13:26:23 +02:00
if ( updatePrimaryDevice )
2009-12-24 12:28:01 +01:00
cDevice : : PrimaryDevice ( ) - > SetAvailableTrack ( ttSubtitle , NumSpids , spids [ NumSpids ] , slangs [ NumSpids ] ) ;
2008-08-15 14:49:34 +02:00
NumSpids + + ;
2009-12-29 15:46:12 +01:00
spids [ NumSpids ] = 0 ;
2008-08-15 14:49:34 +02:00
}
break ;
case SI : : ISO639LanguageDescriptorTag : {
SI : : ISO639LanguageDescriptor * ld = ( SI : : ISO639LanguageDescriptor * ) d ;
dbgpatpmt ( " '%s' " , ld - > languageCode ) ;
strn0cpy ( lang , I18nNormalizeLanguageCode ( ld - > languageCode ) , MAXLANGCODE1 ) ;
}
break ;
default : ;
}
delete d ;
}
if ( dpid ) {
if ( NumDpids < MAXDPIDS ) {
2009-12-24 12:28:01 +01:00
dpids [ NumDpids ] = dpid ;
2010-06-05 13:50:56 +02:00
dtypes [ NumDpids ] = dtype ;
2009-12-24 12:28:01 +01:00
strn0cpy ( dlangs [ NumDpids ] , lang , sizeof ( dlangs [ NumDpids ] ) ) ;
2010-02-28 14:42:07 +01:00
if ( updatePrimaryDevice & & Setup . UseDolbyDigital )
2009-06-06 13:26:23 +02:00
cDevice : : PrimaryDevice ( ) - > SetAvailableTrack ( ttDolby , NumDpids , dpid , lang ) ;
2008-08-15 14:49:34 +02:00
NumDpids + + ;
2009-12-29 15:46:12 +01:00
dpids [ NumDpids ] = 0 ;
2008-08-15 14:49:34 +02:00
}
}
}
break ;
2009-12-06 12:57:45 +01:00
default : ;
2008-08-15 14:49:34 +02:00
}
dbgpatpmt ( " \n " ) ;
2009-06-06 13:26:23 +02:00
if ( updatePrimaryDevice ) {
cDevice : : PrimaryDevice ( ) - > EnsureAudioTrack ( true ) ;
cDevice : : PrimaryDevice ( ) - > EnsureSubtitleTrack ( ) ;
}
2008-08-15 14:49:34 +02:00
}
2009-01-24 13:47:46 +01:00
pmtVersion = Pmt . getVersionNumber ( ) ;
2008-08-15 14:49:34 +02:00
}
else
esyslog ( " ERROR: can't parse PMT " ) ;
pmtSize = 0 ;
}
2009-12-05 11:32:31 +01:00
bool cPatPmtParser : : GetVersions ( int & PatVersion , int & PmtVersion ) const
2009-05-24 15:11:28 +02:00
{
PatVersion = patVersion ;
PmtVersion = pmtVersion ;
return patVersion > = 0 & & pmtVersion > = 0 ;
}
2008-08-15 14:49:34 +02:00
// --- cTsToPes --------------------------------------------------------------
cTsToPes : : cTsToPes ( void )
{
data = NULL ;
2009-06-21 13:34:40 +02:00
size = 0 ;
Reset ( ) ;
2008-08-15 14:49:34 +02:00
}
cTsToPes : : ~ cTsToPes ( )
{
free ( data ) ;
}
void cTsToPes : : PutTs ( const uchar * Data , int Length )
{
2009-04-19 11:07:07 +02:00
if ( TsError ( Data ) ) {
Reset ( ) ;
return ; // ignore packets with TEI set, and drop any PES data collected so far
}
2008-08-15 14:49:34 +02:00
if ( TsPayloadStart ( Data ) )
Reset ( ) ;
else if ( ! size )
return ; // skip everything before the first payload start
Length = TsGetPayload ( & Data ) ;
if ( length + Length > size ) {
2011-02-20 17:37:24 +01:00
int NewSize = max ( KILOBYTE ( 2 ) , length + Length ) ;
if ( uchar * NewData = ( uchar * ) realloc ( data , NewSize ) ) {
data = NewData ;
size = NewSize ;
}
else {
2011-02-25 15:25:42 +01:00
esyslog ( " ERROR: out of memory " ) ;
2010-05-13 14:39:41 +02:00
Reset ( ) ;
return ;
}
2008-08-15 14:49:34 +02:00
}
memcpy ( data + length , Data , Length ) ;
length + = Length ;
}
2008-12-13 14:43:22 +01:00
# define MAXPESLENGTH 0xFFF0
2008-08-15 14:49:34 +02:00
const uchar * cTsToPes : : GetPes ( int & Length )
{
2009-06-21 13:34:40 +02:00
if ( repeatLast ) {
repeatLast = false ;
Length = lastLength ;
return lastData ;
}
2008-12-13 14:43:22 +01:00
if ( offset < length & & PesLongEnough ( length ) ) {
if ( ! PesHasLength ( data ) ) // this is a video PES packet with undefined length
offset = 6 ; // trigger setting PES length for initial slice
if ( offset ) {
uchar * p = data + offset - 6 ;
if ( p ! = data ) {
p - = 3 ;
2012-01-12 12:25:54 +01:00
if ( p < data ) {
Reset ( ) ;
return NULL ;
}
2008-12-13 14:43:22 +01:00
memmove ( p , data , 4 ) ;
}
int l = min ( length - offset , MAXPESLENGTH ) ;
offset + = l ;
if ( p ! = data ) {
l + = 3 ;
p [ 6 ] = 0x80 ;
p [ 7 ] = 0x00 ;
p [ 8 ] = 0x00 ;
}
p [ 4 ] = l / 256 ;
p [ 5 ] = l & 0xFF ;
Length = l + 6 ;
2009-06-21 13:34:40 +02:00
lastLength = Length ;
lastData = p ;
2008-12-13 14:43:22 +01:00
return p ;
}
else {
Length = PesLength ( data ) ;
2009-01-16 14:44:55 +01:00
if ( Length < = length ) {
offset = Length ; // to make sure we break out in case of garbage data
2009-06-21 13:34:40 +02:00
lastLength = Length ;
lastData = data ;
2009-01-16 14:44:55 +01:00
return data ;
}
2008-08-15 14:49:34 +02:00
}
}
return NULL ;
}
2009-06-21 13:34:40 +02:00
void cTsToPes : : SetRepeatLast ( void )
{
repeatLast = true ;
}
2008-08-15 14:49:34 +02:00
void cTsToPes : : Reset ( void )
{
2008-12-13 14:43:22 +01:00
length = offset = 0 ;
2009-06-21 13:34:40 +02:00
lastData = NULL ;
lastLength = 0 ;
repeatLast = false ;
2008-08-15 14:49:34 +02:00
}
// --- Some helper functions for debugging -----------------------------------
void BlockDump ( const char * Name , const u_char * Data , int Length )
{
printf ( " --- %s \n " , Name ) ;
for ( int i = 0 ; i < Length ; i + + ) {
if ( i & & ( i % 16 ) = = 0 )
printf ( " \n " ) ;
printf ( " %02X " , Data [ i ] ) ;
}
printf ( " \n " ) ;
}
void TsDump ( const char * Name , const u_char * Data , int Length )
{
printf ( " %s: %04X " , Name , Length ) ;
int n = min ( Length , 20 ) ;
for ( int i = 0 ; i < n ; i + + )
printf ( " %02X " , Data [ i ] ) ;
if ( n < Length ) {
printf ( " ... " ) ;
n = max ( n , Length - 10 ) ;
for ( n = max ( n , Length - 10 ) ; n < Length ; n + + )
printf ( " %02X " , Data [ n ] ) ;
}
printf ( " \n " ) ;
}
void PesDump ( const char * Name , const u_char * Data , int Length )
{
TsDump ( Name , Data , Length ) ;
}
2009-01-06 14:41:11 +01:00
// --- cFrameDetector --------------------------------------------------------
2009-11-22 11:30:27 +01:00
# define EMPTY_SCANNER (0xFFFFFFFF)
2009-01-06 14:41:11 +01:00
cFrameDetector : : cFrameDetector ( int Pid , int Type )
{
2009-11-22 11:30:27 +01:00
SetPid ( Pid , Type ) ;
2009-03-27 13:38:59 +01:00
synced = false ;
2011-09-04 10:13:14 +02:00
newFrame = independentFrame = false ;
2009-03-27 13:38:59 +01:00
numPtsValues = 0 ;
2011-03-20 10:22:22 +01:00
numFrames = 0 ;
2009-03-27 13:38:59 +01:00
numIFrames = 0 ;
2010-11-01 12:31:52 +01:00
framesPerSecond = 0 ;
2009-03-27 13:38:59 +01:00
framesInPayloadUnit = framesPerPayloadUnit = 0 ;
2011-03-20 10:22:22 +01:00
payloadUnitOfFrame = 0 ;
2009-01-06 14:41:11 +01:00
scanning = false ;
2009-11-22 11:30:27 +01:00
scanner = EMPTY_SCANNER ;
2009-01-06 14:41:11 +01:00
}
2009-03-27 13:38:59 +01:00
static int CmpUint32 ( const void * p1 , const void * p2 )
{
if ( * ( uint32_t * ) p1 < * ( uint32_t * ) p2 ) return - 1 ;
if ( * ( uint32_t * ) p1 > * ( uint32_t * ) p2 ) return 1 ;
return 0 ;
}
2009-11-22 11:30:27 +01:00
void cFrameDetector : : SetPid ( int Pid , int Type )
{
pid = Pid ;
type = Type ;
isVideo = type = = 0x01 | | type = = 0x02 | | type = = 0x1B ; // MPEG 1, 2 or 4
}
void cFrameDetector : : Reset ( void )
{
2011-09-04 10:13:14 +02:00
newFrame = independentFrame = false ;
2011-03-20 10:22:22 +01:00
payloadUnitOfFrame = 0 ;
2009-11-22 11:30:27 +01:00
scanning = false ;
scanner = EMPTY_SCANNER ;
}
2011-09-04 13:09:06 +02:00
int cFrameDetector : : SkipPackets ( const uchar * & Data , int & Length , int & Processed , int & FrameTypeOffset )
{
if ( ! synced )
dbgframes ( " %d> " , FrameTypeOffset ) ;
while ( Length > = TS_SIZE ) {
// switch to the next TS packet, but skip those that have a different PID:
Data + = TS_SIZE ;
Length - = TS_SIZE ;
Processed + = TS_SIZE ;
if ( TsPid ( Data ) = = pid )
break ;
else if ( Length < TS_SIZE )
esyslog ( " ERROR: out of data while skipping TS packets in cFrameDetector " ) ;
}
FrameTypeOffset - = TS_SIZE ;
FrameTypeOffset + = TsPayloadOffset ( Data ) ;
return FrameTypeOffset ;
}
2009-01-06 14:41:11 +01:00
int cFrameDetector : : Analyze ( const uchar * Data , int Length )
{
2011-09-04 10:13:14 +02:00
int SeenPayloadStart = false ;
2009-03-27 14:11:43 +01:00
int Processed = 0 ;
2011-09-04 10:13:14 +02:00
newFrame = independentFrame = false ;
2009-03-27 14:11:43 +01:00
while ( Length > = TS_SIZE ) {
2009-05-03 14:45:53 +02:00
if ( Data [ 0 ] ! = TS_SYNC_BYTE ) {
int Skipped = 1 ;
while ( Skipped < Length & & ( Data [ Skipped ] ! = TS_SYNC_BYTE | | Length - Skipped > TS_SIZE & & Data [ Skipped + TS_SIZE ] ! = TS_SYNC_BYTE ) )
Skipped + + ;
esyslog ( " ERROR: skipped %d bytes to sync on start of TS packet " , Skipped ) ;
return Processed + Skipped ;
}
2009-11-22 11:30:27 +01:00
if ( TsHasPayload ( Data ) & & ! TsIsScrambled ( Data ) ) {
int Pid = TsPid ( Data ) ;
if ( Pid = = pid ) {
if ( TsPayloadStart ( Data ) ) {
2011-09-04 10:13:14 +02:00
SeenPayloadStart = true ;
2009-11-22 11:30:27 +01:00
if ( synced & & Processed )
2011-09-04 10:13:14 +02:00
return Processed ;
if ( Length < MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE )
return Processed ; // need more data, in case the frame type is not stored in the first TS packet
2011-03-13 13:58:22 +01:00
if ( framesPerSecond < = 0.0 ) {
2010-11-01 12:31:52 +01:00
// frame rate unknown, so collect a sequence of PTS values:
2011-08-27 14:27:22 +02:00
if ( numPtsValues < 2 | | numPtsValues < MaxPtsValues & & numIFrames < 2 ) { // collect a sequence containing at least two I-frames
2009-11-22 11:30:27 +01:00
const uchar * Pes = Data + TsPayloadOffset ( Data ) ;
2011-06-11 11:40:18 +02:00
if ( numIFrames & & PesHasPts ( Pes ) ) {
2009-11-22 11:30:27 +01:00
ptsValues [ numPtsValues ] = PesGetPts ( Pes ) ;
// check for rollover:
if ( numPtsValues & & ptsValues [ numPtsValues - 1 ] > 0xF0000000 & & ptsValues [ numPtsValues ] < 0x10000000 ) {
dbgframes ( " # " ) ;
numPtsValues = 0 ;
numIFrames = 0 ;
2011-03-20 10:22:22 +01:00
numFrames = 0 ;
2009-11-22 11:30:27 +01:00
}
else
numPtsValues + + ;
2009-03-27 14:11:43 +01:00
}
2009-03-27 13:38:59 +01:00
}
2009-11-22 11:30:27 +01:00
else {
// find the smallest PTS delta:
qsort ( ptsValues , numPtsValues , sizeof ( uint32_t ) , CmpUint32 ) ;
numPtsValues - - ;
for ( int i = 0 ; i < numPtsValues ; i + + )
ptsValues [ i ] = ptsValues [ i + 1 ] - ptsValues [ i ] ;
qsort ( ptsValues , numPtsValues , sizeof ( uint32_t ) , CmpUint32 ) ;
uint32_t Delta = ptsValues [ 0 ] ;
// determine frame info:
if ( isVideo ) {
2010-11-01 12:31:52 +01:00
if ( abs ( Delta - 3600 ) < = 1 )
framesPerSecond = 25.0 ;
2009-11-22 11:30:27 +01:00
else if ( Delta % 3003 = = 0 )
2010-11-01 12:31:52 +01:00
framesPerSecond = 30.0 / 1.001 ;
2011-03-20 10:22:22 +01:00
else if ( abs ( Delta - 1800 ) < = 1 ) {
if ( numFrames > 50 ) {
// this is a "best guess": if there are more than 50 frames between two I-frames, we assume each "frame" actually contains a "field", so two "fields" make one "frame"
framesPerSecond = 25.0 ;
framesPerPayloadUnit = - 2 ;
}
else
framesPerSecond = 50.0 ;
}
2010-11-01 12:31:52 +01:00
else if ( Delta = = 1501 )
2011-03-20 10:22:22 +01:00
if ( numFrames > 50 ) {
// this is a "best guess": if there are more than 50 frames between two I-frames, we assume each "frame" actually contains a "field", so two "fields" make one "frame"
framesPerSecond = 30.0 / 1.001 ;
framesPerPayloadUnit = - 2 ;
}
else
framesPerSecond = 60.0 / 1.001 ;
2009-11-22 11:30:27 +01:00
else {
2011-06-12 14:24:09 +02:00
framesPerSecond = DEFAULTFRAMESPERSECOND ;
dsyslog ( " unknown frame delta (%d), assuming %5.2f fps " , Delta , DEFAULTFRAMESPERSECOND ) ;
2009-11-22 11:30:27 +01:00
}
2009-03-27 14:11:43 +01:00
}
2009-11-22 11:30:27 +01:00
else // audio
2010-11-01 12:31:52 +01:00
framesPerSecond = 90000.0 / Delta ; // PTS of audio frames is always increasing
2011-03-20 10:22:22 +01:00
dbgframes ( " \n Delta = %d FPS = %5.2f FPPU = %d NF = %d \n " , Delta , framesPerSecond , framesPerPayloadUnit , numFrames ) ;
2009-01-06 14:41:11 +01:00
}
}
2009-11-22 11:30:27 +01:00
scanner = EMPTY_SCANNER ;
scanning = true ;
2009-01-06 14:41:11 +01:00
}
2009-11-22 11:30:27 +01:00
if ( scanning ) {
int PayloadOffset = TsPayloadOffset ( Data ) ;
if ( TsPayloadStart ( Data ) ) {
PayloadOffset + = PesPayloadOffset ( Data + PayloadOffset ) ;
if ( ! framesPerPayloadUnit )
framesPerPayloadUnit = framesInPayloadUnit ;
if ( DebugFrames & & ! synced )
dbgframes ( " / " ) ;
}
for ( int i = PayloadOffset ; scanning & & i < TS_SIZE ; i + + ) {
2011-09-04 10:13:14 +02:00
scanner < < = 8 ;
scanner | = Data [ i ] ;
2009-11-22 11:30:27 +01:00
switch ( type ) {
case 0x01 : // MPEG 1 video
case 0x02 : // MPEG 2 video
if ( scanner = = 0x00000100 ) { // Picture Start Code
scanner = EMPTY_SCANNER ;
2011-09-04 10:13:14 +02:00
if ( synced & & ! SeenPayloadStart & & Processed )
return Processed ; // flush everything before this new frame
2011-09-04 13:09:06 +02:00
int FrameTypeOffset = i + 2 ;
if ( FrameTypeOffset > = TS_SIZE ) // the byte to check is in the next TS packet
i = SkipPackets ( Data , Length , Processed , FrameTypeOffset ) ;
2009-11-22 11:30:27 +01:00
newFrame = true ;
2011-09-04 13:09:06 +02:00
uchar FrameType = ( Data [ FrameTypeOffset ] > > 3 ) & 0x07 ;
2011-06-12 14:06:11 +02:00
independentFrame = FrameType = = 1 ; // I-Frame
2009-11-22 11:30:27 +01:00
if ( synced ) {
if ( framesPerPayloadUnit < = 1 )
scanning = false ;
}
else {
framesInPayloadUnit + + ;
if ( independentFrame )
numIFrames + + ;
2011-03-20 10:22:22 +01:00
if ( numIFrames = = 1 )
numFrames + + ;
2011-06-12 14:06:11 +02:00
dbgframes ( " %u " , FrameType ) ;
2009-11-22 11:30:27 +01:00
}
if ( synced )
return Processed + TS_SIZE ; // flag this new frame
2009-01-06 14:41:11 +01:00
}
2009-11-22 11:30:27 +01:00
break ;
case 0x1B : // MPEG 4 video
if ( scanner = = 0x00000109 ) { // Access Unit Delimiter
scanner = EMPTY_SCANNER ;
2011-09-04 10:13:14 +02:00
if ( synced & & ! SeenPayloadStart & & Processed )
return Processed ; // flush everything before this new frame
2011-09-04 13:09:06 +02:00
int FrameTypeOffset = i + 1 ;
if ( FrameTypeOffset > = TS_SIZE ) // the byte to check is in the next TS packet
i = SkipPackets ( Data , Length , Processed , FrameTypeOffset ) ;
2009-11-22 11:30:27 +01:00
newFrame = true ;
2012-09-14 09:11:18 +02:00
uchar FrameType = Data [ FrameTypeOffset ] & 0xE0 ;
independentFrame = FrameType = = 0x00 ;
2009-11-22 11:30:27 +01:00
if ( synced ) {
2011-03-20 10:22:22 +01:00
if ( framesPerPayloadUnit < 0 ) {
payloadUnitOfFrame = ( payloadUnitOfFrame + 1 ) % - framesPerPayloadUnit ;
if ( payloadUnitOfFrame ! = 0 & & independentFrame )
payloadUnitOfFrame = 0 ;
2011-09-04 10:13:14 +02:00
if ( payloadUnitOfFrame )
2011-03-20 10:22:22 +01:00
newFrame = false ;
}
2009-11-22 11:30:27 +01:00
if ( framesPerPayloadUnit < = 1 )
scanning = false ;
}
else {
framesInPayloadUnit + + ;
if ( independentFrame )
numIFrames + + ;
2011-03-20 10:22:22 +01:00
if ( numIFrames = = 1 )
numFrames + + ;
2011-06-12 14:06:11 +02:00
dbgframes ( " %02X " , FrameType ) ;
2009-11-22 11:30:27 +01:00
}
if ( synced )
return Processed + TS_SIZE ; // flag this new frame
2009-03-27 14:11:43 +01:00
}
2009-11-22 11:30:27 +01:00
break ;
case 0x04 : // MPEG audio
case 0x06 : // AC3 audio
2009-11-01 15:02:49 +01:00
if ( synced & & Processed )
return Processed ;
2009-03-27 14:11:43 +01:00
newFrame = true ;
2009-11-22 11:30:27 +01:00
independentFrame = true ;
if ( ! synced ) {
framesInPayloadUnit = 1 ;
if ( TsPayloadStart ( Data ) )
2009-03-27 14:11:43 +01:00
numIFrames + + ;
2009-01-06 14:41:11 +01:00
}
2009-11-22 11:30:27 +01:00
scanning = false ;
break ;
default : esyslog ( " ERROR: unknown stream type %d (PID %d) in frame detector " , type , pid ) ;
pid = 0 ; // let's just ignore any further data
}
}
2011-03-13 13:58:22 +01:00
if ( ! synced & & framesPerSecond > 0.0 & & independentFrame ) {
2009-11-22 11:30:27 +01:00
synced = true ;
2011-03-20 10:22:22 +01:00
dbgframes ( " * \n " ) ;
2009-11-22 11:30:27 +01:00
Reset ( ) ;
return Processed + TS_SIZE ;
2009-03-27 14:11:43 +01:00
}
2009-01-06 14:41:11 +01:00
}
2009-03-27 13:38:59 +01:00
}
2009-11-22 11:30:27 +01:00
else if ( Pid = = PATPID & & synced & & Processed )
return Processed ; // allow the caller to see any PAT packets
2009-01-06 14:41:11 +01:00
}
2009-03-27 14:11:43 +01:00
Data + = TS_SIZE ;
Length - = TS_SIZE ;
Processed + = TS_SIZE ;
2009-01-06 14:41:11 +01:00
}
2009-03-27 14:11:43 +01:00
return Processed ;
2009-01-06 14:41:11 +01:00
}