From 430284a8a78b536151b829d7ba63450b85cb2f90 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sat, 23 Feb 2002 17:11:19 +0100 Subject: [PATCH] Reading 'epg.data' at startup --- FORMATS | 34 +++++++++++++++ HISTORY | 5 ++- eit.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- eit.h | 10 ++++- vdr.c | 5 ++- 5 files changed, 167 insertions(+), 12 deletions(-) diff --git a/FORMATS b/FORMATS index 9f1a7d99..0da29397 100644 --- a/FORMATS +++ b/FORMATS @@ -180,3 +180,37 @@ Video Disk Recorder File Formats for audio 2 (if available). Dolby Digital data is stored in packets with ids 0xBD. +* epg.data + + This file contains the EPG data in an easily parsable format. The first + character of each line defines what kind of data this line contains. + + The following tag characters are defined: + + C + E + T + S <subtitle> + D <description> + e + c + + Lowercase characters mark the end of a sequence that was started by the + corresponding uppercase character. The outer frame consists of a sequence + of one or more 'C'...'c' (Channel) entries. Inside these any number of + 'E'...'e' (Event) entries are allowed. The 'T', 'S' and 'D' entries are + optional (although every event should at least have a 'T' entry). + + <service id> is the "program number" as defined in 'channels.conf' + <channel name> is the "name" as in 'channels.conf' (for information only) + <start time> is the time (as a time_t integer) in UTC when this event starts + <duration> is the time (in seconds) that this event will take + <table id> is a hex number that indicates the table this event is contained + in (if this is left empty or 0 this event will not be overwritten + or modified by data that comes from the DVB stream) + <title> is the title of the event + <subtitle> is the subtitle (typically the name of the episode etc.) + <description> is the description of the event + + This file will be read at program startup in order to restore the results of + previous EPG scans. diff --git a/HISTORY b/HISTORY index 1e133b2a..58ab9cc5 100644 --- a/HISTORY +++ b/HISTORY @@ -997,7 +997,7 @@ Video Disk Recorder Revision History - If a recording has no episode title, the trailing '~' is no longer shown in the progress display. -2002-02-17: Version 1.0.0pre1 +2002-02-23: Version 1.0.0pre1 - Added scanning for EPG data for another 4 days on channels that support this (thanks to Oleg Assovski). @@ -1020,3 +1020,6 @@ Video Disk Recorder Revision History "instant" recording (see FORMATS for details). - Fixed the SVDRP GRAB command in case the video device can't be opened (thanks to Adrian Stabiszewski). +- At startup the data written into 'epg.data' is now read into the EPG data + structures. In order for this to work, the 'E' record has been extended to + (optionally) contain the 'table ID' (see FORMATS for details). diff --git a/eit.c b/eit.c index afb1ed4f..bd43fe92 100644 --- a/eit.c +++ b/eit.c @@ -16,7 +16,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: eit.c 1.36 2002/02/23 13:53:53 kls Exp $ + * $Id: eit.c 1.37 2002/02/23 17:11:19 kls Exp $ ***************************************************************************/ #include "eit.h" @@ -341,7 +341,7 @@ unsigned short cEventInfo::GetServiceID() const void cEventInfo::Dump(FILE *f, const char *Prefix) const { if (tTime + lDuration >= time(NULL)) { - fprintf(f, "%sE %u %ld %ld\n", Prefix, uEventID, tTime, lDuration); + 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)) @@ -352,6 +352,54 @@ void cEventInfo::Dump(FILE *f, const char *Prefix) const } } +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->GetServiceID(), 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 false; + default: esyslog(LOG_ERR, "ERROR: unexpected tag while reading EPG data: %s", s); + return false; + } + } + return true; + } + return false; +} + #define MAXEPGBUGFIXSTATS 5 #define MAXEPGBUGFIXCHANS 50 struct tEpgBugFixStats { @@ -545,6 +593,13 @@ cSchedule::cSchedule(unsigned short servid) cSchedule::~cSchedule() { } + +cEventInfo *cSchedule::AddEvent(cEventInfo *EventInfo) +{ + Events.Add(EventInfo); + return EventInfo; +} + /** */ const cEventInfo * cSchedule::GetPresentEvent() const { @@ -689,6 +744,31 @@ void cSchedule::Dump(FILE *f, const char *Prefix) const } } +bool cSchedule::Read(FILE *f, cSchedules *Schedules) +{ + if (Schedules) { + char *s; + while ((s = readline(f)) != NULL) { + if (*s == 'C') { + unsigned int uServiceID; + if (1 == sscanf(s + 1, "%u", &uServiceID)) { + cSchedule *p = (cSchedule *)Schedules->SetCurrentServiceID(uServiceID); + if (p) { + while (cEventInfo::Read(f, p)) + ; // loop stops after having read the closing 'c' + } + } + } + else { + esyslog(LOG_ERR, "ERROR: unexpected tag while reading EPG data: %s", s); + return false; + } + } + return true; + } + return false; +} + // --- cSchedules ------------------------------------------------------------ cSchedules::cSchedules() @@ -701,7 +781,7 @@ cSchedules::~cSchedules() { } /** */ -bool cSchedules::SetCurrentServiceID(unsigned short servid) +const cSchedule *cSchedules::SetCurrentServiceID(unsigned short servid) { pCurrentSchedule = GetSchedule(servid); if (pCurrentSchedule == NULL) @@ -709,12 +789,12 @@ bool cSchedules::SetCurrentServiceID(unsigned short servid) Add(new cSchedule(servid)); pCurrentSchedule = GetSchedule(servid); if (pCurrentSchedule == NULL) - return false; + return NULL; } uCurrentServiceID = servid; - return true; + return pCurrentSchedule; } /** */ const cSchedule * cSchedules::GetSchedule() const @@ -757,6 +837,13 @@ void cSchedules::Dump(FILE *f, const char *Prefix) const p->Dump(f, Prefix); } +/** */ +bool cSchedules::Read(FILE *f) +{ + cMutexLock MutexLock; + return cSchedule::Read(f, (cSchedules *)cSIProcessor::Schedules(MutexLock)); +} + // --- cEIT ------------------------------------------------------------------ class cEIT { @@ -822,16 +909,19 @@ int cEIT::ProcessEIT(unsigned char *buffer) 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. - pSchedule->Events.Add(new cEventInfo(VdrProgramInfo->ServiceID, VdrProgramInfo->EventID)); - pEvent = (cEventInfo *)pSchedule->GetEvent((unsigned short)VdrProgramInfo->EventID); + pEvent = pSchedule->AddEvent(new cEventInfo(VdrProgramInfo->ServiceID, 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 a "actual TS" table, lets skip it. + // 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; } @@ -920,6 +1010,25 @@ const cSchedules *cSIProcessor::Schedules(cMutexLock &MutexLock) return NULL; } +bool cSIProcessor::Read(FILE *f) +{ + bool OwnFile = f == NULL; + if (OwnFile) { + const char *FileName = GetEpgDataFileName(); + if (access(FileName, R_OK) == 0) { + dsyslog(LOG_INFO, "reading EPG data from %s", FileName); + if ((f = fopen(FileName, "r")) == NULL) { + LOG_ERROR; + return false; + } + } + } + bool result = cSchedules::Read(f); + if (OwnFile) + fclose(f); + return result; +} + void cSIProcessor::SetEpgDataFileName(const char *FileName) { epgDataFileName = NULL; diff --git a/eit.h b/eit.h index 16f21017..55c0f4e0 100644 --- a/eit.h +++ b/eit.h @@ -16,7 +16,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: eit.h 1.14 2002/02/23 13:51:31 kls Exp $ + * $Id: eit.h 1.15 2002/02/23 15:30:25 kls Exp $ ***************************************************************************/ #ifndef __EIT_H @@ -72,6 +72,7 @@ public: int GetChannelNumber(void) const { return nChannelNumber; } void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const' void Dump(FILE *f, const char *Prefix = "") const; + static bool Read(FILE *f, cSchedule *Schedule); void FixEpgBugs(void); }; @@ -92,6 +93,7 @@ protected: cSchedule(unsigned short servid = 0); public: ~cSchedule(); + cEventInfo *AddEvent(cEventInfo *EventInfo); const cEventInfo *GetPresentEvent(void) const; const cEventInfo *GetFollowingEvent(void) const; unsigned short GetServiceID(void) const; @@ -100,15 +102,17 @@ public: const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); } int NumEvents(void) const { return Events.Count(); } void Dump(FILE *f, const char *Prefix = "") const; + static bool Read(FILE *f, cSchedules *Schedules); }; class cSchedules : public cList<cSchedule> { + friend class cSchedule; friend class cSIProcessor; private: const cSchedule *pCurrentSchedule; unsigned short uCurrentServiceID; protected: - bool SetCurrentServiceID(unsigned short servid); + const cSchedule *SetCurrentServiceID(unsigned short servid); void Cleanup(); public: cSchedules(void); @@ -116,6 +120,7 @@ public: const cSchedule *GetSchedule(unsigned short servid) const; const cSchedule *GetSchedule(void) const; void Dump(FILE *f, const char *Prefix = "") const; + static bool Read(FILE *f); }; typedef struct sip_filter { @@ -150,6 +155,7 @@ public: // Caller must provide a cMutexLock which has to survive the entire // time the returned cSchedules is accessed. Once the cSchedules is no // longer used, the cMutexLock must be destroyed. + static bool Read(FILE *f = NULL); void SetStatus(bool On); bool SetUseTSTime(bool use); bool SetCurrentServiceID(unsigned short servid); diff --git a/vdr.c b/vdr.c index 9d5167b4..296478fa 100644 --- a/vdr.c +++ b/vdr.c @@ -22,7 +22,7 @@ * * The project's page is at http://www.cadsoft.de/people/kls/vdr * - * $Id: vdr.c 1.95 2002/02/10 15:12:43 kls Exp $ + * $Id: vdr.c 1.96 2002/02/23 16:35:36 kls Exp $ */ #include <getopt.h> @@ -35,6 +35,7 @@ #ifdef DVDSUPPORT #include "dvd.h" #endif //DVDSUPPORT +#include "eit.h" #include "i18n.h" #include "interface.h" #include "menu.h" @@ -286,6 +287,8 @@ int main(int argc, char *argv[]) cDvbApi::SetPrimaryDvbApi(Setup.PrimaryDVB); + cSIProcessor::Read(); + Channels.SwitchTo(Setup.CurrentChannel); cDvbApi::PrimaryDvbApi->SetVolume(Setup.CurrentVolume, true);