mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
VDR developer version 2.1.3 is now available at ftp://ftp.tvdr.de/vdr/Developer/vdr-2.1.3.tar.bz2 A 'diff' against the previous version is available at ftp://ftp.tvdr.de/vdr/Developer/vdr-2.1.2-2.1.3.diff MD5 checksums: 054f80e0045aa6fad118e9285b52f4f2 vdr-2.1.3.tar.bz2 3c5ab05d5c4d0b984b34e84190e80949 vdr-2.1.2-2.1.3.diff WARNING: ======== This is a *developer* version. Even though *I* use it in my productive environment, I strongly recommend that you only use it under controlled conditions and for testing and debugging. Originally I intended to release this version only after the new DiSEqC configuration dialog was finished. But in the meantime quite a few other things have come up, so I decided to postpone that dialog and first release what has piled up so far. From the HISTORY file: - Changed the return value of cPositioner::HorizonLongitude() to 0 in case the latitude of the antenna location is beyond +/-81 degrees. - Updated the Finnish OSD texts (thanks to Rolf Ahrenberg). - Fixed some compiler warnings with gcc-4.6.3 (thanks to Rolf Ahrenberg). - Changed the name of the SVDRP command RENR to MOVR (suggested by Rolf Ahrenberg). - When cutting a recording it is now checked whether there is already an edited version of this recording (with the same name, but starting with '%'), and the user is prompted for confirmation to overwrite it (suggested by Rolf Ahrenberg). - Revoked "Added maximum signal strength value for TechniSat SkyStar 2 DVB-S rev 2.3P" because it broke things for the "TechniSat AirStar 2" DVB-T card. - The LIRC remote control now connects to the socket even if it doesn't yet exist when VDR is started (thanks to Lars Hanisch). - Changed the absolute latitude limit for visible satellites to 81.2 degrees. - Added code for parsing LCN and AVC descriptors to libsi (thanks to Rolf Ahrenberg). - In the "Select folder" menu pressing Ok now selects the folder, even if this is a folder that contains sub folders (marked with "..."). To open such a folder you can press the Red key. - Fixed a possible access to uninitialized data in cEIT::cEIT() (reported by Dominik Strasser). - The new menu category mcRecordingEdit is now used to mark menus that edit recording properties (suggested by Stefan Braun). - Changes in the teletext PID no longer cause retuning (and thus interrupting a recording). - Removed '_' from the FileNameChars and CharMap translations in uk_UA.po. - Updated the Italian OSD texts (thanks to Diego Pierotto). - Fixed a missing initialization in the c'tor of cSkinLCARSDisplayChannel (thanks to Marko Mäkelä). - Simplified some conditional expressions in skinlcars.c and skinsttng.c (suggested by Marko Mäkelä). - Fixed uninitialized item area coordinates in cSkinLCARSDisplayMenu (reported by Marko Mäkelä). - Fixed a possible crash if the recordings list is updated externally while the Recordings menu is open (reported by Lars Hanisch). - Added a missing closing ')' in the help and man page entry of the --vfat option (reported by Lars Hanisch). - Fixed setting the name of the video directory to avoid a crash when using --genindex, and also to use the correct directory with --edit (the latter reported by Marko Mäkelä). - The Recordings menu can now be called with a cRecordingFilter, which allows the caller to have it display only a certain subset of the recordings (thanks to Lars Hanisch). - Added handling UTF-8 'umlaut' characters to cKbdRemote (thanks to Lars Hanisch). - Made it clear that the Data parameter in cDevice::StillPicture() may point to a series of packets, not just a single one (thanks to Thomas Reufer). - cDevice::TrickSpeed() now has an additional parameter named Forward, which indicates the direction in which replay is being done (suggested by Thomas Reufer). This information may be necessary for some output devices in order to properly implement trick modes. Authors of plugins that implement output devices will need to add this parameter to their derived cDevice class, regardless of whether they will make use of it or not. - Added a note to ePlayMode in device.h that VDR itself always uses pmAudioVideo when replaying a recording (suggested by Thomas Reufer). - Fixed some spellings in positioner.h and Doxyfile (thanks to Ville Skyttä). - Changed '%a' to the POSIX compliant '%m' in all scanf() calls (thanks to Ville Skyttä). - The new function cCamSlot::Decrypt() can be used by derived classes to implement a CAM slot that can be freely assigned to any device, without being directly inserted into the full TS data stream in hardware. A derived class that implements Decrypt() will also need to set the new parameter ReceiveCaPids in the call to the cCamSlot base class constructor to true, in order to receive the CA pid TS packets that contain data necessary for decrypting. - Many member functions of cCamSlot have been made virtual to allow for easier implementation of derived classes. - cTSBuffer now provides the number of available bytes in its Get() function. - cDvbDevice::GetTSPacket() now calls CamSlot()->Decrypt() in order to allow CAM slots that can be freely assigned to any device access to the TS data stream. - Added a check to avoid a possible NULL pointer dereference in cCiSession::SendData() (reported by Ville Skyttä). - Deleted a superfluous assignment in cPipe::Open() (reported by Ville Skyttä). - The script given to VDR with the '-r' option is now also called after the recording process has actually started (thanks to Christian Kaiser). - Avoiding unnecessary pkg-config warnings in plugin Makefiles (thanks to Ville Skyttä). Plugin authors may want to apply the following change to their Makefile: -PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc)) +PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr)) - Eliminated MAXDVBDEVICES (suggested by Oliver Endriss). - Channels that are no longer contained in the current SDT of a transponder are now marked with the keyword OBSOLETE in their name and provider fields. That way you can identify obsolete channels when you switch to them, and you can get the complete overview of all obsolete channels by sorting the Channels list by provider (by pressing the 0 key twice). Automatic deletion of obsolete channels may follow later.
416 lines
19 KiB
C
416 lines
19 KiB
C
/*
|
|
* 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>.
|
|
*
|
|
* $Id: eit.c 3.3 2013/11/03 13:55:00 kls Exp $
|
|
*/
|
|
|
|
#include "eit.h"
|
|
#include <sys/time.h>
|
|
#include "epg.h"
|
|
#include "i18n.h"
|
|
#include "libsi/section.h"
|
|
#include "libsi/descriptor.h"
|
|
|
|
#define VALID_TIME (31536000 * 2) // two years
|
|
|
|
// --- cEIT ------------------------------------------------------------------
|
|
|
|
class cEIT : public SI::EIT {
|
|
public:
|
|
cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus = false);
|
|
};
|
|
|
|
cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus)
|
|
:SI::EIT(Data, false)
|
|
{
|
|
if (!CheckCRCAndParse())
|
|
return;
|
|
|
|
time_t Now = time(NULL);
|
|
if (Now < VALID_TIME)
|
|
return; // we need the current time for handling PDC descriptors
|
|
|
|
if (!Channels.Lock(false, 10))
|
|
return;
|
|
tChannelID channelID(Source, getOriginalNetworkId(), getTransportStreamId(), getServiceId());
|
|
cChannel *channel = Channels.GetByChannelID(channelID, true);
|
|
if (!channel || EpgHandlers.IgnoreChannel(channel)) {
|
|
Channels.Unlock();
|
|
return;
|
|
}
|
|
|
|
EpgHandlers.BeginSegmentTransfer(channel, OnlyRunningStatus);
|
|
bool handledExternally = EpgHandlers.HandledExternally(channel);
|
|
cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true);
|
|
|
|
bool Empty = true;
|
|
bool Modified = false;
|
|
time_t SegmentStart = 0;
|
|
time_t SegmentEnd = 0;
|
|
struct tm t = { 0 };
|
|
localtime_r(&Now, &t); // this initializes the time zone in 't'
|
|
|
|
SI::EIT::Event SiEitEvent;
|
|
for (SI::Loop::Iterator it; eventLoop.getNext(SiEitEvent, it); ) {
|
|
if (EpgHandlers.HandleEitEvent(pSchedule, &SiEitEvent, Tid, getVersionNumber()))
|
|
continue; // an EPG handler has done all of the processing
|
|
time_t StartTime = SiEitEvent.getStartTime();
|
|
int Duration = SiEitEvent.getDuration();
|
|
// 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.
|
|
if (StartTime == 0 || StartTime > 0 && Duration == 0)
|
|
continue;
|
|
Empty = false;
|
|
if (!SegmentStart)
|
|
SegmentStart = StartTime;
|
|
SegmentEnd = StartTime + Duration;
|
|
cEvent *newEvent = NULL;
|
|
cEvent *rEvent = NULL;
|
|
cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), StartTime);
|
|
if (!pEvent || handledExternally) {
|
|
if (OnlyRunningStatus)
|
|
continue;
|
|
if (handledExternally && !EpgHandlers.IsUpdate(SiEitEvent.getEventId(), StartTime, Tid, getVersionNumber()))
|
|
continue;
|
|
// If we don't have that event yet, we create a new one.
|
|
// Otherwise we copy the information into the existing event anyway, because the data might have changed.
|
|
pEvent = newEvent = new cEvent(SiEitEvent.getEventId());
|
|
newEvent->SetStartTime(StartTime);
|
|
newEvent->SetDuration(Duration);
|
|
if (!handledExternally)
|
|
pSchedule->AddEvent(newEvent);
|
|
}
|
|
else {
|
|
// We have found an existing event, either through its event ID or its start time.
|
|
pEvent->SetSeen();
|
|
uchar TableID = max(pEvent->TableID(), uchar(0x4E)); // for backwards compatibility, table ids less than 0x4E are treated as if they were "present"
|
|
// If the new event has a higher table ID, let's skip it.
|
|
// The lower the table ID, the more "current" the information.
|
|
if (Tid > TableID)
|
|
continue;
|
|
// If the new event comes from the same table and has the same version number
|
|
// as the existing one, let's skip it to avoid unnecessary work.
|
|
// Unfortunately some stations (like, e.g. "Premiere") broadcast their EPG data on several transponders (like
|
|
// the actual Premiere transponder and the Sat.1/Pro7 transponder), but use different version numbers on
|
|
// each of them :-( So if one DVB card is tuned to the Premiere transponder, while an other one is tuned
|
|
// to the Sat.1/Pro7 transponder, events will keep toggling because of the bogus version numbers.
|
|
else if (Tid == TableID && pEvent->Version() == getVersionNumber())
|
|
continue;
|
|
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);
|
|
}
|
|
if (pEvent->TableID() > 0x4E) // for backwards compatibility, table ids less than 0x4E are never overwritten
|
|
pEvent->SetTableID(Tid);
|
|
if (Tid == 0x4E) { // we trust only the present/following info on the actual TS
|
|
if (SiEitEvent.getRunningStatus() >= SI::RunningStatusNotRunning)
|
|
pSchedule->SetRunningStatus(pEvent, SiEitEvent.getRunningStatus(), channel);
|
|
}
|
|
if (OnlyRunningStatus) {
|
|
pEvent->SetVersion(0xFF); // we have already changed the table id above, so set the version to an invalid value to make sure the next full run will be executed
|
|
continue; // do this before setting the version, so that the full update can be done later
|
|
}
|
|
pEvent->SetVersion(getVersionNumber());
|
|
|
|
int LanguagePreferenceShort = -1;
|
|
int LanguagePreferenceExt = -1;
|
|
bool UseExtendedEventDescriptor = false;
|
|
SI::Descriptor *d;
|
|
SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL;
|
|
SI::ShortEventDescriptor *ShortEventDescriptor = NULL;
|
|
cLinkChannels *LinkChannels = NULL;
|
|
cComponents *Components = NULL;
|
|
for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2)); ) {
|
|
switch (d->getDescriptorTag()) {
|
|
case SI::ExtendedEventDescriptorTag: {
|
|
SI::ExtendedEventDescriptor *eed = (SI::ExtendedEventDescriptor *)d;
|
|
if (I18nIsPreferredLanguage(Setup.EPGLanguages, eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) {
|
|
delete ExtendedEventDescriptors;
|
|
ExtendedEventDescriptors = new SI::ExtendedEventDescriptors;
|
|
UseExtendedEventDescriptor = true;
|
|
}
|
|
if (UseExtendedEventDescriptor) {
|
|
ExtendedEventDescriptors->Add(eed);
|
|
d = NULL; // so that it is not deleted
|
|
}
|
|
if (eed->getDescriptorNumber() == eed->getLastDescriptorNumber())
|
|
UseExtendedEventDescriptor = false;
|
|
}
|
|
break;
|
|
case SI::ShortEventDescriptorTag: {
|
|
SI::ShortEventDescriptor *sed = (SI::ShortEventDescriptor *)d;
|
|
if (I18nIsPreferredLanguage(Setup.EPGLanguages, sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) {
|
|
delete ShortEventDescriptor;
|
|
ShortEventDescriptor = sed;
|
|
d = NULL; // so that it is not deleted
|
|
}
|
|
}
|
|
break;
|
|
case SI::ContentDescriptorTag: {
|
|
SI::ContentDescriptor *cd = (SI::ContentDescriptor *)d;
|
|
SI::ContentDescriptor::Nibble Nibble;
|
|
int NumContents = 0;
|
|
uchar Contents[MaxEventContents] = { 0 };
|
|
for (SI::Loop::Iterator it3; cd->nibbleLoop.getNext(Nibble, it3); ) {
|
|
if (NumContents < MaxEventContents) {
|
|
Contents[NumContents] = ((Nibble.getContentNibbleLevel1() & 0xF) << 4) | (Nibble.getContentNibbleLevel2() & 0xF);
|
|
NumContents++;
|
|
}
|
|
}
|
|
EpgHandlers.SetContents(pEvent, Contents);
|
|
}
|
|
break;
|
|
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;
|
|
}
|
|
EpgHandlers.SetParentalRating(pEvent, ParentalRating);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SI::PDCDescriptorTag: {
|
|
SI::PDCDescriptor *pd = (SI::PDCDescriptor *)d;
|
|
t.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
|
|
int month = t.tm_mon;
|
|
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;
|
|
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--;
|
|
time_t vps = mktime(&t);
|
|
EpgHandlers.SetVps(pEvent, vps);
|
|
}
|
|
break;
|
|
case SI::TimeShiftedEventDescriptorTag: {
|
|
SI::TimeShiftedEventDescriptor *tsed = (SI::TimeShiftedEventDescriptor *)d;
|
|
cSchedule *rSchedule = (cSchedule *)Schedules->GetSchedule(tChannelID(Source, channel->Nid(), channel->Tid(), tsed->getReferenceServiceId()));
|
|
if (!rSchedule)
|
|
break;
|
|
rEvent = (cEvent *)rSchedule->GetEvent(tsed->getReferenceEventId());
|
|
if (!rEvent)
|
|
break;
|
|
EpgHandlers.SetTitle(pEvent, rEvent->Title());
|
|
EpgHandlers.SetShortText(pEvent, rEvent->ShortText());
|
|
EpgHandlers.SetDescription(pEvent, rEvent->Description());
|
|
}
|
|
break;
|
|
case SI::LinkageDescriptorTag: {
|
|
SI::LinkageDescriptor *ld = (SI::LinkageDescriptor *)d;
|
|
tChannelID linkID(Source, ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId());
|
|
if (ld->getLinkageType() == 0xB0) { // Premiere World
|
|
bool hit = StartTime <= Now && Now < StartTime + Duration;
|
|
if (hit) {
|
|
char linkName[ld->privateData.getLength() + 1];
|
|
strn0cpy(linkName, (const char *)ld->privateData.getData(), sizeof(linkName));
|
|
// TODO is there a standard way to determine the character set of this string?
|
|
cChannel *link = Channels.GetByChannelID(linkID);
|
|
if (link != channel) { // only link to other channels, not the same one
|
|
//fprintf(stderr, "Linkage %s %4d %4d %5d %5d %5d %5d %02X '%s'\n", hit ? "*" : "", channel->Number(), link ? link->Number() : -1, SiEitEvent.getEventId(), ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId(), ld->getLinkageType(), linkName);//XXX
|
|
if (link) {
|
|
if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3)
|
|
link->SetName(linkName, "", "");
|
|
}
|
|
else if (Setup.UpdateChannels >= 4) {
|
|
cChannel *transponder = channel;
|
|
if (channel->Tid() != ld->getTransportStreamId())
|
|
transponder = Channels.GetByTransponderID(linkID);
|
|
link = Channels.NewChannel(transponder, linkName, "", "", ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId());
|
|
//XXX patFilter->Trigger();
|
|
}
|
|
if (link) {
|
|
if (!LinkChannels)
|
|
LinkChannels = new cLinkChannels;
|
|
LinkChannels->Add(new cLinkChannel(link));
|
|
}
|
|
}
|
|
else
|
|
channel->SetPortalName(linkName);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SI::ComponentDescriptorTag: {
|
|
SI::ComponentDescriptor *cd = (SI::ComponentDescriptor *)d;
|
|
uchar Stream = cd->getStreamContent();
|
|
uchar Type = cd->getComponentType();
|
|
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
|
|
if (!Components)
|
|
Components = new cComponents;
|
|
char buffer[Utf8BufSize(256)];
|
|
Components->SetComponent(Components->NumComponents(), Stream, Type, I18nNormalizeLanguageCode(cd->languageCode), cd->description.getText(buffer, sizeof(buffer)));
|
|
}
|
|
}
|
|
break;
|
|
default: ;
|
|
}
|
|
delete d;
|
|
}
|
|
|
|
if (!rEvent) {
|
|
if (ShortEventDescriptor) {
|
|
char buffer[Utf8BufSize(256)];
|
|
EpgHandlers.SetTitle(pEvent, ShortEventDescriptor->name.getText(buffer, sizeof(buffer)));
|
|
EpgHandlers.SetShortText(pEvent, ShortEventDescriptor->text.getText(buffer, sizeof(buffer)));
|
|
}
|
|
else {
|
|
EpgHandlers.SetTitle(pEvent, NULL);
|
|
EpgHandlers.SetShortText(pEvent, NULL);
|
|
}
|
|
if (ExtendedEventDescriptors) {
|
|
char buffer[Utf8BufSize(ExtendedEventDescriptors->getMaximumTextLength(": ")) + 1];
|
|
EpgHandlers.SetDescription(pEvent, ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": "));
|
|
}
|
|
else
|
|
EpgHandlers.SetDescription(pEvent, NULL);
|
|
}
|
|
delete ExtendedEventDescriptors;
|
|
delete ShortEventDescriptor;
|
|
|
|
EpgHandlers.SetComponents(pEvent, Components);
|
|
|
|
EpgHandlers.FixEpgBugs(pEvent);
|
|
if (LinkChannels)
|
|
channel->SetLinkChannels(LinkChannels);
|
|
Modified = true;
|
|
EpgHandlers.HandleEvent(pEvent);
|
|
if (handledExternally)
|
|
delete pEvent;
|
|
}
|
|
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
|
|
pSchedule->ClrRunningStatus(channel);
|
|
pSchedule->SetPresentSeen();
|
|
}
|
|
if (Modified && !OnlyRunningStatus) {
|
|
EpgHandlers.SortSchedule(pSchedule);
|
|
EpgHandlers.DropOutdated(pSchedule, SegmentStart, SegmentEnd, Tid, getVersionNumber());
|
|
Schedules->SetModified(pSchedule);
|
|
}
|
|
Channels.Unlock();
|
|
EpgHandlers.EndSegmentTransfer(Modified, OnlyRunningStatus);
|
|
}
|
|
|
|
// --- cTDT ------------------------------------------------------------------
|
|
|
|
#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
|
|
|
|
class cTDT : public SI::TDT {
|
|
private:
|
|
static cMutex mutex;
|
|
static time_t lastAdj;
|
|
public:
|
|
cTDT(const u_char *Data);
|
|
};
|
|
|
|
cMutex cTDT::mutex;
|
|
time_t cTDT::lastAdj = 0;
|
|
|
|
cTDT::cTDT(const u_char *Data)
|
|
:SI::TDT(Data, false)
|
|
{
|
|
CheckParse();
|
|
|
|
time_t dvbtim = getTime();
|
|
time_t loctim = time(NULL);
|
|
|
|
int diff = dvbtim - loctim;
|
|
if (abs(diff) > MAX_TIME_DIFF) {
|
|
mutex.Lock();
|
|
if (abs(diff) > MAX_ADJ_DIFF) {
|
|
if (stime(&dvbtim) == 0)
|
|
isyslog("system time changed from %s (%ld) to %s (%ld)", *TimeToString(loctim), loctim, *TimeToString(dvbtim), dvbtim);
|
|
else
|
|
esyslog("ERROR while setting system time: %m");
|
|
}
|
|
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");
|
|
}
|
|
mutex.Unlock();
|
|
}
|
|
}
|
|
|
|
// --- cEitFilter ------------------------------------------------------------
|
|
|
|
time_t cEitFilter::disableUntil = 0;
|
|
|
|
cEitFilter::cEitFilter(void)
|
|
{
|
|
Set(0x12, 0x40, 0xC0); // event info now&next actual/other TS (0x4E/0x4F), future actual/other TS (0x5X/0x6X)
|
|
Set(0x14, 0x70); // TDT
|
|
}
|
|
|
|
void cEitFilter::SetDisableUntil(time_t Time)
|
|
{
|
|
disableUntil = Time;
|
|
}
|
|
|
|
void cEitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
|
|
{
|
|
if (disableUntil) {
|
|
if (time(NULL) > disableUntil)
|
|
disableUntil = 0;
|
|
else
|
|
return;
|
|
}
|
|
switch (Pid) {
|
|
case 0x12: {
|
|
if (Tid >= 0x4E && Tid <= 0x6F) {
|
|
cSchedulesLock SchedulesLock(true, 10);
|
|
cSchedules *Schedules = (cSchedules *)cSchedules::Schedules(SchedulesLock);
|
|
if (Schedules)
|
|
cEIT EIT(Schedules, Source(), Tid, Data);
|
|
else {
|
|
// If we don't get a write lock, let's at least get a read lock, so
|
|
// that we can set the running status and 'seen' timestamp (well, actually
|
|
// with a read lock we shouldn't be doing that, but it's only integers that
|
|
// get changed, so it should be ok)
|
|
cSchedulesLock SchedulesLock;
|
|
cSchedules *Schedules = (cSchedules *)cSchedules::Schedules(SchedulesLock);
|
|
if (Schedules)
|
|
cEIT EIT(Schedules, Source(), Tid, Data, true);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 0x14: {
|
|
if (Setup.SetSystemTime && Setup.TimeSource == Source() && Setup.TimeTransponder && ISTRANSPONDER(Transponder(), Setup.TimeTransponder))
|
|
cTDT TDT(Data);
|
|
}
|
|
break;
|
|
default: ;
|
|
}
|
|
}
|