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 .
*
2013-02-17 13:19:36 +01:00
* $ Id : remux . c 2.74 2013 / 02 / 17 13 : 17 : 58 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
2012-11-02 14:35:57 +01:00
# define EMPTY_SCANNER (0xFFFFFFFF)
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 ------------------------------------------------
2012-11-18 12:19:51 +01:00
void TsHidePayload ( uchar * p )
{
p [ 1 ] & = ~ TS_PAYLOAD_START ;
p [ 3 ] | = TS_ADAPT_FIELD_EXISTS ;
p [ 3 ] & = ~ TS_PAYLOAD_EXISTS ;
p [ 4 ] = TS_SIZE - 5 ;
p [ 5 ] = 0x00 ;
memset ( p + 6 , 0xFF , TS_SIZE - 6 ) ;
}
void TsSetPcr ( uchar * p , int64_t Pcr )
{
if ( TsHasAdaptationField ( p ) ) {
if ( p [ 4 ] > = 7 & & ( p [ 5 ] & TS_ADAPT_PCR ) ) {
int64_t b = Pcr / PCRFACTOR ;
int e = Pcr % PCRFACTOR ;
p [ 6 ] = b > > 25 ;
p [ 7 ] = b > > 17 ;
p [ 8 ] = b > > 9 ;
p [ 9 ] = b > > 1 ;
p [ 10 ] = ( b < < 7 ) | ( p [ 10 ] & 0x7E ) | ( ( e > > 8 ) & 0x01 ) ;
p [ 11 ] = e ;
}
}
}
2009-03-13 14:45:12 +01:00
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 ;
}
2012-11-18 12:19:51 +01:00
int64_t TsGetDts ( const uchar * p , int l )
2009-04-19 11:07:07 +02:00
{
2012-11-18 12:19:51 +01:00
// Find the first packet with a DTS and use it:
while ( l > 0 ) {
const uchar * d = p ;
if ( TsPayloadStart ( d ) & & TsGetPayload ( & d ) & & PesHasDts ( d ) )
return PesGetDts ( d ) ;
p + = TS_SIZE ;
l - = TS_SIZE ;
}
return - 1 ;
}
void TsSetPts ( uchar * p , int l , int64_t Pts )
{
// Find the first packet with a PTS and use it:
while ( l > 0 ) {
const uchar * d = p ;
if ( TsPayloadStart ( d ) & & TsGetPayload ( & d ) & & PesHasPts ( d ) ) {
PesSetPts ( const_cast < uchar * > ( d ) , Pts ) ;
return ;
2009-04-19 11:07:07 +02:00
}
2012-11-18 12:19:51 +01:00
p + = TS_SIZE ;
2009-04-19 11:07:07 +02:00
l - = TS_SIZE ;
2012-11-18 12:19:51 +01:00
}
}
void TsSetDts ( uchar * p , int l , int64_t Dts )
{
// Find the first packet with a DTS and use it:
while ( l > 0 ) {
const uchar * d = p ;
if ( TsPayloadStart ( d ) & & TsGetPayload ( & d ) & & PesHasDts ( d ) ) {
PesSetDts ( const_cast < uchar * > ( d ) , Dts ) ;
return ;
}
2009-04-19 11:07:07 +02:00
p + = TS_SIZE ;
2012-11-18 12:19:51 +01:00
l - = TS_SIZE ;
2009-04-19 11:07:07 +02:00
}
}
2012-11-18 12:19:51 +01:00
// --- Some PES handling tools -----------------------------------------------
void PesSetPts ( uchar * p , int64_t Pts )
{
p [ 9 ] = ( ( Pts > > 29 ) & 0x0E ) | ( p [ 9 ] & 0xF1 ) ;
p [ 10 ] = Pts > > 22 ;
p [ 11 ] = ( ( Pts > > 14 ) & 0xFE ) | 0x01 ;
p [ 12 ] = Pts > > 7 ;
p [ 13 ] = ( ( Pts < < 1 ) & 0xFE ) | 0x01 ;
}
void PesSetDts ( uchar * p , int64_t Dts )
{
p [ 14 ] = ( ( Dts > > 29 ) & 0x0E ) | ( p [ 14 ] & 0xF1 ) ;
p [ 15 ] = Dts > > 22 ;
p [ 16 ] = ( ( Dts > > 14 ) & 0xFE ) | 0x01 ;
p [ 17 ] = Dts > > 7 ;
p [ 18 ] = ( ( Dts < < 1 ) & 0xFE ) | 0x01 ;
}
int64_t PtsDiff ( int64_t Pts1 , int64_t Pts2 )
{
int64_t d = Pts2 - Pts1 ;
if ( d > MAX33BIT / 2 )
return d - ( MAX33BIT + 1 ) ;
if ( d < - MAX33BIT / 2 )
return d + ( MAX33BIT + 1 ) ;
return d ;
}
2012-11-02 14:35:57 +01:00
// --- cTsPayload ------------------------------------------------------------
cTsPayload : : cTsPayload ( void )
{
data = NULL ;
length = 0 ;
pid = - 1 ;
index = 0 ;
}
cTsPayload : : cTsPayload ( uchar * Data , int Length , int Pid )
{
Setup ( Data , Length , Pid ) ;
}
void cTsPayload : : Setup ( uchar * Data , int Length , int Pid )
{
data = Data ;
length = Length ;
pid = Pid > = 0 ? Pid : TsPid ( Data ) ;
index = 0 ;
}
uchar cTsPayload : : GetByte ( void )
{
if ( ! Eof ( ) ) {
if ( index % TS_SIZE = = 0 ) { // encountered the next TS header
for ( ; ; index + = TS_SIZE ) {
if ( data [ index ] = = TS_SYNC_BYTE & & index + TS_SIZE < = length ) { // to make sure we are at a TS header start and drop incomplete TS packets at the end
uchar * p = data + index ;
if ( TsPid ( p ) = = pid ) { // only handle TS packets for the initial PID
if ( TsHasPayload ( p ) ) {
if ( index > 0 & & TsPayloadStart ( p ) ) { // checking index to not skip the very first TS packet
length = index ; // triggers EOF
return 0x00 ;
}
index + = TsPayloadOffset ( p ) ;
break ;
}
}
}
else {
length = index ; // triggers EOF
return 0x00 ;
}
}
}
return data [ index + + ] ;
}
return 0x00 ;
}
bool cTsPayload : : SkipBytes ( int Bytes )
{
while ( Bytes - - > 0 )
GetByte ( ) ;
return ! Eof ( ) ;
}
bool cTsPayload : : SkipPesHeader ( void )
{
return SkipBytes ( PesPayloadOffset ( data + TsPayloadOffset ( data ) ) ) ;
}
int cTsPayload : : GetLastIndex ( void )
{
return index - 1 ;
}
void cTsPayload : : SetByte ( uchar Byte , int Index )
{
if ( Index > = 0 & & Index < length )
data [ Index ] = Byte ;
}
bool cTsPayload : : Find ( uint32_t Code )
{
int OldIndex = index ;
uint32_t Scanner = EMPTY_SCANNER ;
while ( ! Eof ( ) ) {
Scanner = ( Scanner < < 8 ) | GetByte ( ) ;
if ( Scanner = = Code )
return true ;
}
index = OldIndex ;
return false ;
}
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 ;
2012-11-19 10:32:31 +01:00
pmtPids [ 0 ] = 0 ;
2008-08-15 14:49:34 +02:00
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 ;
2012-11-19 10:32:31 +01:00
int NumPmtPids = 0 ;
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 ( ) ) {
2012-11-19 10:32:31 +01:00
if ( NumPmtPids < = MAX_PMT_PIDS )
pmtPids [ NumPmtPids + + ] = assoc . getPid ( ) ;
2008-08-15 14:49:34 +02:00
dbgpatpmt ( " service id = %d, pid = %d \n " , assoc . getServiceId ( ) , assoc . getPid ( ) ) ;
}
}
2012-11-19 10:32:31 +01:00
pmtPids [ NumPmtPids ] = 0 ;
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
2012-11-25 14:21:15 +01:00
case 0x1B : // H.264
2008-08-15 14:49:34 +02:00
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 ;
2012-09-19 10:46:17 +02:00
case 0x81 : // STREAMTYPE_USER_PRIVATE
{
dbgpatpmt ( " AC3 " ) ;
char lang [ MAXLANGCODE1 ] = { 0 } ;
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 ;
dbgpatpmt ( " '%s' " , ld - > languageCode ) ;
strn0cpy ( lang , I18nNormalizeLanguageCode ( ld - > languageCode ) , MAXLANGCODE1 ) ;
}
break ;
default : ;
}
delete d ;
}
if ( NumDpids < MAXDPIDS ) {
dpids [ NumDpids ] = stream . getPid ( ) ;
dtypes [ NumDpids ] = SI : : AC3DescriptorTag ;
strn0cpy ( dlangs [ NumDpids ] , lang , sizeof ( dlangs [ NumDpids ] ) ) ;
if ( updatePrimaryDevice & & Setup . UseDolbyDigital )
cDevice : : PrimaryDevice ( ) - > SetAvailableTrack ( ttDolby , NumDpids , stream . getPid ( ) , lang ) ;
NumDpids + + ;
dpids [ NumDpids ] = 0 ;
}
}
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 ;
}
2012-11-02 14:35:57 +01:00
bool cPatPmtParser : : ParsePatPmt ( const uchar * Data , int Length )
{
while ( Length > = TS_SIZE ) {
if ( * Data ! = TS_SYNC_BYTE )
break ; // just for safety
int Pid = TsPid ( Data ) ;
if ( Pid = = PATPID )
ParsePat ( Data , TS_SIZE ) ;
2012-11-19 10:32:31 +01:00
else if ( IsPmtPid ( Pid ) ) {
2012-11-02 14:35:57 +01:00
ParsePmt ( Data , TS_SIZE ) ;
if ( patVersion > = 0 & & pmtVersion > = 0 )
return true ;
}
Data + = TS_SIZE ;
Length - = TS_SIZE ;
}
return false ;
}
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
2012-11-02 14:35:57 +01:00
// --- cFrameParser ----------------------------------------------------------
class cFrameParser {
protected :
bool debug ;
bool newFrame ;
bool independentFrame ;
public :
cFrameParser ( void ) ;
virtual ~ cFrameParser ( ) { } ;
virtual int Parse ( const uchar * Data , int Length , int Pid ) = 0 ;
///< Parses the given Data, which is a sequence of Length bytes of TS packets.
///< The payload in the TS packets with the given Pid is searched for just
///< enough information to determine the beginning and type of the next video
///< frame.
///< Returns the number of bytes parsed. Upon return, the functions NewFrame()
///< and IndependentFrame() can be called to retrieve the required information.
void SetDebug ( bool Debug ) { debug = Debug ; }
bool NewFrame ( void ) { return newFrame ; }
bool IndependentFrame ( void ) { return independentFrame ; }
} ;
cFrameParser : : cFrameParser ( void )
{
debug = true ;
newFrame = false ;
independentFrame = false ;
}
2009-01-06 14:41:11 +01:00
2012-11-02 14:35:57 +01:00
// --- cAudioParser ----------------------------------------------------------
class cAudioParser : public cFrameParser {
public :
cAudioParser ( void ) ;
virtual int Parse ( const uchar * Data , int Length , int Pid ) ;
} ;
cAudioParser : : cAudioParser ( void )
{
}
int cAudioParser : : Parse ( const uchar * Data , int Length , int Pid )
{
if ( TsPayloadStart ( Data ) ) {
newFrame = independentFrame = true ;
if ( debug )
dbgframes ( " / " ) ;
}
else
newFrame = independentFrame = false ;
return TS_SIZE ;
}
// --- cMpeg2Parser ----------------------------------------------------------
class cMpeg2Parser : public cFrameParser {
private :
uint32_t scanner ;
bool seenIndependentFrame ;
public :
cMpeg2Parser ( void ) ;
virtual int Parse ( const uchar * Data , int Length , int Pid ) ;
} ;
cMpeg2Parser : : cMpeg2Parser ( void )
{
scanner = EMPTY_SCANNER ;
seenIndependentFrame = false ;
}
int cMpeg2Parser : : Parse ( const uchar * Data , int Length , int Pid )
{
newFrame = independentFrame = false ;
2012-11-13 10:00:00 +01:00
bool SeenPayloadStart = false ;
2012-11-02 14:35:57 +01:00
cTsPayload tsPayload ( const_cast < uchar * > ( Data ) , Length , Pid ) ;
if ( TsPayloadStart ( Data ) ) {
2012-11-13 10:00:00 +01:00
SeenPayloadStart = true ;
2012-11-02 14:35:57 +01:00
tsPayload . SkipPesHeader ( ) ;
scanner = EMPTY_SCANNER ;
if ( debug & & seenIndependentFrame )
dbgframes ( " / " ) ;
}
2012-11-13 10:00:00 +01:00
uint32_t OldScanner = scanner ; // need to remember it in case of multiple frames per payload
2012-11-06 11:03:06 +01:00
for ( ; ; ) {
2012-11-13 10:00:00 +01:00
if ( ! SeenPayloadStart & & tsPayload . AtTsStart ( ) )
OldScanner = scanner ;
2012-11-06 11:03:06 +01:00
scanner = ( scanner < < 8 ) | tsPayload . GetByte ( ) ;
if ( scanner = = 0x00000100 ) { // Picture Start Code
2012-11-13 10:00:00 +01:00
if ( ! SeenPayloadStart & & tsPayload . GetLastIndex ( ) > TS_SIZE ) {
scanner = OldScanner ;
return tsPayload . Used ( ) - TS_SIZE ;
}
2012-11-06 11:03:06 +01:00
newFrame = true ;
tsPayload . GetByte ( ) ;
uchar FrameType = ( tsPayload . GetByte ( ) > > 3 ) & 0x07 ;
independentFrame = FrameType = = 1 ; // I-Frame
if ( debug ) {
seenIndependentFrame | = independentFrame ;
if ( seenIndependentFrame ) {
static const char FrameTypes [ ] = " ?IPBD??? " ;
dbgframes ( " %c " , FrameTypes [ FrameType ] ) ;
}
}
break ;
}
if ( tsPayload . AtPayloadStart ( ) // stop at any new payload start to have the buffer refilled if necessary
| | ( tsPayload . Available ( ) < MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE // stop if the available data is below the limit...
& & ( tsPayload . Available ( ) < = 0 | | tsPayload . AtTsStart ( ) ) ) ) // ...but only if there is no more data at all, or if we are at a TS boundary
break ;
}
2012-11-02 14:35:57 +01:00
return tsPayload . Used ( ) ;
}
2012-11-25 14:21:15 +01:00
// --- cH264Parser -----------------------------------------------------------
2012-11-02 14:35:57 +01:00
2012-11-25 14:21:15 +01:00
class cH264Parser : public cFrameParser {
2012-11-02 14:35:57 +01:00
private :
enum eNalUnitType {
nutCodedSliceNonIdr = 1 ,
nutCodedSliceIdr = 5 ,
nutSequenceParameterSet = 7 ,
nutAccessUnitDelimiter = 9 ,
} ;
cTsPayload tsPayload ;
uchar byte ; // holds the current byte value in case of bitwise access
int bit ; // the bit index into the current byte (-1 if we're not in bit reading mode)
int zeroBytes ; // the number of consecutive zero bytes (to detect 0x000003)
uint32_t scanner ;
// Identifiers written in '_' notation as in "ITU-T H.264":
bool separate_colour_plane_flag ;
int log2_max_frame_num ;
bool frame_mbs_only_flag ;
//
bool gotAccessUnitDelimiter ;
bool gotSequenceParameterSet ;
uchar GetByte ( bool Raw = false ) ;
///< Gets the next data byte. If Raw is true, no filtering will be done.
///< With Raw set to false, if the byte sequence 0x000003 is encountered,
///< the byte with 0x03 will be skipped.
uchar GetBit ( void ) ;
uint32_t GetBits ( int Bits ) ;
uint32_t GetGolombUe ( void ) ;
int32_t GetGolombSe ( void ) ;
void ParseAccessUnitDelimiter ( void ) ;
void ParseSequenceParameterSet ( void ) ;
void ParseSliceHeader ( void ) ;
public :
2012-11-25 14:21:15 +01:00
cH264Parser ( void ) ;
///< Sets up a new H.264 parser.
2012-11-02 14:35:57 +01:00
///< This class parses only the data absolutely necessary to determine the
///< frame borders and field count of the given H264 material.
virtual int Parse ( const uchar * Data , int Length , int Pid ) ;
} ;
2012-11-25 14:21:15 +01:00
cH264Parser : : cH264Parser ( void )
2012-11-02 14:35:57 +01:00
{
byte = 0 ;
bit = - 1 ;
zeroBytes = 0 ;
scanner = EMPTY_SCANNER ;
separate_colour_plane_flag = false ;
log2_max_frame_num = 0 ;
frame_mbs_only_flag = false ;
gotAccessUnitDelimiter = false ;
gotSequenceParameterSet = false ;
}
2012-11-25 14:21:15 +01:00
uchar cH264Parser : : GetByte ( bool Raw )
2012-11-02 14:35:57 +01:00
{
uchar b = tsPayload . GetByte ( ) ;
if ( ! Raw ) {
// If we encounter the byte sequence 0x000003, we need to skip the 0x03:
if ( b = = 0x00 )
zeroBytes + + ;
else {
if ( b = = 0x03 & & zeroBytes > = 2 )
b = tsPayload . GetByte ( ) ;
zeroBytes = 0 ;
}
}
else
zeroBytes = 0 ;
bit = - 1 ;
return b ;
}
2012-11-25 14:21:15 +01:00
uchar cH264Parser : : GetBit ( void )
2012-11-02 14:35:57 +01:00
{
if ( bit < 0 ) {
byte = GetByte ( ) ;
bit = 7 ;
}
return ( byte & ( 1 < < bit - - ) ) ? 1 : 0 ;
}
2012-11-25 14:21:15 +01:00
uint32_t cH264Parser : : GetBits ( int Bits )
2012-11-02 14:35:57 +01:00
{
uint32_t b = 0 ;
while ( Bits - - )
b | = GetBit ( ) < < Bits ;
return b ;
}
2012-11-25 14:21:15 +01:00
uint32_t cH264Parser : : GetGolombUe ( void )
2012-11-02 14:35:57 +01:00
{
int z = - 1 ;
for ( int b = 0 ; ! b ; z + + )
b = GetBit ( ) ;
return ( 1 < < z ) - 1 + GetBits ( z ) ;
}
2012-11-25 14:21:15 +01:00
int32_t cH264Parser : : GetGolombSe ( void )
2012-11-02 14:35:57 +01:00
{
uint32_t v = GetGolombUe ( ) ;
if ( v ) {
if ( ( v & 0x01 ) ! = 0 )
return ( v + 1 ) / 2 ; // fails for v == 0xFFFFFFFF, but that will probably never happen
else
return - int32_t ( v / 2 ) ;
}
return v ;
}
2012-11-25 14:21:15 +01:00
int cH264Parser : : Parse ( const uchar * Data , int Length , int Pid )
2012-11-02 14:35:57 +01:00
{
newFrame = independentFrame = false ;
tsPayload . Setup ( const_cast < uchar * > ( Data ) , Length , Pid ) ;
if ( TsPayloadStart ( Data ) ) {
tsPayload . SkipPesHeader ( ) ;
scanner = EMPTY_SCANNER ;
if ( debug & & gotSequenceParameterSet ) {
dbgframes ( " / " ) ;
}
}
2012-11-06 11:03:06 +01:00
for ( ; ; ) {
scanner = ( scanner < < 8 ) | GetByte ( true ) ;
if ( ( scanner & 0xFFFFFF00 ) = = 0x00000100 ) { // NAL unit start
uchar NalUnitType = scanner & 0x1F ;
switch ( NalUnitType ) {
case nutAccessUnitDelimiter : ParseAccessUnitDelimiter ( ) ;
gotAccessUnitDelimiter = true ;
break ;
case nutSequenceParameterSet : ParseSequenceParameterSet ( ) ;
gotSequenceParameterSet = true ;
break ;
case nutCodedSliceNonIdr :
case nutCodedSliceIdr : if ( gotAccessUnitDelimiter & & gotSequenceParameterSet ) {
2013-02-17 13:19:36 +01:00
ParseSliceHeader ( ) ;
2012-11-06 11:03:06 +01:00
gotAccessUnitDelimiter = false ;
return tsPayload . Used ( ) ;
}
break ;
default : ;
}
}
if ( tsPayload . AtPayloadStart ( ) // stop at any new payload start to have the buffer refilled if necessary
| | ( tsPayload . Available ( ) < MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE // stop if the available data is below the limit...
& & ( tsPayload . Available ( ) < = 0 | | tsPayload . AtTsStart ( ) ) ) ) // ...but only if there is no more data at all, or if we are at a TS boundary
break ;
}
2012-11-02 14:35:57 +01:00
return tsPayload . Used ( ) ;
}
2012-11-25 14:21:15 +01:00
void cH264Parser : : ParseAccessUnitDelimiter ( void )
2012-11-02 14:35:57 +01:00
{
if ( debug & & gotSequenceParameterSet )
dbgframes ( " A " ) ;
GetByte ( ) ; // primary_pic_type
}
2012-11-25 14:21:15 +01:00
void cH264Parser : : ParseSequenceParameterSet ( void )
2012-11-02 14:35:57 +01:00
{
uchar profile_idc = GetByte ( ) ; // profile_idc
GetByte ( ) ; // constraint_set[0-5]_flags, reserved_zero_2bits
GetByte ( ) ; // level_idc
GetGolombUe ( ) ; // seq_parameter_set_id
if ( profile_idc = = 100 | | profile_idc = = 110 | | profile_idc = = 122 | | profile_idc = = 244 | | profile_idc = = 44 | | profile_idc = = 83 | | profile_idc = = 86 | | profile_idc = = 118 | | profile_idc = = 128 ) {
int chroma_format_idc = GetGolombUe ( ) ; // chroma_format_idc
if ( chroma_format_idc = = 3 )
separate_colour_plane_flag = GetBit ( ) ;
GetGolombUe ( ) ; // bit_depth_luma_minus8
GetGolombUe ( ) ; // bit_depth_chroma_minus8
GetBit ( ) ; // qpprime_y_zero_transform_bypass_flag
if ( GetBit ( ) ) { // seq_scaling_matrix_present_flag
for ( int i = 0 ; i < ( ( chroma_format_idc ! = 3 ) ? 8 : 12 ) ; i + + ) {
if ( GetBit ( ) ) { // seq_scaling_list_present_flag
int SizeOfScalingList = ( i < 6 ) ? 16 : 64 ;
int LastScale = 8 ;
int NextScale = 8 ;
for ( int j = 0 ; j < SizeOfScalingList ; j + + ) {
if ( NextScale )
NextScale = ( LastScale + GetGolombSe ( ) + 256 ) % 256 ; // delta_scale
if ( NextScale )
LastScale = NextScale ;
}
}
}
}
}
log2_max_frame_num = GetGolombUe ( ) + 4 ; // log2_max_frame_num_minus4
int pic_order_cnt_type = GetGolombUe ( ) ; // pic_order_cnt_type
if ( pic_order_cnt_type = = 0 )
GetGolombUe ( ) ; // log2_max_pic_order_cnt_lsb_minus4
else if ( pic_order_cnt_type = = 1 ) {
GetBit ( ) ; // delta_pic_order_always_zero_flag
GetGolombSe ( ) ; // offset_for_non_ref_pic
GetGolombSe ( ) ; // offset_for_top_to_bottom_field
for ( int i = GetGolombUe ( ) ; i - - ; ) // num_ref_frames_in_pic_order_cnt_cycle
GetGolombSe ( ) ; // offset_for_ref_frame
}
GetGolombUe ( ) ; // max_num_ref_frames
GetBit ( ) ; // gaps_in_frame_num_value_allowed_flag
GetGolombUe ( ) ; // pic_width_in_mbs_minus1
GetGolombUe ( ) ; // pic_height_in_map_units_minus1
frame_mbs_only_flag = GetBit ( ) ; // frame_mbs_only_flag
if ( debug ) {
if ( gotAccessUnitDelimiter & & ! gotSequenceParameterSet )
dbgframes ( " A " ) ; // just for completeness
dbgframes ( frame_mbs_only_flag ? " S " : " s " ) ;
}
}
2012-11-25 14:21:15 +01:00
void cH264Parser : : ParseSliceHeader ( void )
2012-11-02 14:35:57 +01:00
{
newFrame = true ;
GetGolombUe ( ) ; // first_mb_in_slice
int slice_type = GetGolombUe ( ) ; // slice_type, 0 = P, 1 = B, 2 = I, 3 = SP, 4 = SI
independentFrame = ( slice_type % 5 ) = = 2 ;
if ( debug ) {
static const char SliceTypes [ ] = " PBIpi " ;
dbgframes ( " %c " , SliceTypes [ slice_type % 5 ] ) ;
}
if ( frame_mbs_only_flag )
return ; // don't need the rest - a frame is complete
GetGolombUe ( ) ; // pic_parameter_set_id
if ( separate_colour_plane_flag )
GetBits ( 2 ) ; // colour_plane_id
GetBits ( log2_max_frame_num ) ; // frame_num
if ( ! frame_mbs_only_flag ) {
if ( GetBit ( ) ) // field_pic_flag
newFrame = ! GetBit ( ) ; // bottom_field_flag
if ( debug )
dbgframes ( newFrame ? " t " : " b " ) ;
}
}
// --- cFrameDetector --------------------------------------------------------
2009-11-22 11:30:27 +01:00
2009-01-06 14:41:11 +01:00
cFrameDetector : : cFrameDetector ( int Pid , int Type )
{
2012-11-02 14:35:57 +01:00
parser = NULL ;
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 ;
numIFrames = 0 ;
2010-11-01 12:31:52 +01:00
framesPerSecond = 0 ;
2009-03-27 13:38:59 +01:00
framesInPayloadUnit = framesPerPayloadUnit = 0 ;
2009-01-06 14:41:11 +01:00
scanning = false ;
}
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 ;
2012-11-25 14:21:15 +01:00
isVideo = type = = 0x01 | | type = = 0x02 | | type = = 0x1B ; // MPEG 1, 2 or H.264
2012-11-02 14:35:57 +01:00
delete parser ;
parser = NULL ;
if ( type = = 0x01 | | type = = 0x02 )
parser = new cMpeg2Parser ;
else if ( type = = 0x1B )
2012-11-25 14:21:15 +01:00
parser = new cH264Parser ;
2012-11-02 14:35:57 +01:00
else if ( type = = 0x04 | | type = = 0x06 ) // MPEG audio or AC3 audio
parser = new cAudioParser ;
else if ( type ! = 0 )
esyslog ( " ERROR: unknown stream type %d (PID %d) in frame detector " , type , pid ) ;
2011-09-04 13:09:06 +02:00
}
2009-01-06 14:41:11 +01:00
int cFrameDetector : : Analyze ( const uchar * Data , int Length )
{
2012-11-02 14:35:57 +01:00
if ( ! parser )
return 0 ;
2009-03-27 14:11:43 +01:00
int Processed = 0 ;
2011-09-04 10:13:14 +02:00
newFrame = independentFrame = false ;
2012-11-02 14:35:57 +01:00
while ( Length > = MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE ) { // makes sure we are looking at enough data, in case the frame type is not stored in the first TS packet
// Sync on TS packet borders:
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 ;
}
2012-11-02 14:35:57 +01:00
// Handle one TS packet:
int Handled = TS_SIZE ;
2009-11-22 11:30:27 +01:00
if ( TsHasPayload ( Data ) & & ! TsIsScrambled ( Data ) ) {
int Pid = TsPid ( Data ) ;
if ( Pid = = pid ) {
2012-11-13 10:00:00 +01:00
if ( Processed )
return Processed ;
if ( TsPayloadStart ( Data ) )
2012-11-02 14:35:57 +01:00
scanning = true ;
if ( scanning ) {
// Detect the beginning of a new frame:
if ( TsPayloadStart ( Data ) ) {
if ( ! framesPerPayloadUnit )
framesPerPayloadUnit = framesInPayloadUnit ;
}
int n = parser - > Parse ( Data , Length , pid ) ;
if ( n > 0 ) {
if ( parser - > NewFrame ( ) ) {
newFrame = true ;
independentFrame = parser - > IndependentFrame ( ) ;
if ( synced ) {
if ( framesPerPayloadUnit < = 1 )
scanning = false ;
}
else {
framesInPayloadUnit + + ;
if ( independentFrame )
numIFrames + + ;
}
}
Handled = n ;
}
}
if ( TsPayloadStart ( Data ) ) {
// Determine the frame rate from the PTS values in the PES headers:
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
2012-11-02 14:35:57 +01:00
if ( newFrame ) { // only take PTS values at the beginning of a frame (in case if fields!)
const uchar * Pes = Data + TsPayloadOffset ( Data ) ;
if ( numIFrames & & PesHasPts ( Pes ) ) {
ptsValues [ numPtsValues ] = PesGetPts ( Pes ) ;
// check for rollover:
if ( numPtsValues & & ptsValues [ numPtsValues - 1 ] > 0xF0000000 & & ptsValues [ numPtsValues ] < 0x10000000 ) {
dbgframes ( " # " ) ;
numPtsValues = 0 ;
numIFrames = 0 ;
}
else
numPtsValues + + ;
2009-11-22 11:30:27 +01:00
}
2009-03-27 14:11:43 +01:00
}
2009-03-27 13:38:59 +01:00
}
2012-11-02 14:35:57 +01:00
if ( numPtsValues > = 2 & & numIFrames > = 2 ) {
2009-11-22 11:30:27 +01:00
// 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 ) ;
2012-11-02 14:35:57 +01:00
uint32_t Delta = ptsValues [ 0 ] / framesPerPayloadUnit ;
2009-11-22 11:30:27 +01:00
// 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 ;
2012-11-02 14:35:57 +01:00
else if ( abs ( Delta - 1800 ) < = 1 )
framesPerSecond = 50.0 ;
2010-11-01 12:31:52 +01:00
else if ( Delta = = 1501 )
2012-11-02 14:35:57 +01:00
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
2012-11-18 12:19:51 +01:00
framesPerSecond = double ( PTSTICKS ) / Delta ; // PTS of audio frames is always increasing
2012-11-02 14:35:57 +01:00
dbgframes ( " \n Delta = %d FPS = %5.2f FPPU = %d NF = %d \n " , Delta , framesPerSecond , framesPerPayloadUnit , numPtsValues + 1 ) ;
synced = true ;
parser - > SetDebug ( false ) ;
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
}
2012-11-02 14:35:57 +01:00
Data + = Handled ;
Length - = Handled ;
Processed + = Handled ;
if ( newFrame )
break ;
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
}