summaryrefslogtreecommitdiff
path: root/eit.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2004-01-04 18:00:00 +0100
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2004-01-04 18:00:00 +0100
commit93a94b18b540fbcb9bcdaaea9abd26cdf23d6ee6 (patch)
treebede17e6cd329d36ec02bc53dfe567b95ec835a5 /eit.c
parentc432905dd60630f906ac89f58592ad835a9063ef (diff)
downloadvdr-patch-lnbsharing-93a94b18b540fbcb9bcdaaea9abd26cdf23d6ee6.tar.gz
vdr-patch-lnbsharing-93a94b18b540fbcb9bcdaaea9abd26cdf23d6ee6.tar.bz2
Version 1.3.0vdr-1.3.0
- Changed thread handling to make it work with NPTL ("Native Posix Thread Library"). Thanks to Jon Burgess, Andreas Schultz, Werner Fink and Stefan Huelswitt. - The cThread class now accepts a 'Description' parameter, which is used to log the beginning and end of the thread, together with its process and thread id. For descriptions that need additional parameters you can use the function cThread::SetDescription(), which accepts 'printf()' like arguments. Existing plugins that use threads should be changed to use this functionality instead of explicit 'dsyslog()' calls inside their Action() function in order to support logging the thread ids. - Added "Slovak Link" and "Czech Link" to 'ca.conf' (thanks to Emil Petersky). However, 'ca.conf' is now pretty much obsolete due to the automatic CA handling. - Mutexes are now created with PTHREAD_MUTEX_ERRORCHECK_NP, which makes the 'lockingTid' stuff obsolete (thanks to Stefan Huelswitt). - Changed font handling to allow language specific character sets. - Adopted the small font character set from the "Elchi" patch (originally provided by Alessio Sangalli). - Greek language texts now use iso8859-7 character set (thanks to Dimitrios Dimitrakos). - Rearranged section data handling, so that the actual data handling can be done separately, even from within plugins. - The EPG data structures have been moved from eit.[hc] to epg.[hc] and have been adapted to the general VDR coding style. Plugins that use these data structures may need to change some function names (which should be obvious). The name 'subtitle' has been changed to 'shortText' to avoid clashes with actual subtitles that are part of a movie. The name 'extendedDescription' has been shortened to 'description'. - Replaced 'libdtv' with 'libsi' (thanks to Marcel Wiesweg), which is thread safe and can be used by multiple section filters simultaneously. - Added 'cRwLock' to 'thread.[hc]'. Note that all plugin Makefiles need to define _GNU_SOURCE for this to work (see the example plugin Makefiles and 'newplugin'). - Fixed a problem with crc32 in SI handling on 64bit systems (thanks to Pedro Miguel Sequeira de Justo Teixeira for reporting this one). - Fixed an alignment problem in CAM access on 64bit systems (thanks to Pedro Miguel Sequeira de Justo Teixeira for reporting this one). - Added 'StreamType' setting to CAM communication, which is important for Aston/SECA CAMs (thanks to Antonino Sergi). - Now the CA descriptors are sent to the CAM in the 'program' or 'ES level' sections, depending on where they are found in the PMT (thanks to Hans-Peter Raschke for reporting this one). This should make SkyCrypt CAMs work. - Now using the 'version number' of EPG events to avoid unnecessary work. - Channel data is now automatically derived from the DVB data stream (inspired by the 'autopid' patch from Andreas Schultz). - The current channel is now automatically re-tuned if the PIDs or other settings change. If a recording is going on on a channel that has a change in its settings, the recording will be stopped and immediately restarted to use the new channel settings. - EPG events now use the complete channel ID with NID, TID and SID. - Channel names in 'channels.conf' can now have a short form, as provided by some tv stations (see man vdr(5)). Currently channels that provide short names in addition to long ones are listed in the OSD as "short,long name", as in "RTL,RTL Television". The short names will be used explicitly later. - The Ca parameter in 'channels.conf' has been extended and now contains all the CA system ids for the given channel. When switching to a channel VDR now tests for a device that provides one of these CA system ids. The devices automatically get their supported ids from the CI handler. - The values in 'ca.conf' are currently without any real meaning. Whether or not a channel with conditional access can be received is now determined automatically by evaluating its CA descriptors and comparing them to the CA system ids provided by the installed CAM. Only the special values 1-16 are used to assign a channel to a particular device. - Increased the maximum number of possible OSD colors to 256. - Limited the line length in the EPG bugfix report, which appears to fix a buffer overflow that caused a crash when cleaning up the EPG data (at 05:00 in the morning).
Diffstat (limited to 'eit.c')
-rw-r--r--eit.c1596
1 files changed, 157 insertions, 1439 deletions
diff --git a/eit.c b/eit.c
index 8e0ade7..45a4d80 100644
--- a/eit.c
+++ b/eit.c
@@ -1,1483 +1,201 @@
-/***************************************************************************
- eit.c - description
- -------------------
- begin : Fri Aug 25 2000
- copyright : (C) 2000 by Robert Schneider
- email : Robert.Schneider@web.de
-
- 2001-08-15: Adapted to 'libdtv' by Rolf Hakenes <hakenes@hippomi.de>
-
- ***************************************************************************/
-
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * $Id: eit.c 1.80 2003/10/12 11:05:42 kls Exp $
- ***************************************************************************/
+/*
+ * 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 1.84 2004/01/02 22:27:29 kls Exp $
+ */
#include "eit.h"
-#include <ctype.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <linux/dvb/dmx.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/poll.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-#include "channels.h"
-#include "config.h"
-#include "libdtv/libdtv.h"
-#include "videodir.h"
-
-// --- cMJD ------------------------------------------------------------------
-
-class cMJD {
-public:
- cMJD();
- cMJD(u_char date_hi, u_char date_lo);
- cMJD(u_char date_hi, u_char date_lo, u_char timehr, u_char timemi, u_char timese);
- ~cMJD();
- /** */
- void ConvertToTime();
- /** */
- bool SetSystemTime();
- /** */
- time_t GetTime_t();
-protected: // Protected attributes
- /** */
- time_t mjdtime;
-protected: // Protected attributes
- /** */
- u_char time_second;
-protected: // Protected attributes
- /** */
- u_char time_minute;
-protected: // Protected attributes
- /** */
- u_char time_hour;
-protected: // Protected attributes
- /** */
- u_short mjd;
-};
-
-cMJD::cMJD()
-{
-}
-
-cMJD::cMJD(u_char date_hi, u_char date_lo)
-{
- mjd = date_hi << 8 | date_lo;
- time_hour = time_minute = time_second = 0;
- ConvertToTime();
-}
-
-cMJD::cMJD(u_char date_hi, u_char date_lo, u_char timehr, u_char timemi, u_char timese)
-{
- mjd = date_hi << 8 | date_lo;
- time_hour = timehr;
- time_minute = timemi;
- time_second = timese;
- ConvertToTime();
-}
-
-cMJD::~cMJD()
-{
-}
-
-/** */
-void cMJD::ConvertToTime()
-{
- struct tm t;
-
- t.tm_sec = time_second;
- t.tm_min = time_minute;
- t.tm_hour = time_hour;
- int k;
+#include "epg.h"
+#include "libsi/section.h"
+#include "libsi/descriptor.h"
- t.tm_year = (int) ((mjd - 15078.2) / 365.25);
- t.tm_mon = (int) ((mjd - 14956.1 - (int)(t.tm_year * 365.25)) / 30.6001);
- t.tm_mday = (int) (mjd - 14956 - (int)(t.tm_year * 365.25) - (int)(t.tm_mon * 30.6001));
- k = (t.tm_mon == 14 || t.tm_mon == 15) ? 1 : 0;
- t.tm_year = t.tm_year + k;
- t.tm_mon = t.tm_mon - 1 - k * 12;
- t.tm_mon--;
-
- t.tm_isdst = -1;
- t.tm_gmtoff = 0;
-
- mjdtime = timegm(&t);
-
- //isyslog("Time parsed = %s\n", ctime(&mjdtime));
-}
-
-/** */
-bool cMJD::SetSystemTime()
-{
- struct tm *ptm;
- time_t loctim;
-
- struct tm tm_r;
- ptm = localtime_r(&mjdtime, &tm_r);
- loctim = time(NULL);
-
- if (abs(mjdtime - loctim) > 2)
- {
- isyslog("System Time = %s (%ld)\n", ctime(&loctim), loctim);
- isyslog("Local Time = %s (%ld)\n", ctime(&mjdtime), mjdtime);
- if (stime(&mjdtime) < 0)
- esyslog("ERROR while setting system time: %m");
- return true;
- }
-
- return false;
-}
-/** */
-time_t cMJD::GetTime_t()
-{
- return mjdtime;
-}
-
-// --- cTDT ------------------------------------------------------------------
+// --- cEIT ------------------------------------------------------------------
-class cTDT {
+class cEIT : public SI::EIT {
public:
- cTDT(tdt_t *ptdt);
- ~cTDT();
- /** */
- bool SetSystemTime();
-protected: // Protected attributes
- /** */
- tdt_t tdt;
- /** */
- cMJD mjd; // kls 2001-03-02: made this a member instead of a pointer (it wasn't deleted in the destructor!)
-};
-
-#define BCD2DEC(b) (((b >> 4) & 0x0F) * 10 + (b & 0x0F))
-
-cTDT::cTDT(tdt_t *ptdt)
-:tdt(*ptdt)
-,mjd(tdt.utc_mjd_hi, tdt.utc_mjd_lo, BCD2DEC(tdt.utc_time_h), BCD2DEC(tdt.utc_time_m), BCD2DEC(tdt.utc_time_s))
-{
-}
-
-cTDT::~cTDT()
-{
-}
-/** */
-bool cTDT::SetSystemTime()
-{
- return mjd.SetSystemTime();
-}
-
-// --- cEventInfo ------------------------------------------------------------
-
-cEventInfo::cEventInfo(tChannelID channelid, unsigned short eventid)
-{
- pTitle = NULL;
- pSubtitle = NULL;
- pExtendedDescription = NULL;
- bIsPresent = bIsFollowing = false;
- lDuration = 0;
- tTime = 0;
- uTableID = 0;
- uEventID = eventid;
- channelID = channelid;
- nChannelNumber = 0;
-}
-
-cEventInfo::~cEventInfo()
-{
- free(pTitle);
- free(pSubtitle);
- free(pExtendedDescription);
-}
-
-/** */
-const char * cEventInfo::GetTitle() const
-{
- return pTitle;
-}
-/** */
-const char * cEventInfo::GetSubtitle() const
-{
- return pSubtitle;
-}
-/** */
-const char * cEventInfo::GetExtendedDescription() const
-{
- return pExtendedDescription;
-}
-/** */
-bool cEventInfo::IsPresent() const
-{
- return bIsPresent;
-}
-/** */
-void cEventInfo::SetPresent(bool pres)
-{
- bIsPresent = pres;
-}
-/** */
-bool cEventInfo::IsFollowing() const
-{
- return bIsFollowing;
-}
-
-void cEventInfo::SetTableID(unsigned char tableid)
-{
- uTableID = tableid;
-}
-
-/** */
-void cEventInfo::SetFollowing(bool foll)
-{
- bIsFollowing = foll;
-}
-/** */
-const char * cEventInfo::GetDate() const
-{
- static char szDate[25];
-
- struct tm tm_r;
- strftime(szDate, sizeof(szDate), "%d.%m.%Y", localtime_r(&tTime, &tm_r));
-
- return szDate;
-}
-
-const unsigned char cEventInfo::GetTableID(void) const
-{
- return uTableID;
-}
-
-/** */
-const char * cEventInfo::GetTimeString() const
-{
- static char szTime[25];
-
- struct tm tm_r;
- strftime(szTime, sizeof(szTime), "%R", localtime_r(&tTime, &tm_r));
-
- return szTime;
-}
-/** */
-const char * cEventInfo::GetEndTimeString() const
-{
- static char szEndTime[25];
- time_t tEndTime = tTime + lDuration;
-
- struct tm tm_r;
- strftime(szEndTime, sizeof(szEndTime), "%R", localtime_r(&tEndTime, &tm_r));
+ cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data);
+ };
- return szEndTime;
-}
-/** */
-time_t cEventInfo::GetTime() const
-{
- return tTime;
-}
-/** */
-long cEventInfo::GetDuration() const
-{
- return lDuration;
-}
-/** */
-unsigned short cEventInfo::GetEventID() const
-{
- return uEventID;
-}
-/** */
-void cEventInfo::SetTitle(const char *string)
-{
- pTitle = strcpyrealloc(pTitle, string);
-}
-/** */
-void cEventInfo::SetSubtitle(const char *string)
-{
- pSubtitle = strcpyrealloc(pSubtitle, string);
-}
-/** */
-void cEventInfo::SetExtendedDescription(const char *string)
-{
- pExtendedDescription = strcpyrealloc(pExtendedDescription, string);
-}
-/** */
-void cEventInfo::SetTime(time_t t)
-{
- tTime = t;
-}
-/** */
-void cEventInfo::SetDuration(long l)
-{
- lDuration = l;
-}
-/** */
-void cEventInfo::SetEventID(unsigned short evid)
+cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)
+:SI::EIT(Data, false)
{
- uEventID = evid;
-}
-/** */
-void cEventInfo::SetChannelID(tChannelID channelid)
-{
- channelID = channelid;
-}
+ if (!CheckCRCAndParse())
+ return;
-/** */
-tChannelID cEventInfo::GetChannelID() const
-{
- return channelID;
-}
+ tChannelID channelID(Source, getOriginalNetworkId(), getTransportStreamId(), getServiceId());
+ cChannel *channel = Channels.GetByChannelID(channelID, true);
+ if (!channel)
+ return; // only collect data for known channels
-/** */
-void cEventInfo::Dump(FILE *f, const char *Prefix) const
-{
- if (tTime + lDuration >= time(NULL)) {
- fprintf(f, "%sE %u %ld %ld %X\n", Prefix, uEventID, tTime, lDuration, uTableID);
- if (!isempty(pTitle))
- fprintf(f, "%sT %s\n", Prefix, pTitle);
- if (!isempty(pSubtitle))
- fprintf(f, "%sS %s\n", Prefix, pSubtitle);
- if (!isempty(pExtendedDescription))
- fprintf(f, "%sD %s\n", Prefix, pExtendedDescription);
- fprintf(f, "%se\n", Prefix);
- }
-}
+ cEvent *rEvent = NULL;
-bool cEventInfo::Read(FILE *f, cSchedule *Schedule)
-{
- if (Schedule) {
- cEventInfo *pEvent = NULL;
- char *s;
- while ((s = readline(f)) != NULL) {
- char *t = skipspace(s + 1);
- switch (*s) {
- case 'E': if (!pEvent) {
- unsigned int uEventID;
- time_t tTime;
- long lDuration;
- unsigned int uTableID = 0;
- int n = sscanf(t, "%u %ld %ld %X", &uEventID, &tTime, &lDuration, &uTableID);
- if (n == 3 || n == 4) {
- pEvent = (cEventInfo *)Schedule->GetEvent(uEventID, tTime);
- if (!pEvent)
- pEvent = Schedule->AddEvent(new cEventInfo(Schedule->GetChannelID(), uEventID));
- if (pEvent) {
- pEvent->SetTableID(uTableID);
- pEvent->SetTime(tTime);
- pEvent->SetDuration(lDuration);
- }
- }
- }
- break;
- case 'T': if (pEvent)
- pEvent->SetTitle(t);
- break;
- case 'S': if (pEvent)
- pEvent->SetSubtitle(t);
- break;
- case 'D': if (pEvent)
- pEvent->SetExtendedDescription(t);
- break;
- case 'e': pEvent = NULL;
- break;
- case 'c': // to keep things simple we react on 'c' here
- return true;
- default: esyslog("ERROR: unexpected tag while reading EPG data: %s", s);
- return false;
- }
- }
- esyslog("ERROR: unexpected end of file while reading EPG data");
+ cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channelID);
+ if (!pSchedule) {
+ pSchedule = new cSchedule(channelID);
+ Schedules->Add(pSchedule);
}
- return false;
-}
-
-#define MAXEPGBUGFIXSTATS 7
-#define MAXEPGBUGFIXCHANS 100
-struct tEpgBugFixStats {
- int hits;
- int n;
- tChannelID channelIDs[MAXEPGBUGFIXCHANS];
- tEpgBugFixStats(void) { hits = n = 0; }
- };
-tEpgBugFixStats EpgBugFixStats[MAXEPGBUGFIXSTATS];
-
-static void EpgBugFixStat(int Number, tChannelID ChannelID)
-{
- if (0 <= Number && Number < MAXEPGBUGFIXSTATS) {
- tEpgBugFixStats *p = &EpgBugFixStats[Number];
- p->hits++;
- int i = 0;
- for (; i < p->n; i++) {
- if (p->channelIDs[i] == ChannelID)
- break;
+ SI::EIT::Event SiEitEvent;
+ for (SI::Loop::Iterator it; eventLoop.hasNext(it); ) {
+ SiEitEvent = eventLoop.getNext(it);
+
+ cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), SiEitEvent.getStartTime());
+ if (!pEvent) {
+ // If we don't have that event ID yet, we create a new one.
+ // Otherwise we copy the information into the existing event anyway, because the data might have changed.
+ pEvent = pSchedule->AddEvent(new cEvent(channelID, SiEitEvent.getEventId()));
+ if (!pEvent)
+ continue;
+ pEvent->SetTableID(Tid);
}
- if (i == p->n && p->n < MAXEPGBUGFIXCHANS)
- p->channelIDs[p->n++] = ChannelID;
- }
-}
-
-static void ReportEpgBugFixStats(bool Reset = false)
-{
- if (Setup.EPGBugfixLevel > 0) {
- bool GotHits = false;
- char buffer[1024];
- for (int i = 0; i < MAXEPGBUGFIXSTATS; i++) {
- const char *delim = "\t";
- tEpgBugFixStats *p = &EpgBugFixStats[i];
- if (p->hits) {
- bool PrintedStats = false;
- char *q = buffer;
- *buffer = 0;
- for (int c = 0; c < p->n; c++) {
- cChannel *channel = Channels.GetByChannelID(p->channelIDs[c], true);
- if (channel) {
- if (!GotHits) {
- dsyslog("=====================");
- dsyslog("EPG bugfix statistics");
- dsyslog("=====================");
- dsyslog("IF SOMEBODY WHO IS IN CHARGE OF THE EPG DATA FOR ONE OF THE LISTED");
- dsyslog("CHANNELS READS THIS: PLEASE TAKE A LOOK AT THE FUNCTION cEventInfo::FixEpgBugs()");
- dsyslog("IN VDR/eit.c TO LEARN WHAT'S WRONG WITH YOUR DATA, AND FIX IT!");
- dsyslog("=====================");
- dsyslog("Fix\tHits\tChannels");
- GotHits = true;
- }
- if (!PrintedStats) {
- q += snprintf(q, sizeof(buffer) - (q - buffer), "%d\t%d", i, p->hits);
- PrintedStats = true;
- }
- q += snprintf(q, sizeof(buffer) - (q - buffer), "%s%s", delim, channel->Name());
- delim = ", ";
- }
- }
- if (*buffer)
- dsyslog("%s", buffer);
- }
- if (Reset)
- p->hits = p->n = 0;
+ else {
+ // We have found an existing event, either through its event ID or its start time.
+ // If the existing event has a zero table ID it was defined externally and shall
+ // not be overwritten.
+ if (pEvent->TableID() == 0x00)
+ continue;
+ // If the new event comes from a table that belongs to an "other TS" and the existing
+ // one comes from an "actual TS" table, let's skip it.
+ #define ISACTUALTS(tid) (tid == 0x4E || (tid & 0x50) == 0x50)
+ if (!ISACTUALTS(Tid) && ISACTUALTS(pEvent->TableID()))
+ continue;
+ // If the new event comes from a "schedule" table and the existing one comes from
+ // a "present/following" table, let's skip it (the p/f table usually contains more
+ // information, like e.g. a description).
+ if ((Tid & 0x50) == 0x50 && pEvent->TableID() == 0x4E || (Tid & 0x60) == 0x60 && pEvent->TableID() == 0x4F)
+ continue;
+ // If both events come from the same "schedule" table and the new event's table id is larger than the
+ // existing one's, let's skip it (higher tids mean "farther in the future" and usually have less information).
+ if (((Tid & 0x50) == 0x50 || (Tid & 0x60) == 0x60) && (pEvent->TableID() & 0xF0) == (Tid & 0xF0) && (Tid > pEvent->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.
+ if (Tid == pEvent->TableID() && pEvent->Version() == getVersionNumber())
+ continue;
}
- if (GotHits)
- dsyslog("=====================");
- }
-}
-
-void cEventInfo::FixEpgBugs(void)
-{
- // VDR can't usefully handle newline characters in the EPG data, so let's
- // always convert them to blanks (independent of the setting of EPGBugfixLevel):
- strreplace(pTitle, '\n', ' ');
- strreplace(pSubtitle, '\n', ' ');
- strreplace(pExtendedDescription, '\n', ' ');
-
- if (Setup.EPGBugfixLevel == 0)
- return;
-
- // Some TV stations apparently have their own idea about how to fill in the
- // EPG data. Let's fix their bugs as good as we can:
- if (pTitle) {
-
- // VOX puts too much information into the Subtitle and leaves the Extended
- // Description empty:
- //
- // Title
- // (NAT, Year Min')[ ["Subtitle". ]Extended Description]
- //
- if (pSubtitle && !pExtendedDescription) {
- if (*pSubtitle == '(') {
- char *e = strchr(pSubtitle + 1, ')');
- if (e) {
- if (*(e + 1)) {
- if (*++e == ' ')
- if (*(e + 1) == '"')
- e++;
+ pEvent->SetVersion(getVersionNumber());
+ pEvent->SetTableID(Tid);
+ pEvent->SetEventID(SiEitEvent.getEventId()); // unfortunately some stations use different event ids for the same event in different tables :-(
+
+ SI::Descriptor *d;
+ SI::ExtendedEventDescriptors exGroup;
+ char text[256];
+ for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2)); ) {
+ switch (d->getDescriptorTag()) {
+ case SI::ExtendedEventDescriptorTag:
+ exGroup.Add((SI::ExtendedEventDescriptor *)d);
+ d = NULL; //so that it is not deleted
+ break;
+ case SI::ShortEventDescriptorTag: {
+ SI::ShortEventDescriptor *sed = (SI::ShortEventDescriptor *)d;
+ pEvent->SetTitle(sed->name.getText(text));
+ pEvent->SetShortText(sed->text.getText(text));
}
- else
- e = NULL;
- char *s = e ? strdup(e) : NULL;
- free(pSubtitle);
- pSubtitle = s;
- EpgBugFixStat(0, GetChannelID());
- // now the fixes #1 and #2 below will handle the rest
- }
- }
- }
-
- // VOX and VIVA put the Subtitle in quotes and use either the Subtitle
- // or the Extended Description field, depending on how long the string is:
- //
- // Title
- // "Subtitle". Extended Description
- //
- if ((pSubtitle == NULL) != (pExtendedDescription == NULL)) {
- char *p = pSubtitle ? pSubtitle : pExtendedDescription;
- if (*p == '"') {
- const char *delim = "\".";
- char *e = strstr(p + 1, delim);
- if (e) {
- *e = 0;
- char *s = strdup(p + 1);
- char *d = strdup(e + strlen(delim));
- free(pSubtitle);
- free(pExtendedDescription);
- pSubtitle = s;
- pExtendedDescription = d;
- EpgBugFixStat(1, GetChannelID());
- }
- }
- }
-
- // VOX and VIVA put the Extended Description into the Subtitle (preceeded
- // by a blank) if there is no actual Subtitle and the Extended Description
- // is short enough:
- //
- // Title
- // Extended Description
- //
- if (pSubtitle && !pExtendedDescription) {
- if (*pSubtitle == ' ') {
- memmove(pSubtitle, pSubtitle + 1, strlen(pSubtitle));
- pExtendedDescription = pSubtitle;
- pSubtitle = NULL;
- EpgBugFixStat(2, GetChannelID());
- }
- }
-
- // Pro7 sometimes repeats the Title in the Subtitle:
- //
- // Title
- // Title
- //
- if (pSubtitle && strcmp(pTitle, pSubtitle) == 0) {
- free(pSubtitle);
- pSubtitle = NULL;
- EpgBugFixStat(3, GetChannelID());
- }
-
- // ZDF.info puts the Subtitle between double quotes, which is nothing
- // but annoying (some even put a '.' after the closing '"'):
- //
- // Title
- // "Subtitle"[.]
- //
- if (pSubtitle && *pSubtitle == '"') {
- int l = strlen(pSubtitle);
- if (l > 2 && (pSubtitle[l - 1] == '"' || (pSubtitle[l - 1] == '.' && pSubtitle[l - 2] == '"'))) {
- memmove(pSubtitle, pSubtitle + 1, l);
- char *p = strrchr(pSubtitle, '"');
- if (p)
- *p = 0;
- EpgBugFixStat(4, GetChannelID());
- }
- }
-
- if (Setup.EPGBugfixLevel <= 1)
- return;
-
- // Some channels apparently try to do some formatting in the texts,
- // which is a bad idea because they have no way of knowing the width
- // of the window that will actually display the text.
- // Remove excess whitespace:
- pTitle = compactspace(pTitle);
- pSubtitle = compactspace(pSubtitle);
- pExtendedDescription = compactspace(pExtendedDescription);
- // Remove superfluous hyphens:
- if (pExtendedDescription) {
- char *p = pExtendedDescription;
- while (*p && *(p + 1) && *(p + 2)) {
- if (*p == '-' && *(p + 1) == ' ' && *(p + 2) && islower(*(p - 1)) && islower(*(p + 2))) {
- if (!startswith(p + 2, "und ")) { // special case in German, as in "Lach- und Sachgeschichten"
- memmove(p, p + 2, strlen(p + 2) + 1);
- EpgBugFixStat(5, GetChannelID());
- }
+ break;
+ case SI::ContentDescriptorTag:
+ break;
+ case SI::ParentalRatingDescriptorTag:
+ break;
+ case SI::TimeShiftedEventDescriptorTag: {
+ SI::TimeShiftedEventDescriptor *tsed = (SI::TimeShiftedEventDescriptor *)d;
+ cSchedule *rSchedule = (cSchedule *)Schedules->GetSchedule(tChannelID(Source, 0, 0, tsed->getReferenceServiceId()));
+ if (!rSchedule)
+ break;
+ rEvent = (cEvent *)rSchedule->GetEvent(tsed->getReferenceEventId());
+ if (!rEvent)
+ break;
+ pEvent->SetTitle(rEvent->Title());
+ pEvent->SetShortText(rEvent->ShortText());
+ pEvent->SetDescription(rEvent->Description());
}
- p++;
- }
- }
-
-#define MAX_USEFUL_SUBTITLE_LENGTH 40
- // Some channels put a whole lot of information in the Subtitle and leave
- // the Extended Description totally empty. So if the Subtitle length exceeds
- // MAX_USEFUL_SUBTITLE_LENGTH, let's put this into the Extended Description
- // instead:
- if (!isempty(pSubtitle) && isempty(pExtendedDescription)) {
- if (strlen(pSubtitle) > MAX_USEFUL_SUBTITLE_LENGTH) {
- free(pExtendedDescription);
- pExtendedDescription = pSubtitle;
- pSubtitle = NULL;
- EpgBugFixStat(6, GetChannelID());
- }
- }
-
- // Some channels use the ` ("backtick") character, where a ' (single quote)
- // would be normally used. Actually, "backticks" in normal text don't make
- // much sense, so let's replace them:
- strreplace(pTitle, '`', '\'');
- strreplace(pSubtitle, '`', '\'');
- strreplace(pExtendedDescription, '`', '\'');
- }
-}
-
-// --- cSchedule -------------------------------------------------------------
-
-cSchedule::cSchedule(tChannelID channelid)
-{
- pPresent = pFollowing = NULL;
- channelID = channelid;
-}
-
-
-cSchedule::~cSchedule()
-{
-}
-
-cEventInfo *cSchedule::AddEvent(cEventInfo *EventInfo)
-{
- Events.Add(EventInfo);
- return EventInfo;
-}
-
-const cEventInfo *cSchedule::GetPresentEvent(void) const
-{
- return GetEventAround(time(NULL));
-}
+ break;
+ default: ;
+ }
+ delete d;
+ }
-const cEventInfo *cSchedule::GetFollowingEvent(void) const
-{
- const cEventInfo *pe = NULL;
- time_t now = time(NULL);
- time_t delta = INT_MAX;
- for (cEventInfo *p = Events.First(); p; p = Events.Next(p)) {
- time_t dt = p->GetTime() - now;
- if (dt > 0 && dt < delta) {
- delta = dt;
- pe = p;
+ if (!rEvent) {
+ char buffer[exGroup.getMaximumTextLength()];
+ pEvent->SetDescription(exGroup.getText(buffer));
}
- }
- return pe;
-}
-
-void cSchedule::SetChannelID(tChannelID channelid)
-{
- channelID = channelid;
-}
-/** */
-tChannelID cSchedule::GetChannelID() const
-{
- return channelID;
-}
-/** */
-const cEventInfo * cSchedule::GetEvent(unsigned short uEventID, time_t tTime) const
-{
- // Returns either the event info with the given uEventID or, if that one can't
- // be found, the one with the given tTime (or NULL if neither can be found)
- cEventInfo *pe = Events.First();
- cEventInfo *pt = NULL;
- while (pe != NULL)
- {
- if (pe->GetEventID() == uEventID)
- return pe;
- if (tTime > 0 && pe->GetTime() == tTime) // 'tTime < 0' is apparently used with NVOD channels
- pt = pe;
-
- pe = Events.Next(pe);
- }
- return pt;
-}
+ pEvent->SetStartTime(SiEitEvent.getStartTime());
+ pEvent->SetDuration(SiEitEvent.getDuration());
+ pEvent->FixEpgBugs();
-const cEventInfo *cSchedule::GetEventAround(time_t Time) const
-{
- const cEventInfo *pe = NULL;
- time_t delta = INT_MAX;
- for (cEventInfo *p = Events.First(); p; p = Events.Next(p)) {
- time_t dt = Time - p->GetTime();
- if (dt >= 0 && dt < delta && p->GetTime() + p->GetDuration() >= Time) {
- delta = dt;
- pe = p;
+ if (isPresentFollowing()) {
+ if (SiEitEvent.getRunningStatus() == SI::RunningStatusPausing || SiEitEvent.getRunningStatus() == SI::RunningStatusRunning)
+ pSchedule->SetPresentEvent(pEvent);
+ else if (SiEitEvent.getRunningStatus() == SI::RunningStatusStartsInAFewSeconds)
+ pSchedule->SetFollowingEvent(pEvent);
}
}
- return pe;
-}
-
-bool cSchedule::SetPresentEvent(cEventInfo *pEvent)
-{
- if (pPresent != NULL)
- pPresent->SetPresent(false);
- pPresent = pEvent;
- pPresent->SetPresent(true);
-
- return true;
-}
-
-/** */
-bool cSchedule::SetFollowingEvent(cEventInfo *pEvent)
-{
- if (pFollowing != NULL)
- pFollowing->SetFollowing(false);
- pFollowing = pEvent;
- pFollowing->SetFollowing(true);
-
- return true;
-}
-
-/** */
-void cSchedule::Cleanup()
-{
- Cleanup(time(NULL));
-}
-
-/** */
-void cSchedule::Cleanup(time_t tTime)
-{
- cEventInfo *pEvent;
- for (int a = 0; true ; a++)
- {
- pEvent = Events.Get(a);
- if (pEvent == NULL)
- break;
- if (pEvent->GetTime() + pEvent->GetDuration() + 3600 < tTime) // adding one hour for safety
- {
- Events.Del(pEvent);
- a--;
- }
- }
-}
-
-/** */
-void cSchedule::Dump(FILE *f, const char *Prefix) const
-{
- cChannel *channel = Channels.GetByChannelID(channelID, true);
- if (channel)
- {
- fprintf(f, "%sC %s %s\n", Prefix, channel->GetChannelID().ToString(), channel->Name());
- for (cEventInfo *p = Events.First(); p; p = Events.Next(p))
- p->Dump(f, Prefix);
- fprintf(f, "%sc\n", Prefix);
- }
-}
-
-bool cSchedule::Read(FILE *f, cSchedules *Schedules)
-{
- if (Schedules) {
- char *s;
- while ((s = readline(f)) != NULL) {
- if (*s == 'C') {
- s = skipspace(s + 1);
- char *p = strchr(s, ' ');
- if (p)
- *p = 0; // strips optional channel name
- if (*s) {
- tChannelID channelID = tChannelID::FromString(s);
- if (channelID.Valid()) {
- cSchedule *p = (cSchedule *)Schedules->AddChannelID(channelID);
- if (p) {
- if (!cEventInfo::Read(f, p))
- return false;
- }
- }
- else {
- esyslog("ERROR: illegal channel ID: %s", s);
- return false;
- }
- }
- }
- else {
- esyslog("ERROR: unexpected tag while reading EPG data: %s", s);
- return false;
- }
- }
- return true;
- }
- return false;
-}
-
-// --- cSchedules ------------------------------------------------------------
-
-cSchedules::cSchedules()
-{
- pCurrentSchedule = NULL;
-}
-
-cSchedules::~cSchedules()
-{
-}
-/** */
-const cSchedule *cSchedules::AddChannelID(tChannelID channelid)
-{
- channelid.ClrRid();
- const cSchedule *p = GetSchedule(channelid);
- if (!p) {
- Add(new cSchedule(channelid));
- p = GetSchedule(channelid);
- }
- return p;
-}
-/** */
-const cSchedule *cSchedules::SetCurrentChannelID(tChannelID channelid)
-{
- channelid.ClrRid();
- pCurrentSchedule = AddChannelID(channelid);
- if (pCurrentSchedule)
- currentChannelID = channelid;
- return pCurrentSchedule;
-}
-/** */
-const cSchedule * cSchedules::GetSchedule() const
-{
- return pCurrentSchedule;
-}
-/** */
-const cSchedule * cSchedules::GetSchedule(tChannelID channelid) const
-{
- cSchedule *p;
-
- channelid.ClrRid();
- p = First();
- while (p != NULL)
- {
- if (p->GetChannelID() == channelid)
- return p;
- p = Next(p);
- }
-
- return NULL;
-}
-
-/** */
-void cSchedules::Cleanup()
-{
- cSchedule *p;
-
- p = First();
- while (p != NULL)
- {
- p->Cleanup(time(NULL));
- p = Next(p);
- }
-}
-
-/** */
-void cSchedules::Dump(FILE *f, const char *Prefix) const
-{
- for (cSchedule *p = First(); p; p = Next(p))
- p->Dump(f, Prefix);
-}
-
-/** */
-bool cSchedules::Read(FILE *f)
-{
- cMutexLock MutexLock;
- return cSchedule::Read(f, (cSchedules *)cSIProcessor::Schedules(MutexLock));
-}
-
-// --- cEIT ------------------------------------------------------------------
-
-class cEIT {
-private:
- cSchedules *schedules;
-public:
- cEIT(unsigned char *buf, int length, cSchedules *Schedules);
- ~cEIT();
- /** */
- int ProcessEIT(unsigned char *buffer, int CurrentSource);
-
-protected: // Protected methods
- /** returns true if this EIT covers a
-present/following information, false if it's
-schedule information */
- bool IsPresentFollowing();
-protected: // Protected attributes
- /** Table ID of this EIT struct */
- u_char tid;
-};
-
-cEIT::cEIT(unsigned char * buf, int length, cSchedules *Schedules)
-{
- tid = buf[0];
- schedules = Schedules;
-}
-
-cEIT::~cEIT()
-{
-}
-
-/** */
-int cEIT::ProcessEIT(unsigned char *buffer, int CurrentSource)
-{
- cEventInfo *pEvent, *rEvent = NULL;
- cSchedule *pSchedule, *rSchedule = NULL;
- struct LIST *VdrProgramInfos;
- struct VdrProgramInfo *VdrProgramInfo;
-
- if (!buffer)
- return -1;
-
- VdrProgramInfos = createVdrProgramInfos(buffer);
-
- if (VdrProgramInfos) {
- for (VdrProgramInfo = (struct VdrProgramInfo *) VdrProgramInfos->Head; VdrProgramInfo; VdrProgramInfo = (struct VdrProgramInfo *) xSucc (VdrProgramInfo)) {
- //XXX TODO use complete channel ID
- cChannel *channel = Channels.GetByServiceID(CurrentSource, VdrProgramInfo->ServiceID);
- tChannelID channelID = channel ? channel->GetChannelID() : tChannelID(CurrentSource, 0, 0, VdrProgramInfo->ServiceID);
- channelID.ClrRid();
- //XXX
- pSchedule = (cSchedule *)schedules->GetSchedule(channelID);
- if (!pSchedule) {
- schedules->Add(new cSchedule(channelID));
- pSchedule = (cSchedule *)schedules->GetSchedule(channelID);
- if (!pSchedule)
- break;
- }
- if (VdrProgramInfo->ReferenceServiceID) {
- rSchedule = (cSchedule *)schedules->GetSchedule(tChannelID(CurrentSource, 0, 0, VdrProgramInfo->ReferenceServiceID));
- if (!rSchedule)
- break;
- rEvent = (cEventInfo *)rSchedule->GetEvent((unsigned short)VdrProgramInfo->ReferenceEventID);
- if (!rEvent)
- break;
- }
- pEvent = (cEventInfo *)pSchedule->GetEvent((unsigned short)VdrProgramInfo->EventID, VdrProgramInfo->StartTime);
- if (!pEvent) {
- // If we don't have that event ID yet, we create a new one.
- // Otherwise we copy the information into the existing event anyway, because the data might have changed.
- pEvent = pSchedule->AddEvent(new cEventInfo(channelID, VdrProgramInfo->EventID));
- if (!pEvent)
- break;
- pEvent->SetTableID(tid);
- }
- else {
- // We have found an existing event, either through its event ID or its start time.
- // If the existing event has a zero table ID it was defined externally and shall
- // not be overwritten.
- if (pEvent->GetTableID() == 0x00)
- continue;
- // If the new event comes from a table that belongs to an "other TS" and the existing
- // one comes from an "actual TS" table, lets skip it.
- if ((tid == 0x4F || tid == 0x60 || tid == 0x61) && (pEvent->GetTableID() == 0x4E || pEvent->GetTableID() == 0x50 || pEvent->GetTableID() == 0x51))
- continue;
- }
- if (rEvent) {
- pEvent->SetTitle(rEvent->GetTitle());
- pEvent->SetSubtitle(rEvent->GetSubtitle());
- pEvent->SetExtendedDescription(rEvent->GetExtendedDescription());
- }
- else {
- pEvent->SetTableID(tid);
- pEvent->SetTitle(VdrProgramInfo->ShortName);
- pEvent->SetSubtitle(VdrProgramInfo->ShortText);
- pEvent->SetExtendedDescription(VdrProgramInfo->ExtendedName);
- //XXX kls 2001-09-22:
- //XXX apparently this never occurred, so I have simpified ExtendedDescription handling
- //XXX pEvent->AddExtendedDescription(VdrProgramInfo->ExtendedText);
- }
- pEvent->SetTime(VdrProgramInfo->StartTime);
- pEvent->SetDuration(VdrProgramInfo->Duration);
- pEvent->FixEpgBugs();
- if (IsPresentFollowing()) {
- if ((GetRunningStatus(VdrProgramInfo->Status) == RUNNING_STATUS_PAUSING) || (GetRunningStatus(VdrProgramInfo->Status) == RUNNING_STATUS_RUNNING))
- pSchedule->SetPresentEvent(pEvent);
- else if (GetRunningStatus(VdrProgramInfo->Status) == RUNNING_STATUS_AWAITING)
- pSchedule->SetFollowingEvent(pEvent);
- }
- }
- }
-
- xMemFreeAll(NULL);
- return 0;
-}
-
-/** returns true if this EIT covers a
-present/following information, false if it's
-schedule information */
-bool cEIT::IsPresentFollowing()
-{
- if (tid == 0x4e || tid == 0x4f)
- return true;
-
- return false;
}
-// --- cCaDescriptor ---------------------------------------------------------
+// --- cTDT ------------------------------------------------------------------
-class cCaDescriptor : public cListObject {
- friend class cSIProcessor;
+class cTDT : public SI::TDT {
private:
- int source;
- int transponder;
- int serviceId;
- int caSystem;
- unsigned int providerId;
- int caPid;
- int length;
- uchar *data;
+ static cMutex mutex;
public:
- cCaDescriptor(int Source, int Transponder, int ServiceId, int CaSystem, unsigned int ProviderId, int CaPid, int Length, uchar *Data);
- virtual ~cCaDescriptor();
- int Length(void) const { return length; }
- const uchar *Data(void) const { return data; }
+ cTDT(const u_char *Data);
};
-cCaDescriptor::cCaDescriptor(int Source, int Transponder, int ServiceId, int CaSystem, unsigned int ProviderId, int CaPid, int Length, uchar *Data)
-{
- source = Source;
- transponder = Transponder;
- serviceId = ServiceId;
- caSystem = CaSystem;
- providerId = ProviderId;
- caPid = CaPid;
- length = Length + 6;
- data = MALLOC(uchar, length);
- data[0] = DESCR_CA;
- data[1] = length - 2;
- data[2] = (caSystem >> 8) & 0xFF;
- data[3] = caSystem & 0xFF;
- data[4] = ((CaPid >> 8) & 0x1F) | 0xE0;
- data[5] = CaPid & 0xFF;
- if (Length)
- memcpy(&data[6], Data, Length);
-//#define DEBUG_CA_DESCRIPTORS 1
-#ifdef DEBUG_CA_DESCRIPTORS
- char buffer[1024];
- char *q = buffer;
- q += sprintf(q, "CAM: %04X %5d %5d %04X %6X %04X -", source, transponder, serviceId, caSystem, providerId, caPid);
- for (int i = 0; i < length; i++)
- q += sprintf(q, " %02X", data[i]);
- dsyslog(buffer);
-#endif
-}
+cMutex cTDT::mutex;
-cCaDescriptor::~cCaDescriptor()
+cTDT::cTDT(const u_char *Data)
+:SI::TDT(Data, false)
{
- free(data);
-}
-
-// --- cSIProcessor ----------------------------------------------------------
-
-#define MAX_FILTERS 20
-#define EPGDATAFILENAME "epg.data"
+ CheckParse();
-int cSIProcessor::numSIProcessors = 0;
-cSchedules *cSIProcessor::schedules = NULL;
-cMutex cSIProcessor::schedulesMutex;
-cList<cCaDescriptor> cSIProcessor::caDescriptors;
-cMutex cSIProcessor::caDescriptorsMutex;
-const char *cSIProcessor::epgDataFileName = EPGDATAFILENAME;
-time_t cSIProcessor::lastDump = time(NULL);
+ time_t sattim = getTime();
+ time_t loctim = time(NULL);
-/** */
-cSIProcessor::cSIProcessor(const char *FileName)
-{
- fileName = strdup(FileName);
- masterSIProcessor = numSIProcessors == 0; // the first one becomes the 'master'
- currentSource = 0;
- currentTransponder = 0;
- statusCount = 0;
- pmtIndex = 0;
- pmtPid = 0;
- filters = NULL;
- if (!numSIProcessors++) { // the first one creates them
- schedules = new cSchedules;
- }
- filters = (SIP_FILTER *)calloc(MAX_FILTERS, sizeof(SIP_FILTER));
- SetStatus(true);
- Start();
-}
-
-cSIProcessor::~cSIProcessor()
-{
- if (masterSIProcessor)
- ReportEpgBugFixStats();
- active = false;
- Cancel(3);
- ShutDownFilters();
- free(filters);
- if (!--numSIProcessors) { // the last one deletes them
- delete schedules;
- }
- free(fileName);
-}
-
-const cSchedules *cSIProcessor::Schedules(cMutexLock &MutexLock)
-{
- if (MutexLock.Lock(&schedulesMutex))
- return schedules;
- return NULL;
-}
-
-bool cSIProcessor::Read(FILE *f)
-{
- bool OwnFile = f == NULL;
- if (OwnFile) {
- const char *FileName = GetEpgDataFileName();
- if (access(FileName, R_OK) == 0) {
- dsyslog("reading EPG data from %s", FileName);
- if ((f = fopen(FileName, "r")) == NULL) {
- LOG_ERROR;
- return false;
- }
- }
- else
- return false;
+ if (abs(sattim - loctim) > 2) {
+ mutex.Lock();
+ isyslog("System Time = %s (%ld)\n", ctime(&loctim), loctim);
+ isyslog("Local Time = %s (%ld)\n", ctime(&sattim), sattim);
+ if (stime(&sattim) < 0)
+ esyslog("ERROR while setting system time: %m");
+ mutex.Unlock();
}
- bool result = cSchedules::Read(f);
- if (OwnFile)
- fclose(f);
- return result;
-}
-
-void cSIProcessor::Clear(void)
-{
- cMutexLock MutexLock(&schedulesMutex);
- delete schedules;
- schedules = new cSchedules;
-}
-
-void cSIProcessor::SetEpgDataFileName(const char *FileName)
-{
- epgDataFileName = NULL;
- if (FileName)
- epgDataFileName = strdup(DirectoryOk(FileName) ? AddDirectory(FileName, EPGDATAFILENAME) : FileName);
-}
-
-const char *cSIProcessor::GetEpgDataFileName(void)
-{
- if (epgDataFileName)
- return *epgDataFileName == '/' ? epgDataFileName : AddDirectory(VideoDirectory, epgDataFileName);
- return NULL;
-}
-
-void cSIProcessor::SetStatus(bool On)
-{
- LOCK_THREAD;
- statusCount++;
- ShutDownFilters();
- pmtIndex = 0;
- pmtPid = 0;
- if (On)
- {
- AddFilter(0x00, 0x00); // PAT
- AddFilter(0x14, 0x70); // TDT
- AddFilter(0x12, 0x4e, 0xfe); // event info, actual(0x4e)/other(0x4f) TS, present/following
- AddFilter(0x12, 0x50, 0xfe); // event info, actual TS, schedule(0x50)/schedule for another 4 days(0x51)
- AddFilter(0x12, 0x60, 0xfe); // event info, other TS, schedule(0x60)/schedule for another 4 days(0x61)
- }
-}
-
-#define PMT_SCAN_TIMEOUT 10 // seconds
-
-/** use the vbi device to parse all relevant SI
-information and let the classes corresponding
-to the tables write their information to the disk */
-void cSIProcessor::Action()
-{
- dsyslog("EIT processing thread started (pid=%d)%s", getpid(), masterSIProcessor ? " - master" : "");
-
- time_t lastCleanup = time(NULL);
- time_t lastPmtScan = time(NULL);
-
- int oldStatusCount = 0;
- active = true;
-
- while(active)
- {
- if (masterSIProcessor)
- {
- time_t now = time(NULL);
- struct tm tm_r;
- struct tm *ptm = localtime_r(&now, &tm_r);
- if (now - lastCleanup > 3600 && ptm->tm_hour == 5)
- {
- cMutexLock MutexLock(&schedulesMutex);
- isyslog("cleaning up schedules data");
- schedules->Cleanup();
- lastCleanup = now;
- ReportEpgBugFixStats(true);
- }
- if (epgDataFileName && now - lastDump > 600)
- {
- cMutexLock MutexLock(&schedulesMutex);
- cSafeFile f(GetEpgDataFileName());
- if (f.Open()) {
- schedules->Dump(f);
- f.Close();
- }
- else
- LOG_ERROR;
- lastDump = now;
- }
- }
-
- // set up pfd structures for all active filter
- Lock();
- pollfd pfd[MAX_FILTERS];
- int NumUsedFilters = 0;
- for (int a = 0; a < MAX_FILTERS ; a++)
- {
- if (filters[a].inuse)
- {
- pfd[NumUsedFilters].fd = filters[a].handle;
- pfd[NumUsedFilters].events = POLLIN;
- NumUsedFilters++;
- }
- }
- oldStatusCount = statusCount;
- Unlock();
-
- // wait until data becomes ready from the bitfilter
- if (poll(pfd, NumUsedFilters, 1000) != 0)
- {
- for (int aa = 0; aa < NumUsedFilters; aa++)
- {
- if (pfd[aa].revents & POLLIN)
- {
- int a;
- for (a = 0; a < MAX_FILTERS; a++) {
- if (pfd[aa].fd == filters[a].handle)
- break;
- }
- if (a >= MAX_FILTERS || !filters[a].inuse) // filter no longer available
- continue;
- // read section
- unsigned char buf[4096]; // max. allowed size for any EIT section
- int r = safe_read(filters[a].handle, buf, sizeof(buf));
- if (r > 3) // minimum number of bytes necessary to get section length
- {
- int seclen = (((buf[1] & 0x0F) << 8) | (buf[2] & 0xFF)) + 3;
- int pid = filters[a].pid;
- if (seclen == r)
- {
- //dsyslog("Received pid 0x%04X with table ID 0x%02X and length of %4d\n", pid, buf[0], seclen);
- cMutexLock MutexLock(&schedulesMutex); // since the xMem... stuff is not thread safe, we need to use a "global" mutex
- LOCK_THREAD;
- if (statusCount != oldStatusCount)
- break;
- switch (pid)
- {
- case 0x00:
- if (buf[0] == 0x00)
- {
- if (pmtPid && time(NULL) - lastPmtScan > PMT_SCAN_TIMEOUT) {
- DelFilter(pmtPid, 0x02);
- pmtPid = 0;
- pmtIndex++;
- lastPmtScan = time(NULL);
- }
- if (!pmtPid) {
- struct LIST *pat = siParsePAT(buf);
- if (pat) {
- int Index = 0;
- for (struct Program *prg = (struct Program *)pat->Head; prg; prg = (struct Program *)xSucc(prg)) {
- if (prg->ProgramID) {
- if (Index++ == pmtIndex) {
- pmtPid = prg->NetworkPID;
- AddFilter(pmtPid, 0x02);
- break;
- }
- }
- }
- if (!pmtPid)
- pmtIndex = 0;
- }
- xMemFreeAll(NULL);
- }
- }
- break;
- case 0x14:
- if (buf[0] == 0x70)
- {
- if (Setup.SetSystemTime && Setup.TimeTransponder && ISTRANSPONDER(currentTransponder, Setup.TimeTransponder))
- {
- cTDT ctdt((tdt_t *)buf);
- ctdt.SetSystemTime();
- }
- }
- break;
-
- case 0x12:
- if (buf[0] != 0x72)
- {
- cEIT ceit(buf, seclen, schedules);
- ceit.ProcessEIT(buf, currentSource);
- }
- /*else
- dsyslog("Received stuffing section in EIT\n");
- */
- break;
-
- default: {
- if (pid == pmtPid && buf[0] == 0x02 && currentSource && currentTransponder) {
- struct Pid *pi = siParsePMT(buf);
- if (pi) {
- struct Descriptor *d;
- for (d = (struct Descriptor *)pi->Descriptors->Head; d; d = (struct Descriptor *)xSucc(d))
- NewCaDescriptor(d, pi->ProgramID);
- // Also scan the PidInfo list for descriptors - some broadcasts send them only here.
- for (struct PidInfo *p = (struct PidInfo *)pi->InfoList->Head; p; p = (struct PidInfo *)xSucc(p)) {
- for (d = (struct Descriptor *)p->Descriptors->Head; d; d = (struct Descriptor *)xSucc(d))
- NewCaDescriptor(d, pi->ProgramID);
- }
- }
- xMemFreeAll(NULL);
- lastPmtScan = 0; // this triggers the next scan
- }
- }
- break;
- }
- }
- /*
- else
- dsyslog("read incomplete section - seclen = %d, r = %d", seclen, r);
- */
- }
- }
- }
- }
- }
-
- dsyslog("EIT processing thread ended (pid=%d)%s", getpid(), masterSIProcessor ? " - master" : "");
}
-/** Add a filter with packet identifier pid and
-table identifer tid */
-bool cSIProcessor::AddFilter(unsigned short pid, u_char tid, u_char mask)
-{
- dmx_sct_filter_params sctFilterParams;
- memset(&sctFilterParams, 0, sizeof(sctFilterParams));
- sctFilterParams.pid = pid;
- sctFilterParams.timeout = 0;
- sctFilterParams.flags = DMX_IMMEDIATE_START;
- sctFilterParams.filter.filter[0] = tid;
- sctFilterParams.filter.mask[0] = mask;
+// --- cEitFilter ------------------------------------------------------------
- for (int a = 0; a < MAX_FILTERS; a++)
- {
- if (!filters[a].inuse)
- {
- filters[a].pid = pid;
- filters[a].tid = tid;
- if ((filters[a].handle = open(fileName, O_RDWR | O_NONBLOCK)) >= 0)
- {
- if (ioctl(filters[a].handle, DMX_SET_FILTER, &sctFilterParams) >= 0)
- filters[a].inuse = true;
- else
- {
- esyslog("ERROR: can't set filter (pid=%d, tid=%02X)", pid, tid);
- close(filters[a].handle);
- return false;
- }
- // dsyslog("Registered filter handle %04x, pid = %02d, tid = %02d", filters[a].handle, filters[a].pid, filters[a].tid);
- }
- else
- {
- esyslog("ERROR: can't open filter handle");
- return false;
- }
- return true;
- }
- }
- esyslog("ERROR: too many filters");
-
- return false;
-}
-
-bool cSIProcessor::DelFilter(unsigned short pid, u_char tid)
+cEitFilter::cEitFilter(void)
{
- for (int a = 0; a < MAX_FILTERS; a++)
- {
- if (filters[a].inuse && filters[a].pid == pid && filters[a].tid == tid)
- {
- close(filters[a].handle);
- // dsyslog("Deregistered filter handle %04x, pid = %02d, tid = %02d", filters[a].handle, filters[a].pid, filters[a].tid);
- filters[a].inuse = false;
- return true;
- }
- }
- return false;
+ Set(0x12, 0x4E, 0xFE); // event info, actual(0x4E)/other(0x4F) TS, present/following
+ Set(0x12, 0x50, 0xF0); // event info, actual TS, schedule(0x50)/schedule for future days(0x5X)
+ Set(0x12, 0x60, 0xF0); // event info, other TS, schedule(0x60)/schedule for future days(0x6X)
+ Set(0x14, 0x70); // TDT
}
-/** */
-bool cSIProcessor::ShutDownFilters(void)
+void cEitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
{
- for (int a = 0; a < MAX_FILTERS; a++)
- {
- if (filters[a].inuse)
- {
- close(filters[a].handle);
- // dsyslog("Deregistered filter handle %04x, pid = %02d, tid = %02d", filters[a].handle, filters[a].pid, filters[a].tid);
- filters[a].inuse = false;
- }
- }
-
- return true; // there's no real 'boolean' to return here...
-}
-
-/** */
-void cSIProcessor::SetCurrentTransponder(int CurrentSource, int CurrentTransponder)
-{
- currentSource = CurrentSource;
- currentTransponder = CurrentTransponder;
-}
-
-/** */
-bool cSIProcessor::SetCurrentChannelID(tChannelID channelid)
-{
- cMutexLock MutexLock(&schedulesMutex);
- return schedules ? schedules->SetCurrentChannelID(channelid) : false;
-}
-
-void cSIProcessor::TriggerDump(void)
-{
- cMutexLock MutexLock(&schedulesMutex);
- lastDump = 0;
-}
-
-void cSIProcessor::NewCaDescriptor(struct Descriptor *d, int ServiceId)
-{
- if (DescriptorTag(d) == DESCR_CA) {
- struct CaDescriptor *cd = (struct CaDescriptor *)d;
- cMutexLock MutexLock(&caDescriptorsMutex);
-
- for (cCaDescriptor *ca = caDescriptors.First(); ca; ca = caDescriptors.Next(ca)) {
- if (ca->source == currentSource && ca->transponder == currentTransponder && ca->serviceId == ServiceId && ca->caSystem == cd->CA_type && ca->providerId == cd->ProviderID && ca->caPid == cd->CA_PID)
- return;
+ switch (Pid) {
+ case 0x12: {
+ cSchedulesLock SchedulesLock(true, 10);
+ cSchedules *Schedules = (cSchedules *)cSchedules::Schedules(SchedulesLock);
+ if (Schedules)
+ cEIT EIT(Schedules, Source(), Tid, Data);
}
- caDescriptors.Add(new cCaDescriptor(currentSource, currentTransponder, ServiceId, cd->CA_type, cd->ProviderID, cd->CA_PID, cd->DataLength, cd->Data));
- //XXX update???
- }
-}
-
-int cSIProcessor::GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data)
-{
- if (!CaSystemIds || !*CaSystemIds)
- return 0;
- if (BufSize > 0 && Data) {
- cMutexLock MutexLock(&caDescriptorsMutex);
- int length = 0;
- for (cCaDescriptor *d = caDescriptors.First(); d; d = caDescriptors.Next(d)) {
- if (d->source == Source && d->transponder == Transponder && d->serviceId == ServiceId) {
- const unsigned short *caids = CaSystemIds;
- do {
- if (d->caSystem == *caids) {
- if (length + d->Length() <= BufSize) {
- memcpy(Data + length, d->Data(), d->Length());
- length += d->Length();
- }
- else
- return -1;
- }
- } while (*++caids);
- }
+ break;
+ case 0x14: {
+ if (Setup.SetSystemTime && Setup.TimeTransponder && ISTRANSPONDER(Transponder(), Setup.TimeTransponder))
+ cTDT TDT(Data);
}
- return length;
- }
- return -1;
+ break;
+ }
}