19#define RUNNINGSTATUSTIMEOUT 30
20#define EPGDATAWRITEDELTA 600
33 unsigned int Stream, Type;
70 esyslog(
"ERROR: out of memory");
102 Stream == 2 && (
components[i].type < 5) == (Type < 5)
259 char vpsbuf[64] =
"";
290 switch (Content & 0xF0) {
292 switch (Content & 0x0F) {
294 case 0x00:
return tr(
"Content$Movie/Drama");
295 case 0x01:
return tr(
"Content$Detective/Thriller");
296 case 0x02:
return tr(
"Content$Adventure/Western/War");
297 case 0x03:
return tr(
"Content$Science Fiction/Fantasy/Horror");
298 case 0x04:
return tr(
"Content$Comedy");
299 case 0x05:
return tr(
"Content$Soap/Melodrama/Folkloric");
300 case 0x06:
return tr(
"Content$Romance");
301 case 0x07:
return tr(
"Content$Serious/Classical/Religious/Historical Movie/Drama");
302 case 0x08:
return tr(
"Content$Adult Movie/Drama");
306 switch (Content & 0x0F) {
308 case 0x00:
return tr(
"Content$News/Current Affairs");
309 case 0x01:
return tr(
"Content$News/Weather Report");
310 case 0x02:
return tr(
"Content$News Magazine");
311 case 0x03:
return tr(
"Content$Documentary");
312 case 0x04:
return tr(
"Content$Discussion/Interview/Debate");
316 switch (Content & 0x0F) {
318 case 0x00:
return tr(
"Content$Show/Game Show");
319 case 0x01:
return tr(
"Content$Game Show/Quiz/Contest");
320 case 0x02:
return tr(
"Content$Variety Show");
321 case 0x03:
return tr(
"Content$Talk Show");
325 switch (Content & 0x0F) {
327 case 0x00:
return tr(
"Content$Sports");
328 case 0x01:
return tr(
"Content$Special Event");
329 case 0x02:
return tr(
"Content$Sport Magazine");
330 case 0x03:
return tr(
"Content$Football/Soccer");
331 case 0x04:
return tr(
"Content$Tennis/Squash");
332 case 0x05:
return tr(
"Content$Team Sports");
333 case 0x06:
return tr(
"Content$Athletics");
334 case 0x07:
return tr(
"Content$Motor Sport");
335 case 0x08:
return tr(
"Content$Water Sport");
336 case 0x09:
return tr(
"Content$Winter Sports");
337 case 0x0A:
return tr(
"Content$Equestrian");
338 case 0x0B:
return tr(
"Content$Martial Sports");
342 switch (Content & 0x0F) {
344 case 0x00:
return tr(
"Content$Children's/Youth Programme");
345 case 0x01:
return tr(
"Content$Pre-school Children's Programme");
346 case 0x02:
return tr(
"Content$Entertainment Programme for 6 to 14");
347 case 0x03:
return tr(
"Content$Entertainment Programme for 10 to 16");
348 case 0x04:
return tr(
"Content$Informational/Educational/School Programme");
349 case 0x05:
return tr(
"Content$Cartoons/Puppets");
353 switch (Content & 0x0F) {
355 case 0x00:
return tr(
"Content$Music/Ballet/Dance");
356 case 0x01:
return tr(
"Content$Rock/Pop");
357 case 0x02:
return tr(
"Content$Serious/Classical Music");
358 case 0x03:
return tr(
"Content$Folk/Traditional Music");
359 case 0x04:
return tr(
"Content$Jazz");
360 case 0x05:
return tr(
"Content$Musical/Opera");
361 case 0x06:
return tr(
"Content$Ballet");
365 switch (Content & 0x0F) {
367 case 0x00:
return tr(
"Content$Arts/Culture");
368 case 0x01:
return tr(
"Content$Performing Arts");
369 case 0x02:
return tr(
"Content$Fine Arts");
370 case 0x03:
return tr(
"Content$Religion");
371 case 0x04:
return tr(
"Content$Popular Culture/Traditional Arts");
372 case 0x05:
return tr(
"Content$Literature");
373 case 0x06:
return tr(
"Content$Film/Cinema");
374 case 0x07:
return tr(
"Content$Experimental Film/Video");
375 case 0x08:
return tr(
"Content$Broadcasting/Press");
376 case 0x09:
return tr(
"Content$New Media");
377 case 0x0A:
return tr(
"Content$Arts/Culture Magazine");
378 case 0x0B:
return tr(
"Content$Fashion");
382 switch (Content & 0x0F) {
384 case 0x00:
return tr(
"Content$Social/Political/Economics");
385 case 0x01:
return tr(
"Content$Magazine/Report/Documentary");
386 case 0x02:
return tr(
"Content$Economics/Social Advisory");
387 case 0x03:
return tr(
"Content$Remarkable People");
391 switch (Content & 0x0F) {
393 case 0x00:
return tr(
"Content$Education/Science/Factual");
394 case 0x01:
return tr(
"Content$Nature/Animals/Environment");
395 case 0x02:
return tr(
"Content$Technology/Natural Sciences");
396 case 0x03:
return tr(
"Content$Medicine/Physiology/Psychology");
397 case 0x04:
return tr(
"Content$Foreign Countries/Expeditions");
398 case 0x05:
return tr(
"Content$Social/Spiritual Sciences");
399 case 0x06:
return tr(
"Content$Further Education");
400 case 0x07:
return tr(
"Content$Languages");
404 switch (Content & 0x0F) {
406 case 0x00:
return tr(
"Content$Leisure/Hobbies");
407 case 0x01:
return tr(
"Content$Tourism/Travel");
408 case 0x02:
return tr(
"Content$Handicraft");
409 case 0x03:
return tr(
"Content$Motoring");
410 case 0x04:
return tr(
"Content$Fitness & Health");
411 case 0x05:
return tr(
"Content$Cooking");
412 case 0x06:
return tr(
"Content$Advertisement/Shopping");
413 case 0x07:
return tr(
"Content$Gardening");
417 switch (Content & 0x0F) {
418 case 0x00:
return tr(
"Content$Original Language");
419 case 0x01:
return tr(
"Content$Black & White");
420 case 0x02:
return tr(
"Content$Unpublished");
421 case 0x03:
return tr(
"Content$Live Broadcast");
456 strftime(buf,
sizeof(buf),
"%d.%m. %R", localtime_r(&
vps, &tm_r));
465 fprintf(f,
"%sT %s\n", Prefix,
title);
467 fprintf(f,
"%sS %s\n", Prefix,
shortText);
474 fprintf(f,
"%sG", Prefix);
482 fprintf(f,
"%sX 0 00 %s\n", Prefix,
language);
484 for (
int i = 0; i <
components->NumComponents(); i++) {
486 fprintf(f,
"%sX %s\n", Prefix, *p->
ToString());
490 fprintf(f,
"%sV %jd\n", Prefix, intmax_t(
vps));
493 fprintf(f,
"%s@ %s\n", Prefix,
aux);
497 fprintf(f,
"%se\n", Prefix);
516 int c = strtol(t, &tail, 16);
517 if (0x00 < c && c <= 0xFF) {
530 if (1 == sscanf(t,
"0 00 %3s", l))
539 case 'V':
SetVps(atol(t));
544 default:
esyslog(
"ERROR: unexpected tag while reading EPG data: %s", s);
556 while ((s = ReadLine.
Read(f)) != NULL) {
560 case 'E':
if (!Event) {
567 if (n >= 3 && n <= 5) {
586 case 'e':
if (Event && !Event->
Title())
592 default:
if (Event && !Event->
Parse(s)) {
593 esyslog(
"ERROR: EPG data problem in line %d", Line);
598 esyslog(
"ERROR: unexpected end of file while reading EPG data");
603#define MAXEPGBUGFIXSTATS 13
604#define MAXEPGBUGFIXCHANS 100
620 for (; i < p->
n; i++) {
631 if (
Setup.EPGBugfixLevel > 0) {
632 static time_t LastReport = 0;
633 time_t now = time(NULL);
634 if (now - LastReport > 3600 || Force) {
637 struct tm *ptm = localtime_r(&now, &tm_r);
638 if (ptm->tm_hour != 5)
643 bool GotHits =
false;
646 const char *delim =
" ";
649 bool PrintedStats =
false;
653 for (
int c = 0; c < p->
n; c++) {
656 dsyslog(
"=====================");
657 dsyslog(
"EPG bugfix statistics");
658 dsyslog(
"=====================");
659 dsyslog(
"IF SOMEBODY WHO IS IN CHARGE OF THE EPG DATA FOR ONE OF THE LISTED");
660 dsyslog(
"CHANNELS READS THIS: PLEASE TAKE A LOOK AT THE FUNCTION cEvent::FixEpgBugs()");
661 dsyslog(
"IN VDR/epg.c TO LEARN WHAT'S WRONG WITH YOUR DATA, AND FIX IT!");
662 dsyslog(
"=====================");
667 q += snprintf(q,
sizeof(buffer) - (q - buffer),
"%-3d %-4d", i, p->
hits);
670 q += snprintf(q,
sizeof(buffer) - (q - buffer),
"%s%s", delim, Channel->Name());
672 if (q - buffer > 80) {
673 q += snprintf(q,
sizeof(buffer) - (q - buffer),
"%s...", delim);
684 dsyslog(
"=====================");
695 if (l == 2 && *p == 0xC2)
697 if (*p == 0x86 || *p == 0x87 || *p == 0x0D) {
698 memmove(s, p + 1, len - l + 1);
716 if (
Setup.EPGBugfixLevel == 0)
731 const char *delim =
"\".";
732 char *e = strstr(p + 1, delim);
735 char *s = strdup(p + 1);
736 char *d = strdup(e + strlen(delim));
790 if (
Setup.EPGBugfixLevel <= 1)
801#define MAX_USEFUL_EPISODE_LENGTH 40
836 if (
Setup.EPGBugfixLevel <= 2)
842 for (
int i = 0; i <
components->NumComponents(); i++) {
865 case 0x08: p->
description = strdup(
">16:9");
break;
867 case 0x0D: p->
description = strdup(
"HD 4:3");
break;
871 case 0x0F: p->
description = strdup(
"HD 16:9");
break;
873 case 0x10: p->
description = strdup(
"HD >16:9");
break;
892 case 0x05: p->
description = strdup(
"Dolby Digital");
break;
909 if (
char *p = strstr(
shortText,
"\\n")) {
928 for (
int i = 0; i <
components->NumComponents(); i++) {
947 events.SetUseGarbageCollector();
970 if ((TableId & 0xF0) == 0x50)
1021 time_t now = time(NULL);
1023 if (p->StartTime() <= now)
1025 else if (p->StartTime() > now + 3600)
1039 time_t now = time(NULL);
1063 time_t delta = INT_MAX;
1065 time_t dt = Time - p->StartTime();
1066 if (dt >= 0 && dt < delta && p->EndTime() >= Time) {
1079 p->SetRunningStatus(RunningStatus, Channel);
1103 p->SetVersion(0xFF);
1115 if (SegmentStart > 0 && SegmentEnd > 0) {
1122 if ((p->
TableID() > 0x4E || TableID == 0x4E) && (p->
TableID() != TableID || p->
Version() != Version)) {
1138 if (n && n->
StartTime() == p->StartTime())
1159 while ((Event =
events.First()) != NULL) {
1170 fprintf(f,
"%sC %s %s\n", Prefix, *Channel->GetChannelID().ToString(), Channel->Name());
1193 default:
esyslog(
"ERROR: unknown DumpMode %d (%s %d)", DumpMode, __FUNCTION__, __LINE__);
1195 fprintf(f,
"%sc\n", Prefix);
1205 while ((s = ReadLine.
Read(f)) != NULL) {
1209 char *p = strchr(s,
' ');
1222 esyslog(
"ERROR: invalid channel ID: %s", s);
1228 esyslog(
"ERROR: unexpected tag in line %d while reading EPG data: %s", Line, s);
1244 virtual void Action(
void)
override;
1252:
cThread(
"epg data writer", true)
1268 time_t now = time(NULL);
1269 for (
cSchedule *p = Schedules->First(); p; p = Schedules->
Next(p))
1312 time_t now = time(NULL);
1325 for (
cSchedule *Schedule = Schedules->First(); Schedule; Schedule = Schedules->Next(Schedule))
1326 Schedule->ResetVersions();
1344 for (
const cSchedule *p = Schedules->First(); p; p = Schedules->Next(p))
1345 p->Dump(Channels, f, Prefix, DumpMode, AtTime);
1355 bool OwnFile = f == NULL;
1374 for (
cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1375 if (
const cSchedule *Schedule = Channel->schedule) {
1376 if (!Schedule->ChannelID().Valid())
1377 Channel->schedule = NULL;
1379 Schedules->GetSchedule(Channel);
1400 if (p->ChannelID() == ChannelID)
1416 Channel->
schedule = &DummySchedule;
1417 if (Channel->
schedule == &DummySchedule && AddIfMissing) {
1462 if (eh->IgnoreChannel(Channel))
1471 if (eh->HandleEitEvent(Schedule, EitEvent, TableID, Version))
1480 if (eh->HandledExternally(Channel))
1489 if (eh->IsUpdate(EventID, StartTime, TableID, Version))
1498 if (eh->SetEventID(Event, EventID))
1507 if (eh->SetTitle(Event, Title))
1516 if (eh->SetLanguage(Event, Language))
1525 if (eh->SetShortText(Event, ShortText))
1534 if (eh->SetDescription(Event, Description))
1543 if (eh->SetContents(Event, Contents))
1552 if (eh->SetParentalRating(Event, ParentalRating))
1561 if (eh->SetStartTime(Event, StartTime))
1570 if (eh->SetDuration(Event, Duration))
1579 if (eh->SetVps(Event, Vps))
1588 if (eh->SetComponents(Event, Components))
1597 if (eh->FixEpgBugs(Event))
1606 if (eh->HandleEvent(Event))
1614 if (eh->SortSchedule(Schedule))
1623 if (eh->DropOutdated(Schedule, SegmentStart, SegmentEnd, TableID, Version))
1626 Schedule->
DropOutdated(SegmentStart, SegmentEnd, TableID, Version);
1632 if (!eh->BeginSegmentTransfer(Channel,
false))
1641 if (eh->EndSegmentTransfer(Modified,
false))
#define LOCK_CHANNELS_READ
#define LOCK_CHANNELS_WRITE
const char * Name(void) const
tChannelID GetChannelID(void) const
const cSchedule * schedule
const cChannel * GetByChannelID(tChannelID ChannelID, bool TryWithoutRid=false, bool TryWithoutPolarization=false) const
tComponent * GetComponent(int Index, uchar Stream, uchar Type)
void SetComponent(int Index, const char *s)
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
virtual ~cEpgHandler() override
cEpgHandler(void)
Constructs a new EPG handler and adds it to the list of EPG handlers.
void SortSchedule(cSchedule *Schedule)
void EndSegmentTransfer(bool Modified)
bool IgnoreChannel(const cChannel *Channel)
bool HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version)
void SetStartTime(cEvent *Event, time_t StartTime)
void SetTitle(cEvent *Event, const char *Title)
void DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version)
bool IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version)
void FixEpgBugs(cEvent *Event)
void HandleEvent(cEvent *Event)
void SetComponents(cEvent *Event, cComponents *Components)
void SetVps(cEvent *Event, time_t Vps)
void SetParentalRating(cEvent *Event, int ParentalRating)
bool BeginSegmentTransfer(const cChannel *Channel)
bool HandledExternally(const cChannel *Channel)
void SetContents(cEvent *Event, uchar *Contents)
void SetShortText(cEvent *Event, const char *ShortText)
void SetDuration(cEvent *Event, int Duration)
void SetDescription(cEvent *Event, const char *Description)
void SetLanguage(cEvent *Event, const char *Language)
void SetEventID(cEvent *Event, tEventID EventID)
const char * ShortText(void) const
cString ToDescr(void) const
static const char * ContentToString(uchar Content)
uchar TableID(void) const
void SetAux(const char *Aux)
time_t EndTime(void) const
const char * Language(void) const
static cMutex numTimersMutex
cString GetDateString(void) const
int RunningStatus(void) const
const cComponents * Components(void) const
uchar Contents(int i=0) const
const char * Description(void) const
bool IsRunning(bool OrAboutToStart=false) const
void SetRunningStatus(int RunningStatus, const cChannel *Channel=NULL)
void IncNumTimers(void) const
int ParentalRating(void) const
time_t StartTime(void) const
tChannelID ChannelID(void) const
void SetLanguage(const char *Language)
static bool Read(FILE *f, cSchedule *Schedule, int &Line)
const char * Aux(void) const
void SetShortText(const char *ShortText)
cString GetTimeString(void) const
const char * Title(void) const
void DecNumTimers(void) const
tEventID EventID(void) const
const cSchedule * Schedule(void) const
void SetStartTime(time_t StartTime)
bool HasTimer(void) const
void SetComponents(cComponents *Components)
void SetEventID(tEventID EventID)
cString GetEndTimeString(void) const
cString GetVpsString(void) const
void SetVersion(uchar Version)
void Dump(FILE *f, const char *Prefix="", bool InfoOnly=false) const
void SetDuration(int Duration)
void SetContents(uchar *Contents)
uchar Version(void) const
void SetTitle(const char *Title)
char language[MAXLANGCODE1]
void SetTableID(uchar TableID)
uchar contents[MaxEventContents]
cString GetParentalRatingString(void) const
void SetDescription(const char *Description)
void SetParentalRating(int ParentalRating)
virtual int Compare(const cListObject &ListObject) const override
Must return 0 if this object is equal to ListObject, a positive value if it is "greater",...
void Add(cListObject *Object, cListObject *After=NULL)
cListObject(const cListObject &ListObject)
cListObject * Next(void) const
const cSchedule * First(void) const
cList(const char *NeedsLocking=NULL)
const cSchedule * Next(const cSchedule *Object) const
const cEvent * GetPresentEvent(void) const
cHash< cEvent > eventsHashID
void SetRunningStatus(cEvent *Event, int RunningStatus, const cChannel *Channel=NULL)
void UnhashEvent(cEvent *Event)
const cEvent * GetEventAround(time_t Time) const
const cEvent * GetEventByTime(time_t StartTime) const
void DecNumTimers(void) const
bool OnActualTp(uchar TableId)
void DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version)
static bool Read(FILE *f, cSchedules *Schedules)
cSchedule(tChannelID ChannelID)
void SetPresentSeen(void)
static cMutex numTimersMutex
void ClrRunningStatus(cChannel *Channel=NULL)
const cEvent * GetEventById(tEventID EventID) const
void DelEvent(cEvent *Event)
void HashEvent(cEvent *Event)
tChannelID ChannelID(void) const
cHash< cEvent > eventsHashStartTime
void IncNumTimers(void) const
cEvent * AddEvent(cEvent *Event)
void Dump(const cChannels *Channels, FILE *f, const char *Prefix="", eDumpMode DumpMode=dmAll, time_t AtTime=0) const
const cEvent * GetFollowingEvent(void) const
static cSchedules * GetSchedulesWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of schedules for write access.
const cSchedule * GetSchedule(tChannelID ChannelID) const
static const cSchedules * GetSchedulesRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of schedules for read access.
static bool Dump(FILE *f=NULL, const char *Prefix="", eDumpMode DumpMode=dmAll, time_t AtTime=0)
static void SetEpgDataFileName(const char *FileName)
static void Cleanup(bool Force=false)
static char * epgDataFileName
static void ResetVersions(void)
cSchedule * AddSchedule(tChannelID ChannelID)
static bool Read(FILE *f=NULL)
static cSchedules schedules
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
static cString sprintf(const char *fmt,...) __attribute__((format(printf
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
#define EPGDATAWRITEDELTA
static void StripControlCharacters(char *s)
tEpgBugFixStats EpgBugFixStats[MAXEPGBUGFIXSTATS]
void ReportEpgBugFixStats(bool Force)
#define MAXEPGBUGFIXCHANS
#define MAX_USEFUL_EPISODE_LENGTH
#define RUNNINGSTATUSTIMEOUT
#define MAXEPGBUGFIXSTATS
static void EpgBugFixStat(int Number, tChannelID ChannelID)
static cEpgDataWriter EpgDataWriter
#define LOCK_SCHEDULES_READ
#define LOCK_SCHEDULES_WRITE
@ ecgSocialPoliticalEconomics
@ RunningStatusNotRunning
@ RunningStatusStartsInAFewSeconds
tChannelID & ClrRid(void)
static const tChannelID InvalidID
static tChannelID FromString(const char *s)
bool FromString(const char *s)
char language[MAXLANGCODE2]
tChannelID channelIDs[MAXEPGBUGFIXCHANS]