2003-12-22 13:29:24 +01:00
/*
* eit . c : EIT section filter
*
* See the main source file ' vdr . c ' for copyright information and
* how to reach the author .
*
* Original version ( as used in VDR before 1.3 .0 ) written by
* Robert Schneider < Robert . Schneider @ web . de > and Rolf Hakenes < hakenes @ hippomi . de > .
* Adapted to ' libsi ' for VDR 1.3 .0 by Marcel Wiesweg < marcel . wiesweg @ gmx . de > .
*
2021-04-04 11:06:30 +02:00
* $ Id : eit . c 5.2 2021 / 04 / 04 11 : 06 : 30 kls Exp $
2003-12-22 13:29:24 +01:00
*/
2000-09-03 11:40:00 +02:00
2021-04-04 11:06:30 +02:00
// The various ways in which broadcasters handle (or screw up) their EPG:
// - Some use the same version for all tables, and use unique event ids, which are the same in
// both the 0x5X and the 0x6X tables. And once an event has an id, it keeps it until it is
// no longer in the tables. Those are the good guys!
// - Some use separate versions for each table (0x50, 0x51, ...).
// - Some broadcast tables 0x5X and 0x6X, but use different event ids for the same event in both
// sets of tables, and sometimes even use different titles, short texts or descriptions.
// - Some broadcast the full EPG only on one transponder (tables 0x6X), and on the actual transponder
// they provide only the present/following information.
// - Some have overlapping events, especially when they mess up daylight saving time.
// - Some use all new event ids every time they update their tables.
// So, to bring order to chaos, VDR does as follows:
// - Completely ignore table 0x4F.
// - Once a schedule has seen events from 0x5X, tables 0x6X are ignored for that schedule.
// - When looking up an event in its schedule, the start time is used for tables 0x6X, and the
// event id for tables 0x4E and 0x5X.
2000-09-03 11:40:00 +02:00
# include "eit.h"
2012-12-04 09:34:06 +01:00
# include <sys/time.h>
2003-12-22 13:29:24 +01:00
# include "epg.h"
2004-01-09 15:53:59 +01:00
# include "i18n.h"
2003-12-22 13:29:24 +01:00
# include "libsi/section.h"
# include "libsi/descriptor.h"
2001-03-31 15:04:37 +02:00
2009-05-15 12:41:50 +02:00
# define VALID_TIME (31536000 * 2) // two years
2019-05-20 09:55:22 +02:00
# define DBGEIT 0
2021-03-16 15:10:54 +01:00
// --- cEitTables ------------------------------------------------------------
2021-04-04 11:06:30 +02:00
cEitTables : : cEitTables ( void )
{
complete = false ;
tableStart = 0 ;
tableEnd = 0 ;
}
2021-03-16 15:10:54 +01:00
bool cEitTables : : Check ( uchar TableId , uchar Version , int SectionNumber )
{
int ti = Index ( TableId ) ;
return sectionSyncer [ ti ] . Check ( Version , SectionNumber ) ;
}
bool cEitTables : : Processed ( uchar TableId , uchar LastTableId , int SectionNumber , int LastSectionNumber , int SegmentLastSectionNumber )
{
2021-04-04 11:06:30 +02:00
bool Result = false ;
2021-03-16 15:10:54 +01:00
int ti = Index ( TableId ) ;
int LastIndex = Index ( LastTableId ) ;
2021-04-04 11:06:30 +02:00
complete = false ;
2021-03-16 15:10:54 +01:00
if ( sectionSyncer [ ti ] . Processed ( SectionNumber , LastSectionNumber , SegmentLastSectionNumber ) ) {
2021-04-04 11:06:30 +02:00
Result = true ; // the table with TableId is complete
2021-03-16 15:10:54 +01:00
for ( int i = 0 ; i < = LastIndex ; i + + ) {
if ( ! sectionSyncer [ i ] . Complete ( ) )
2021-04-04 11:06:30 +02:00
return Result ;
2021-03-16 15:10:54 +01:00
}
2021-04-04 11:06:30 +02:00
complete = true ; // all tables have been processed
2021-03-16 15:10:54 +01:00
}
2021-04-04 11:06:30 +02:00
return Result ;
2021-03-16 15:10:54 +01:00
}
2003-12-22 13:29:24 +01:00
// --- cEIT ------------------------------------------------------------------
2000-09-03 11:40:00 +02:00
2003-12-22 13:29:24 +01:00
class cEIT : public SI : : EIT {
2001-03-31 15:04:37 +02:00
public :
2021-04-04 11:06:30 +02:00
cEIT ( cEitTablesHash & EitTablesHash , int Source , u_char Tid , const u_char * Data ) ;
2003-12-22 13:29:24 +01:00
} ;
2001-03-31 15:04:37 +02:00
2021-04-04 11:06:30 +02:00
cEIT : : cEIT ( cEitTablesHash & EitTablesHash , int Source , u_char Tid , const u_char * Data )
2003-12-22 13:29:24 +01:00
: SI : : EIT ( Data , false )
2000-10-29 13:17:22 +01:00
{
2003-12-22 13:29:24 +01:00
if ( ! CheckCRCAndParse ( ) )
return ;
2021-03-16 15:10:54 +01:00
int HashId = getServiceId ( ) ;
2021-04-04 11:06:30 +02:00
cEitTables * EitTables = EitTablesHash . Get ( HashId ) ;
2021-03-16 15:10:54 +01:00
if ( ! EitTables ) {
EitTables = new cEitTables ;
2021-04-04 11:06:30 +02:00
EitTablesHash . Add ( EitTables , HashId ) ;
2015-09-01 11:14:27 +02:00
}
2021-03-16 15:10:54 +01:00
bool Process = EitTables - > Check ( Tid , getVersionNumber ( ) , getSectionNumber ( ) ) ;
2015-09-01 11:14:27 +02:00
if ( Tid ! = 0x4E & & ! Process ) // we need to set the 'seen' tag to watch the running status of the present/following event
return ;
2000-10-29 13:17:22 +01:00
2012-03-14 10:41:48 +01:00
time_t Now = time ( NULL ) ;
if ( Now < VALID_TIME )
return ; // we need the current time for handling PDC descriptors
2015-09-01 11:14:27 +02:00
cStateKey ChannelsStateKey ;
cChannels * Channels = cChannels : : GetChannelsWrite ( ChannelsStateKey , 10 ) ;
2021-03-16 15:10:54 +01:00
if ( ! Channels )
2012-03-14 10:41:48 +01:00
return ;
2004-01-04 12:30:00 +01:00
tChannelID channelID ( Source , getOriginalNetworkId ( ) , getTransportStreamId ( ) , getServiceId ( ) ) ;
2015-09-01 11:14:27 +02:00
cChannel * Channel = Channels - > GetByChannelID ( channelID , true ) ;
if ( ! Channel | | EpgHandlers . IgnoreChannel ( Channel ) ) {
ChannelsStateKey . Remove ( false ) ;
2012-03-10 15:10:43 +01:00
return ;
2012-03-14 10:41:48 +01:00
}
2000-10-29 13:17:22 +01:00
2015-09-01 11:14:27 +02:00
cStateKey SchedulesStateKey ;
cSchedules * Schedules = cSchedules : : GetSchedulesWrite ( SchedulesStateKey , 10 ) ;
if ( ! Schedules ) {
ChannelsStateKey . Remove ( false ) ;
return ;
}
2017-03-31 15:24:35 +02:00
if ( ! EpgHandlers . BeginSegmentTransfer ( Channel ) ) {
SchedulesStateKey . Remove ( false ) ;
ChannelsStateKey . Remove ( false ) ;
return ;
}
2015-09-01 11:14:27 +02:00
bool ChannelsModified = false ;
bool handledExternally = EpgHandlers . HandledExternally ( Channel ) ;
cSchedule * pSchedule = ( cSchedule * ) Schedules - > GetSchedule ( Channel , true ) ;
2002-02-02 12:13:35 +01:00
2021-04-04 11:06:30 +02:00
if ( pSchedule - > OnActualTp ( Tid ) & & ( Tid & 0xF0 ) = = 0x60 ) {
SchedulesStateKey . Remove ( false ) ;
ChannelsStateKey . Remove ( false ) ;
return ;
}
2004-03-13 15:01:05 +01:00
bool Empty = true ;
2004-02-21 13:56:20 +01:00
bool Modified = false ;
2017-05-03 09:02:24 +02:00
time_t LingerLimit = Now - Setup . EPGLinger * 60 ;
2021-04-04 11:06:30 +02:00
time_t SegmentStart = 0 ; // these are actually "section" start/end times
2005-12-26 14:44:28 +01:00
time_t SegmentEnd = 0 ;
2013-11-03 14:02:01 +01:00
struct tm t = { 0 } ;
localtime_r ( & Now , & t ) ; // this initializes the time zone in 't'
2004-02-21 13:56:20 +01:00
2003-12-22 13:29:24 +01:00
SI : : EIT : : Event SiEitEvent ;
2004-10-16 10:14:19 +02:00
for ( SI : : Loop : : Iterator it ; eventLoop . getNext ( SiEitEvent , it ) ; ) {
2012-03-10 15:10:43 +01:00
if ( EpgHandlers . HandleEitEvent ( pSchedule , & SiEitEvent , Tid , getVersionNumber ( ) ) )
continue ; // an EPG handler has done all of the processing
2009-05-10 14:55:15 +02:00
time_t StartTime = SiEitEvent . getStartTime ( ) ;
2009-04-12 09:09:47 +02:00
int Duration = SiEitEvent . getDuration ( ) ;
2005-06-11 16:05:11 +02:00
// Drop bogus events - but keep NVOD reference events, where all bits of the start time field are set to 1, resulting in a negative number.
2009-04-12 09:09:47 +02:00
if ( StartTime = = 0 | | StartTime > 0 & & Duration = = 0 )
2005-05-26 10:27:06 +02:00
continue ;
2017-10-11 09:22:24 +02:00
Empty = false ;
2017-05-03 09:02:24 +02:00
// Ignore events that ended before the "EPG linger time":
if ( StartTime + Duration < LingerLimit )
continue ;
2005-12-26 14:44:28 +01:00
if ( ! SegmentStart )
2009-04-12 09:09:47 +02:00
SegmentStart = StartTime ;
SegmentEnd = StartTime + Duration ;
2021-04-04 11:06:30 +02:00
if ( Tid = = 0x4E ) {
if ( getSectionNumber ( ) = = 0 )
EitTables - > SetTableStart ( SegmentStart ) ;
else
EitTables - > SetTableEnd ( SegmentEnd ) ;
}
2005-05-28 13:17:20 +02:00
cEvent * newEvent = NULL ;
2005-08-13 13:30:04 +02:00
cEvent * rEvent = NULL ;
2021-04-04 11:06:30 +02:00
cEvent * pEvent = NULL ;
if ( Tid = = 0x4E | | ( Tid & 0xF0 ) = = 0x50 )
pEvent = const_cast < cEvent * > ( pSchedule - > GetEventById ( SiEitEvent . getEventId ( ) ) ) ;
else
pEvent = const_cast < cEvent * > ( pSchedule - > GetEventByTime ( StartTime ) ) ;
2012-06-04 10:19:23 +02:00
if ( ! pEvent | | handledExternally ) {
2012-08-25 11:31:35 +02:00
if ( handledExternally & & ! EpgHandlers . IsUpdate ( SiEitEvent . getEventId ( ) , StartTime , Tid , getVersionNumber ( ) ) )
continue ;
2004-02-21 12:28:17 +01:00
// If we don't have that event yet, we create a new one.
2003-12-22 13:29:24 +01:00
// Otherwise we copy the information into the existing event anyway, because the data might have changed.
2005-05-28 13:17:20 +02:00
pEvent = newEvent = new cEvent ( SiEitEvent . getEventId ( ) ) ;
2012-03-10 15:10:43 +01:00
newEvent - > SetStartTime ( StartTime ) ;
newEvent - > SetDuration ( Duration ) ;
2012-06-04 10:19:23 +02:00
if ( ! handledExternally )
pSchedule - > AddEvent ( newEvent ) ;
2002-02-02 12:13:35 +01:00
}
2003-12-22 13:29:24 +01:00
else {
// We have found an existing event, either through its event ID or its start time.
2004-10-24 15:01:50 +02:00
pEvent - > SetSeen ( ) ;
2012-03-10 15:10:43 +01:00
uchar TableID = max ( pEvent - > TableID ( ) , uchar ( 0x4E ) ) ; // for backwards compatibility, table ids less than 0x4E are treated as if they were "present"
2021-04-04 11:06:30 +02:00
// We never overwrite present/following with events from other tables:
if ( TableID = = 0x4E & & Tid ! = 0x4E )
2003-12-25 12:50:22 +01:00
continue ;
2021-04-04 11:06:30 +02:00
if ( pEvent - > HasTimer ( ) ) {
if ( pEvent - > StartTime ( ) ! = StartTime | | pEvent - > Duration ( ) ! = Duration )
dsyslog ( " channel %d (%s) event %s times changed to %s-%s " , Channel - > Number ( ) , Channel - > Name ( ) , * pEvent - > ToDescr ( ) , * TimeString ( StartTime ) , * TimeString ( StartTime + Duration ) ) ;
}
2012-03-10 15:10:43 +01:00
EpgHandlers . SetEventID ( pEvent , SiEitEvent . getEventId ( ) ) ; // unfortunately some stations use different event ids for the same event in different tables :-(
EpgHandlers . SetStartTime ( pEvent , StartTime ) ;
EpgHandlers . SetDuration ( pEvent , Duration ) ;
2002-02-02 12:13:35 +01:00
}
2012-03-10 15:10:43 +01:00
if ( pEvent - > TableID ( ) > 0x4E ) // for backwards compatibility, table ids less than 0x4E are never overwritten
2006-02-19 09:51:02 +01:00
pEvent - > SetTableID ( Tid ) ;
2006-05-25 14:53:15 +02:00
if ( Tid = = 0x4E ) { // we trust only the present/following info on the actual TS
2019-05-20 09:55:22 +02:00
int RunningStatus = SiEitEvent . getRunningStatus ( ) ;
# if DBGEIT
if ( Process )
dsyslog ( " channel %d (%s) event %s status %d (raw data from '%s' section) " , Channel - > Number ( ) , Channel - > Name ( ) , * pEvent - > ToDescr ( ) , RunningStatus , getSectionNumber ( ) ? " following " : " present " ) ;
# endif
if ( RunningStatus > = SI : : RunningStatusNotRunning ) {
// Workaround for broadcasters who set an event to status "not running" where
// this is inappropriate:
if ( RunningStatus ! = pEvent - > RunningStatus ( ) ) { // if the running status of the event has changed...
if ( RunningStatus = = SI : : RunningStatusNotRunning ) { // ...and the new status is "not running"...
int OverrideStatus = - 1 ;
if ( getSectionNumber ( ) = = 0 ) { // ...and if this the "present" event...
if ( pEvent - > RunningStatus ( ) = = SI : : RunningStatusPausing ) // ...and if the event has already been set to "pausing"...
OverrideStatus = SI : : RunningStatusPausing ; // ...then we ignore the faulty new status and stay with "pausing"
}
else // ...and if this is the "following" event...
OverrideStatus = SI : : RunningStatusUndefined ; // ...then we ignore the faulty new status and fall back to "undefined"
if ( OverrideStatus > = 0 ) {
# if DBGEIT
if ( Process )
dsyslog ( " channel %d (%s) event %s status %d (ignored status %d from '%s' section) " , Channel - > Number ( ) , Channel - > Name ( ) , * pEvent - > ToDescr ( ) , OverrideStatus , RunningStatus , getSectionNumber ( ) ? " following " : " present " ) ;
# endif
RunningStatus = OverrideStatus ;
}
}
}
pSchedule - > SetRunningStatus ( pEvent , RunningStatus , Channel ) ;
}
2015-09-01 11:14:27 +02:00
if ( ! Process )
continue ;
2012-03-10 15:10:43 +01:00
}
2004-02-21 12:28:17 +01:00
pEvent - > SetVersion ( getVersionNumber ( ) ) ;
2001-08-17 13:19:10 +02:00
2004-01-09 15:53:59 +01:00
int LanguagePreferenceShort = - 1 ;
int LanguagePreferenceExt = - 1 ;
bool UseExtendedEventDescriptor = false ;
2003-12-22 13:29:24 +01:00
SI : : Descriptor * d ;
2004-01-09 15:53:59 +01:00
SI : : ExtendedEventDescriptors * ExtendedEventDescriptors = NULL ;
SI : : ShortEventDescriptor * ShortEventDescriptor = NULL ;
2004-02-08 11:05:22 +01:00
cLinkChannels * LinkChannels = NULL ;
2005-05-16 14:45:11 +02:00
cComponents * Components = NULL ;
2003-12-22 13:29:24 +01:00
for ( SI : : Loop : : Iterator it2 ; ( d = SiEitEvent . eventDescriptors . getNext ( it2 ) ) ; ) {
switch ( d - > getDescriptorTag ( ) ) {
2004-01-09 15:53:59 +01:00
case SI : : ExtendedEventDescriptorTag : {
SI : : ExtendedEventDescriptor * eed = ( SI : : ExtendedEventDescriptor * ) d ;
2005-09-04 14:48:39 +02:00
if ( I18nIsPreferredLanguage ( Setup . EPGLanguages , eed - > languageCode , LanguagePreferenceExt ) | | ! ExtendedEventDescriptors ) {
2004-01-09 15:53:59 +01:00
delete ExtendedEventDescriptors ;
ExtendedEventDescriptors = new SI : : ExtendedEventDescriptors ;
UseExtendedEventDescriptor = true ;
}
if ( UseExtendedEventDescriptor ) {
2015-02-01 14:59:52 +01:00
if ( ExtendedEventDescriptors - > Add ( eed ) )
d = NULL ; // so that it is not deleted
2004-01-09 15:53:59 +01:00
}
if ( eed - > getDescriptorNumber ( ) = = eed - > getLastDescriptorNumber ( ) )
UseExtendedEventDescriptor = false ;
}
2003-12-22 13:29:24 +01:00
break ;
case SI : : ShortEventDescriptorTag : {
SI : : ShortEventDescriptor * sed = ( SI : : ShortEventDescriptor * ) d ;
2005-09-04 14:48:39 +02:00
if ( I18nIsPreferredLanguage ( Setup . EPGLanguages , sed - > languageCode , LanguagePreferenceShort ) | | ! ShortEventDescriptor ) {
2004-01-09 15:53:59 +01:00
delete ShortEventDescriptor ;
ShortEventDescriptor = sed ;
d = NULL ; // so that it is not deleted
}
2002-09-15 14:35:32 +02:00
}
2003-12-22 13:29:24 +01:00
break ;
2010-01-03 12:20:37 +01:00
case SI : : ContentDescriptorTag : {
SI : : ContentDescriptor * cd = ( SI : : ContentDescriptor * ) d ;
SI : : ContentDescriptor : : Nibble Nibble ;
int NumContents = 0 ;
2010-01-08 15:23:34 +01:00
uchar Contents [ MaxEventContents ] = { 0 } ;
2010-01-03 12:20:37 +01:00
for ( SI : : Loop : : Iterator it3 ; cd - > nibbleLoop . getNext ( Nibble , it3 ) ; ) {
2010-01-08 15:23:34 +01:00
if ( NumContents < MaxEventContents ) {
2010-01-03 12:20:37 +01:00
Contents [ NumContents ] = ( ( Nibble . getContentNibbleLevel1 ( ) & 0xF ) < < 4 ) | ( Nibble . getContentNibbleLevel2 ( ) & 0xF ) ;
NumContents + + ;
}
}
2012-03-10 15:10:43 +01:00
EpgHandlers . SetContents ( pEvent , Contents ) ;
2010-01-03 12:20:37 +01:00
}
2003-12-22 13:29:24 +01:00
break ;
2010-01-03 14:28:33 +01:00
case SI : : ParentalRatingDescriptorTag : {
int LanguagePreferenceRating = - 1 ;
SI : : ParentalRatingDescriptor * prd = ( SI : : ParentalRatingDescriptor * ) d ;
SI : : ParentalRatingDescriptor : : Rating Rating ;
for ( SI : : Loop : : Iterator it3 ; prd - > ratingLoop . getNext ( Rating , it3 ) ; ) {
if ( I18nIsPreferredLanguage ( Setup . EPGLanguages , Rating . languageCode , LanguagePreferenceRating ) ) {
int ParentalRating = ( Rating . getRating ( ) & 0xFF ) ;
switch ( ParentalRating ) {
// values defined by the DVB standard (minimum age = rating + 3 years):
case 0x01 . . . 0x0F : ParentalRating + = 3 ; break ;
// values defined by broadcaster CSAT (now why didn't they just use 0x07, 0x09 and 0x0D?):
case 0x11 : ParentalRating = 10 ; break ;
case 0x12 : ParentalRating = 12 ; break ;
case 0x13 : ParentalRating = 16 ; break ;
default : ParentalRating = 0 ;
}
2012-03-10 15:10:43 +01:00
EpgHandlers . SetParentalRating ( pEvent , ParentalRating ) ;
2010-01-03 14:28:33 +01:00
}
}
}
2003-12-22 13:29:24 +01:00
break ;
2004-02-22 13:33:20 +01:00
case SI : : PDCDescriptorTag : {
SI : : PDCDescriptor * pd = ( SI : : PDCDescriptor * ) d ;
t . tm_isdst = - 1 ; // makes sure mktime() will determine the correct DST setting
2004-03-07 11:53:43 +01:00
int month = t . tm_mon ;
2004-02-22 13:33:20 +01:00
t . tm_mon = pd - > getMonth ( ) - 1 ;
t . tm_mday = pd - > getDay ( ) ;
t . tm_hour = pd - > getHour ( ) ;
t . tm_min = pd - > getMinute ( ) ;
t . tm_sec = 0 ;
2004-03-07 11:53:43 +01:00
if ( month = = 11 & & t . tm_mon = = 0 ) // current month is dec, but event is in jan
t . tm_year + + ;
else if ( month = = 0 & & t . tm_mon = = 11 ) // current month is jan, but event is in dec
t . tm_year - - ;
2004-02-22 13:33:20 +01:00
time_t vps = mktime ( & t ) ;
2012-03-10 15:10:43 +01:00
EpgHandlers . SetVps ( pEvent , vps ) ;
2004-02-22 13:33:20 +01:00
}
break ;
2003-12-22 13:29:24 +01:00
case SI : : TimeShiftedEventDescriptorTag : {
SI : : TimeShiftedEventDescriptor * tsed = ( SI : : TimeShiftedEventDescriptor * ) d ;
2015-09-01 11:14:27 +02:00
cSchedule * rSchedule = ( cSchedule * ) Schedules - > GetSchedule ( tChannelID ( Source , Channel - > Nid ( ) , Channel - > Tid ( ) , tsed - > getReferenceServiceId ( ) ) ) ;
2003-12-22 13:29:24 +01:00
if ( ! rSchedule )
break ;
2021-04-04 11:06:30 +02:00
rEvent = ( cEvent * ) rSchedule - > GetEventById ( tsed - > getReferenceEventId ( ) ) ;
2003-12-22 13:29:24 +01:00
if ( ! rEvent )
break ;
2012-03-10 15:10:43 +01:00
EpgHandlers . SetTitle ( pEvent , rEvent - > Title ( ) ) ;
EpgHandlers . SetShortText ( pEvent , rEvent - > ShortText ( ) ) ;
EpgHandlers . SetDescription ( pEvent , rEvent - > Description ( ) ) ;
2001-08-17 13:19:10 +02:00
}
2003-12-22 13:29:24 +01:00
break ;
2004-02-08 11:05:22 +01:00
case SI : : LinkageDescriptorTag : {
SI : : LinkageDescriptor * ld = ( SI : : LinkageDescriptor * ) d ;
tChannelID linkID ( Source , ld - > getOriginalNetworkId ( ) , ld - > getTransportStreamId ( ) , ld - > getServiceId ( ) ) ;
2014-02-08 14:21:47 +01:00
if ( ld - > getLinkageType ( ) = = SI : : LinkageTypePremiere ) { // Premiere World
2009-04-12 09:09:47 +02:00
bool hit = StartTime < = Now & & Now < StartTime + Duration ;
2004-02-08 11:05:22 +01:00
if ( hit ) {
2004-10-31 13:01:35 +01:00
char linkName [ ld - > privateData . getLength ( ) + 1 ] ;
strn0cpy ( linkName , ( const char * ) ld - > privateData . getData ( ) , sizeof ( linkName ) ) ;
2007-06-10 13:02:43 +02:00
// TODO is there a standard way to determine the character set of this string?
2015-09-01 11:14:27 +02:00
cChannel * link = Channels - > GetByChannelID ( linkID ) ;
if ( link ! = Channel ) { // only link to other channels, not the same one
2004-02-08 11:05:22 +01:00
if ( link ) {
2006-04-15 14:18:25 +02:00
if ( Setup . UpdateChannels = = 1 | | Setup . UpdateChannels > = 3 )
2015-09-01 11:14:27 +02:00
ChannelsModified | = link - > SetName ( linkName , " " , " " ) ;
2004-02-08 11:05:22 +01:00
}
2006-04-15 14:18:25 +02:00
else if ( Setup . UpdateChannels > = 4 ) {
2015-09-01 11:14:27 +02:00
cChannel * Transponder = Channel ;
if ( Channel - > Tid ( ) ! = ld - > getTransportStreamId ( ) )
Transponder = Channels - > GetByTransponderID ( linkID ) ;
link = Channels - > NewChannel ( Transponder , linkName , " " , " " , ld - > getOriginalNetworkId ( ) , ld - > getTransportStreamId ( ) , ld - > getServiceId ( ) ) ;
ChannelsModified = true ;
2004-02-08 11:05:22 +01:00
//XXX patFilter->Trigger();
}
if ( link ) {
if ( ! LinkChannels )
LinkChannels = new cLinkChannels ;
LinkChannels - > Add ( new cLinkChannel ( link ) ) ;
}
}
2004-10-31 13:01:35 +01:00
else
2015-09-01 11:14:27 +02:00
ChannelsModified | = Channel - > SetPortalName ( linkName ) ;
2004-02-08 11:05:22 +01:00
}
}
}
break ;
2005-01-02 15:11:44 +01:00
case SI : : ComponentDescriptorTag : {
SI : : ComponentDescriptor * cd = ( SI : : ComponentDescriptor * ) d ;
uchar Stream = cd - > getStreamContent ( ) ;
2020-06-23 09:27:09 +02:00
uchar Ext = cd - > getStreamContentExt ( ) ;
2005-01-02 15:11:44 +01:00
uchar Type = cd - > getComponentType ( ) ;
2020-06-23 09:27:09 +02:00
if ( ( 1 < = Stream & & Stream < = 6 & & Type ! = 0 ) // 1=MPEG2-video, 2=MPEG1-audio, 3=subtitles, 4=AC3-audio, 5=H.264-video, 6=HEAAC-audio
| | ( Stream = = 9 & & Ext < 2 ) ) { // 0x09=HEVC-video, 0x19=AC-4-audio
2005-05-16 14:45:11 +02:00
if ( ! Components )
Components = new cComponents ;
2007-06-10 13:02:43 +02:00
char buffer [ Utf8BufSize ( 256 ) ] ;
2020-06-23 09:27:09 +02:00
if ( Stream = = 9 )
Stream | = Ext < < 4 ;
2007-10-12 14:52:30 +02:00
Components - > SetComponent ( Components - > NumComponents ( ) , Stream , Type , I18nNormalizeLanguageCode ( cd - > languageCode ) , cd - > description . getText ( buffer , sizeof ( buffer ) ) ) ;
2005-01-02 15:11:44 +01:00
}
}
break ;
2003-12-22 13:29:24 +01:00
default : ;
}
delete d ;
}
2002-10-07 16:24:04 +02:00
2003-12-22 13:29:24 +01:00
if ( ! rEvent ) {
2004-01-09 15:53:59 +01:00
if ( ShortEventDescriptor ) {
2007-06-10 13:02:43 +02:00
char buffer [ Utf8BufSize ( 256 ) ] ;
2012-03-10 15:10:43 +01:00
EpgHandlers . SetTitle ( pEvent , ShortEventDescriptor - > name . getText ( buffer , sizeof ( buffer ) ) ) ;
EpgHandlers . SetShortText ( pEvent , ShortEventDescriptor - > text . getText ( buffer , sizeof ( buffer ) ) ) ;
2004-01-09 15:53:59 +01:00
}
2012-03-10 15:10:43 +01:00
else {
EpgHandlers . SetTitle ( pEvent , NULL ) ;
EpgHandlers . SetShortText ( pEvent , NULL ) ;
2006-10-07 12:37:59 +02:00
}
2004-01-09 15:53:59 +01:00
if ( ExtendedEventDescriptors ) {
2007-06-10 13:02:43 +02:00
char buffer [ Utf8BufSize ( ExtendedEventDescriptors - > getMaximumTextLength ( " : " ) ) + 1 ] ;
2012-03-10 15:10:43 +01:00
EpgHandlers . SetDescription ( pEvent , ExtendedEventDescriptors - > getText ( buffer , sizeof ( buffer ) , " : " ) ) ;
2004-01-09 15:53:59 +01:00
}
2012-03-10 15:10:43 +01:00
else
EpgHandlers . SetDescription ( pEvent , NULL ) ;
2000-11-01 15:53:00 +01:00
}
2004-01-09 15:53:59 +01:00
delete ExtendedEventDescriptors ;
delete ShortEventDescriptor ;
2002-10-07 16:24:04 +02:00
2012-06-04 10:29:19 +02:00
EpgHandlers . SetComponents ( pEvent , Components ) ;
2001-03-31 15:04:37 +02:00
2012-03-10 15:10:43 +01:00
EpgHandlers . FixEpgBugs ( pEvent ) ;
2004-02-08 11:05:22 +01:00
if ( LinkChannels )
2015-09-01 11:14:27 +02:00
ChannelsModified | = Channel - > SetLinkChannels ( LinkChannels ) ;
2004-02-21 13:56:20 +01:00
Modified = true ;
2012-03-10 15:10:43 +01:00
EpgHandlers . HandleEvent ( pEvent ) ;
2012-06-04 10:19:23 +02:00
if ( handledExternally )
delete pEvent ;
2002-10-07 16:24:04 +02:00
}
2009-04-12 09:09:47 +02:00
if ( Tid = = 0x4E ) {
if ( Empty & & getSectionNumber ( ) = = 0 )
// ETR 211: an empty entry in section 0 of table 0x4E means there is currently no event running
2015-09-01 11:14:27 +02:00
pSchedule - > ClrRunningStatus ( Channel ) ;
2005-03-20 13:12:07 +01:00
pSchedule - > SetPresentSeen ( ) ;
2009-04-12 09:09:47 +02:00
}
2021-04-04 11:06:30 +02:00
if ( Process ) {
bool Complete = EitTables - > Processed ( Tid , getLastTableId ( ) , getSectionNumber ( ) , getLastSectionNumber ( ) , getSegmentLastSectionNumber ( ) ) ;
if ( Modified & & ( Tid > = 0x50 | | Complete ) ) { // we process the 0x5X tables segment by segment, but 0x4E only if we have received ALL its segments (0 and 1, i.e. "present" and "following")
if ( Tid = = 0x4E & & getLastSectionNumber ( ) = = 1 ) {
SegmentStart = EitTables - > TableStart ( ) ;
SegmentEnd = EitTables - > TableEnd ( ) ;
}
EpgHandlers . SortSchedule ( pSchedule ) ;
EpgHandlers . DropOutdated ( pSchedule , SegmentStart , SegmentEnd , Tid , getVersionNumber ( ) ) ;
}
2004-10-24 15:01:50 +02:00
}
2015-09-01 11:14:27 +02:00
SchedulesStateKey . Remove ( Modified ) ;
ChannelsStateKey . Remove ( ChannelsModified ) ;
EpgHandlers . EndSegmentTransfer ( Modified ) ;
2000-09-03 11:40:00 +02:00
}
2003-12-22 13:29:24 +01:00
// --- cTDT ------------------------------------------------------------------
2003-01-06 14:44:27 +01:00
2012-12-04 09:34:06 +01:00
# define MAX_TIME_DIFF 1 // number of seconds the local time may differ from dvb time before making any corrections
# define MAX_ADJ_DIFF 10 // number of seconds the local time may differ from dvb time to allow smooth adjustment
# define ADJ_DELTA 300 // number of seconds between calls for smooth time adjustment
2003-12-22 13:29:24 +01:00
class cTDT : public SI : : TDT {
2003-01-06 14:44:27 +01:00
private :
2003-12-22 13:29:24 +01:00
static cMutex mutex ;
2012-12-04 09:34:06 +01:00
static time_t lastAdj ;
2003-01-06 14:44:27 +01:00
public :
2003-12-22 13:29:24 +01:00
cTDT ( const u_char * Data ) ;
2003-01-06 14:44:27 +01:00
} ;
2003-12-22 13:29:24 +01:00
cMutex cTDT : : mutex ;
2012-12-04 09:34:06 +01:00
time_t cTDT : : lastAdj = 0 ;
2003-01-06 14:44:27 +01:00
2003-12-22 13:29:24 +01:00
cTDT : : cTDT ( const u_char * Data )
: SI : : TDT ( Data , false )
2003-01-06 14:44:27 +01:00
{
2003-12-22 13:29:24 +01:00
CheckParse ( ) ;
2000-10-29 13:17:22 +01:00
2012-12-04 09:34:06 +01:00
time_t dvbtim = getTime ( ) ;
2003-12-22 13:29:24 +01:00
time_t loctim = time ( NULL ) ;
2000-10-29 13:17:22 +01:00
2012-12-04 09:34:06 +01:00
int diff = dvbtim - loctim ;
if ( abs ( diff ) > MAX_TIME_DIFF ) {
2003-12-22 13:29:24 +01:00
mutex . Lock ( ) ;
2012-12-04 09:34:06 +01:00
if ( abs ( diff ) > MAX_ADJ_DIFF ) {
2020-05-04 13:02:14 +02:00
timespec ts = { } ;
2020-05-04 12:28:31 +02:00
ts . tv_sec = dvbtim ;
if ( clock_settime ( CLOCK_REALTIME , & ts ) = = 0 )
2012-12-04 09:34:06 +01:00
isyslog ( " system time changed from %s (%ld) to %s (%ld) " , * TimeToString ( loctim ) , loctim , * TimeToString ( dvbtim ) , dvbtim ) ;
2008-04-13 11:44:48 +02:00
else
2005-08-07 13:52:29 +02:00
esyslog ( " ERROR while setting system time: %m " ) ;
}
2012-12-04 09:34:06 +01:00
else if ( time ( NULL ) - lastAdj > ADJ_DELTA ) {
lastAdj = time ( NULL ) ;
timeval delta ;
delta . tv_sec = diff ;
delta . tv_usec = 0 ;
if ( adjtime ( & delta , NULL ) = = 0 )
isyslog ( " system time adjustment initiated from %s (%ld) to %s (%ld) " , * TimeToString ( loctim ) , loctim , * TimeToString ( dvbtim ) , dvbtim ) ;
else
esyslog ( " ERROR while adjusting system time: %m " ) ;
}
2003-12-22 13:29:24 +01:00
mutex . Unlock ( ) ;
2002-02-23 17:11:19 +01:00
}
}
2003-12-22 13:29:24 +01:00
// --- cEitFilter ------------------------------------------------------------
2002-08-25 10:49:02 +02:00
2010-01-03 15:45:23 +01:00
time_t cEitFilter : : disableUntil = 0 ;
2003-12-22 13:29:24 +01:00
cEitFilter : : cEitFilter ( void )
2001-08-11 09:38:12 +02:00
{
2021-04-04 11:06:30 +02:00
Set ( 0x12 , 0x40 , 0xC0 ) ; // event info present&following actual/other TS (0x4E/0x4F), future actual/other TS (0x5X/0x6X)
2012-12-04 11:15:28 +01:00
Set ( 0x14 , 0x70 ) ; // TDT
2001-08-11 09:38:12 +02:00
}
2015-09-01 11:14:27 +02:00
void cEitFilter : : SetStatus ( bool On )
{
cMutexLock MutexLock ( & mutex ) ;
cFilter : : SetStatus ( On ) ;
2021-04-04 11:06:30 +02:00
eitTablesHash . Clear ( ) ;
2015-09-01 11:14:27 +02:00
}
2010-01-03 15:45:23 +01:00
void cEitFilter : : SetDisableUntil ( time_t Time )
{
disableUntil = Time ;
}
2003-12-22 13:29:24 +01:00
void cEitFilter : : Process ( u_short Pid , u_char Tid , const u_char * Data , int Length )
2001-08-11 09:38:12 +02:00
{
2015-09-01 11:14:27 +02:00
cMutexLock MutexLock ( & mutex ) ;
2010-01-03 15:45:23 +01:00
if ( disableUntil ) {
if ( time ( NULL ) > disableUntil )
disableUntil = 0 ;
else
return ;
}
2003-12-22 13:29:24 +01:00
switch ( Pid ) {
case 0x12 : {
2021-04-04 11:06:30 +02:00
if ( Tid = = 0x4E | | Tid > = 0x50 & & Tid < = 0x6F ) // we ignore 0x4F, which only causes trouble
cEIT EIT ( eitTablesHash , Source ( ) , Tid , Data ) ;
2000-11-26 15:23:39 +01:00
}
2003-12-22 13:29:24 +01:00
break ;
case 0x14 : {
2013-10-12 11:23:10 +02:00
if ( Setup . SetSystemTime & & Setup . TimeSource = = Source ( ) & & Setup . TimeTransponder & & ISTRANSPONDER ( Transponder ( ) , Setup . TimeTransponder ) )
2003-12-22 13:29:24 +01:00
cTDT TDT ( Data ) ;
2003-04-25 14:46:22 +02:00
}
2003-12-22 13:29:24 +01:00
break ;
2009-12-06 12:57:45 +01:00
default : ;
2003-12-22 13:29:24 +01:00
}
2003-01-06 14:44:27 +01:00
}