2003-12-22 13:29:24 +01:00
|
|
|
/*
|
|
|
|
* epg.h: Electronic Program Guide
|
|
|
|
*
|
|
|
|
* 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>.
|
|
|
|
*
|
2013-08-23 10:54:49 +02:00
|
|
|
* $Id: epg.h 3.1 2013/08/23 10:50:05 kls Exp $
|
2003-12-22 13:29:24 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef __EPG_H
|
|
|
|
#define __EPG_H
|
|
|
|
|
|
|
|
#include "channels.h"
|
2012-03-10 15:10:43 +01:00
|
|
|
#include "libsi/section.h"
|
2003-12-22 13:29:24 +01:00
|
|
|
#include "thread.h"
|
|
|
|
#include "tools.h"
|
|
|
|
|
2005-01-02 15:11:44 +01:00
|
|
|
#define MAXEPGBUGFIXLEVEL 3
|
2003-12-22 13:29:24 +01:00
|
|
|
|
2010-01-08 15:23:34 +01:00
|
|
|
enum { MaxEventContents = 4 };
|
|
|
|
|
|
|
|
enum eEventContentGroup {
|
|
|
|
ecgMovieDrama = 0x10,
|
|
|
|
ecgNewsCurrentAffairs = 0x20,
|
|
|
|
ecgShow = 0x30,
|
|
|
|
ecgSports = 0x40,
|
|
|
|
ecgChildrenYouth = 0x50,
|
|
|
|
ecgMusicBalletDance = 0x60,
|
|
|
|
ecgArtsCulture = 0x70,
|
|
|
|
ecgSocialPoliticalEconomics = 0x80,
|
|
|
|
ecgEducationalScience = 0x90,
|
|
|
|
ecgLeisureHobbies = 0xA0,
|
|
|
|
ecgSpecial = 0xB0,
|
|
|
|
ecgUserDefined = 0xF0
|
|
|
|
};
|
2010-01-03 12:20:37 +01:00
|
|
|
|
2004-02-22 15:36:36 +01:00
|
|
|
enum eDumpMode { dmAll, dmPresent, dmFollowing, dmAtTime };
|
|
|
|
|
2005-01-02 15:11:44 +01:00
|
|
|
struct tComponent {
|
|
|
|
uchar stream;
|
|
|
|
uchar type;
|
2006-02-18 15:59:43 +01:00
|
|
|
char language[MAXLANGCODE2];
|
2005-01-02 15:11:44 +01:00
|
|
|
char *description;
|
|
|
|
cString ToString(void);
|
|
|
|
bool FromString(const char *s);
|
|
|
|
};
|
|
|
|
|
|
|
|
class cComponents {
|
|
|
|
private:
|
|
|
|
int numComponents;
|
|
|
|
tComponent *components;
|
2011-02-25 15:25:42 +01:00
|
|
|
bool Realloc(int Index);
|
2005-01-02 15:11:44 +01:00
|
|
|
public:
|
2005-05-16 14:45:11 +02:00
|
|
|
cComponents(void);
|
2005-01-02 15:11:44 +01:00
|
|
|
~cComponents(void);
|
|
|
|
int NumComponents(void) const { return numComponents; }
|
2005-05-16 14:45:11 +02:00
|
|
|
void SetComponent(int Index, const char *s);
|
|
|
|
void SetComponent(int Index, uchar Stream, uchar Type, const char *Language, const char *Description);
|
2005-01-02 15:11:44 +01:00
|
|
|
tComponent *Component(int Index) const { return (Index < numComponents) ? &components[Index] : NULL; }
|
2006-02-19 13:27:51 +01:00
|
|
|
tComponent *GetComponent(int Index, uchar Stream, uchar Type); // Gets the Index'th component of Stream and Type, skipping other components
|
2006-10-07 13:59:21 +02:00
|
|
|
// In case of an audio stream the 'type' check actually just distinguishes between "normal" and "Dolby Digital"
|
2005-01-02 15:11:44 +01:00
|
|
|
};
|
|
|
|
|
2003-12-22 13:29:24 +01:00
|
|
|
class cSchedule;
|
|
|
|
|
2006-02-26 14:13:30 +01:00
|
|
|
typedef u_int32_t tEventID;
|
|
|
|
|
2003-12-22 13:29:24 +01:00
|
|
|
class cEvent : public cListObject {
|
2005-05-28 13:17:20 +02:00
|
|
|
friend class cSchedule;
|
2003-12-22 13:29:24 +01:00
|
|
|
private:
|
2010-01-03 14:40:37 +01:00
|
|
|
// The sequence of these parameters is optimized for minimal memory waste!
|
2005-05-28 10:09:06 +02:00
|
|
|
cSchedule *schedule; // The Schedule this event belongs to
|
2006-02-26 14:13:30 +01:00
|
|
|
tEventID eventID; // Event ID of this event
|
2005-01-02 15:11:44 +01:00
|
|
|
uchar tableID; // Table ID this event came from
|
|
|
|
uchar version; // Version number of section this event came from
|
2010-01-03 14:40:37 +01:00
|
|
|
uchar runningStatus; // 0=undefined, 1=not running, 2=starts in a few seconds, 3=pausing, 4=running
|
|
|
|
uchar parentalRating; // Parental rating of this event
|
2005-01-02 15:11:44 +01:00
|
|
|
char *title; // Title of this event
|
|
|
|
char *shortText; // Short description of this event (typically the episode name in case of a series)
|
|
|
|
char *description; // Description of this event
|
2005-05-16 14:45:11 +02:00
|
|
|
cComponents *components; // The stream components of this event
|
2010-01-08 15:23:34 +01:00
|
|
|
uchar contents[MaxEventContents]; // Contents of this event
|
2005-01-02 15:11:44 +01:00
|
|
|
time_t startTime; // Start time of this event
|
|
|
|
int duration; // Duration of this event in seconds
|
|
|
|
time_t vps; // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL)
|
|
|
|
time_t seen; // When this event was last seen in the data stream
|
2003-12-22 13:29:24 +01:00
|
|
|
public:
|
2006-02-26 14:13:30 +01:00
|
|
|
cEvent(tEventID EventID);
|
2003-12-22 13:29:24 +01:00
|
|
|
~cEvent();
|
2004-11-01 10:40:38 +01:00
|
|
|
virtual int Compare(const cListObject &ListObject) const;
|
2005-05-28 10:09:06 +02:00
|
|
|
tChannelID ChannelID(void) const;
|
2005-12-27 14:39:14 +01:00
|
|
|
const cSchedule *Schedule(void) const { return schedule; }
|
2006-02-26 14:13:30 +01:00
|
|
|
tEventID EventID(void) const { return eventID; }
|
2003-12-25 12:50:22 +01:00
|
|
|
uchar TableID(void) const { return tableID; }
|
|
|
|
uchar Version(void) const { return version; }
|
2004-02-21 12:28:17 +01:00
|
|
|
int RunningStatus(void) const { return runningStatus; }
|
2003-12-22 13:29:24 +01:00
|
|
|
const char *Title(void) const { return title; }
|
|
|
|
const char *ShortText(void) const { return shortText; }
|
|
|
|
const char *Description(void) const { return description; }
|
2005-01-02 15:11:44 +01:00
|
|
|
const cComponents *Components(void) const { return components; }
|
2012-02-26 14:02:17 +01:00
|
|
|
uchar Contents(int i = 0) const { return (0 <= i && i < MaxEventContents) ? contents[i] : uchar(0); }
|
2010-01-03 14:28:33 +01:00
|
|
|
int ParentalRating(void) const { return parentalRating; }
|
2003-12-22 13:29:24 +01:00
|
|
|
time_t StartTime(void) const { return startTime; }
|
2004-03-06 11:27:08 +01:00
|
|
|
time_t EndTime(void) const { return startTime + duration; }
|
2003-12-22 13:29:24 +01:00
|
|
|
int Duration(void) const { return duration; }
|
2004-02-22 13:33:20 +01:00
|
|
|
time_t Vps(void) const { return vps; }
|
2004-10-24 15:01:50 +02:00
|
|
|
time_t Seen(void) const { return seen; }
|
2005-03-13 13:19:30 +01:00
|
|
|
bool SeenWithin(int Seconds) const { return time(NULL) - seen < Seconds; }
|
2004-02-29 14:21:22 +01:00
|
|
|
bool HasTimer(void) const;
|
2004-03-06 11:27:08 +01:00
|
|
|
bool IsRunning(bool OrAboutToStart = false) const;
|
2010-01-03 12:20:37 +01:00
|
|
|
static const char *ContentToString(uchar Content);
|
2010-01-03 14:28:33 +01:00
|
|
|
cString GetParentalRatingString(void) const;
|
2004-12-26 12:45:22 +01:00
|
|
|
cString GetDateString(void) const;
|
|
|
|
cString GetTimeString(void) const;
|
|
|
|
cString GetEndTimeString(void) const;
|
|
|
|
cString GetVpsString(void) const;
|
2006-02-26 14:13:30 +01:00
|
|
|
void SetEventID(tEventID EventID);
|
2003-12-22 13:29:24 +01:00
|
|
|
void SetTableID(uchar TableID);
|
2003-12-25 12:50:22 +01:00
|
|
|
void SetVersion(uchar Version);
|
2004-03-13 15:01:05 +01:00
|
|
|
void SetRunningStatus(int RunningStatus, cChannel *Channel = NULL);
|
2003-12-22 13:29:24 +01:00
|
|
|
void SetTitle(const char *Title);
|
|
|
|
void SetShortText(const char *ShortText);
|
|
|
|
void SetDescription(const char *Description);
|
2005-01-02 15:11:44 +01:00
|
|
|
void SetComponents(cComponents *Components); // Will take ownership of Components!
|
2010-01-03 12:20:37 +01:00
|
|
|
void SetContents(uchar *Contents);
|
2010-01-03 14:28:33 +01:00
|
|
|
void SetParentalRating(int ParentalRating);
|
2003-12-22 13:29:24 +01:00
|
|
|
void SetStartTime(time_t StartTime);
|
|
|
|
void SetDuration(int Duration);
|
2004-02-22 13:33:20 +01:00
|
|
|
void SetVps(time_t Vps);
|
2004-10-24 15:01:50 +02:00
|
|
|
void SetSeen(void);
|
2006-03-25 12:51:29 +01:00
|
|
|
cString ToDescr(void) const;
|
2005-05-16 14:45:11 +02:00
|
|
|
void Dump(FILE *f, const char *Prefix = "", bool InfoOnly = false) const;
|
|
|
|
bool Parse(char *s);
|
2003-12-22 13:29:24 +01:00
|
|
|
static bool Read(FILE *f, cSchedule *Schedule);
|
|
|
|
void FixEpgBugs(void);
|
|
|
|
};
|
|
|
|
|
|
|
|
class cSchedules;
|
|
|
|
|
|
|
|
class cSchedule : public cListObject {
|
|
|
|
private:
|
|
|
|
tChannelID channelID;
|
|
|
|
cList<cEvent> events;
|
2005-05-28 13:17:20 +02:00
|
|
|
cHash<cEvent> eventsHashID;
|
|
|
|
cHash<cEvent> eventsHashStartTime;
|
2004-03-13 15:01:05 +01:00
|
|
|
bool hasRunning;
|
2004-10-24 15:01:50 +02:00
|
|
|
time_t modified;
|
2005-03-20 13:12:07 +01:00
|
|
|
time_t presentSeen;
|
2003-12-22 13:29:24 +01:00
|
|
|
public:
|
|
|
|
cSchedule(tChannelID ChannelID);
|
|
|
|
tChannelID ChannelID(void) const { return channelID; }
|
2004-10-24 15:01:50 +02:00
|
|
|
time_t Modified(void) const { return modified; }
|
2005-03-20 13:12:07 +01:00
|
|
|
time_t PresentSeen(void) const { return presentSeen; }
|
|
|
|
bool PresentSeenWithin(int Seconds) const { return time(NULL) - presentSeen < Seconds; }
|
2004-10-24 15:01:50 +02:00
|
|
|
void SetModified(void) { modified = time(NULL); }
|
2005-03-20 13:12:07 +01:00
|
|
|
void SetPresentSeen(void) { presentSeen = time(NULL); }
|
2004-03-06 14:33:22 +01:00
|
|
|
void SetRunningStatus(cEvent *Event, int RunningStatus, cChannel *Channel = NULL);
|
2004-03-13 15:01:05 +01:00
|
|
|
void ClrRunningStatus(cChannel *Channel = NULL);
|
2004-01-09 15:53:59 +01:00
|
|
|
void ResetVersions(void);
|
2004-02-21 13:56:20 +01:00
|
|
|
void Sort(void);
|
2005-12-26 14:44:28 +01:00
|
|
|
void DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version);
|
2003-12-22 13:29:24 +01:00
|
|
|
void Cleanup(time_t Time);
|
|
|
|
void Cleanup(void);
|
|
|
|
cEvent *AddEvent(cEvent *Event);
|
2005-05-28 13:17:20 +02:00
|
|
|
void DelEvent(cEvent *Event);
|
2005-09-11 13:23:49 +02:00
|
|
|
void HashEvent(cEvent *Event);
|
|
|
|
void UnhashEvent(cEvent *Event);
|
2004-03-14 13:27:57 +01:00
|
|
|
const cList<cEvent> *Events(void) const { return &events; }
|
2006-01-29 14:04:37 +01:00
|
|
|
const cEvent *GetPresentEvent(void) const;
|
|
|
|
const cEvent *GetFollowingEvent(void) const;
|
2006-02-26 14:13:30 +01:00
|
|
|
const cEvent *GetEvent(tEventID EventID, time_t StartTime = 0) const;
|
2003-12-22 13:29:24 +01:00
|
|
|
const cEvent *GetEventAround(time_t Time) const;
|
2004-02-22 15:36:36 +01:00
|
|
|
void Dump(FILE *f, const char *Prefix = "", eDumpMode DumpMode = dmAll, time_t AtTime = 0) const;
|
2003-12-22 13:29:24 +01:00
|
|
|
static bool Read(FILE *f, cSchedules *Schedules);
|
|
|
|
};
|
|
|
|
|
|
|
|
class cSchedulesLock {
|
|
|
|
private:
|
|
|
|
bool locked;
|
|
|
|
public:
|
|
|
|
cSchedulesLock(bool WriteLock = false, int TimeoutMs = 0);
|
|
|
|
~cSchedulesLock();
|
|
|
|
bool Locked(void) { return locked; }
|
|
|
|
};
|
|
|
|
|
|
|
|
class cSchedules : public cList<cSchedule> {
|
|
|
|
friend class cSchedule;
|
|
|
|
friend class cSchedulesLock;
|
|
|
|
private:
|
2004-01-04 12:30:00 +01:00
|
|
|
cRwLock rwlock;
|
2003-12-22 13:29:24 +01:00
|
|
|
static cSchedules schedules;
|
2012-05-13 13:46:56 +02:00
|
|
|
static char *epgDataFileName;
|
2003-12-22 13:29:24 +01:00
|
|
|
static time_t lastDump;
|
2004-10-24 15:01:50 +02:00
|
|
|
static time_t modified;
|
2003-12-22 13:29:24 +01:00
|
|
|
public:
|
|
|
|
static void SetEpgDataFileName(const char *FileName);
|
|
|
|
static const cSchedules *Schedules(cSchedulesLock &SchedulesLock);
|
|
|
|
///< Caller must provide a cSchedulesLock which has to survive the entire
|
|
|
|
///< time the returned cSchedules is accessed. Once the cSchedules is no
|
|
|
|
///< longer used, the cSchedulesLock must be destroyed.
|
2004-10-24 15:01:50 +02:00
|
|
|
static time_t Modified(void) { return modified; }
|
|
|
|
static void SetModified(cSchedule *Schedule);
|
2003-12-22 13:29:24 +01:00
|
|
|
static void Cleanup(bool Force = false);
|
2004-01-09 15:53:59 +01:00
|
|
|
static void ResetVersions(void);
|
2003-12-22 13:29:24 +01:00
|
|
|
static bool ClearAll(void);
|
2012-09-24 13:41:54 +02:00
|
|
|
static bool Dump(FILE *f = NULL, const char *Prefix = "", eDumpMode DumpMode = dmAll, time_t AtTime = 0);
|
2003-12-22 13:29:24 +01:00
|
|
|
static bool Read(FILE *f = NULL);
|
|
|
|
cSchedule *AddSchedule(tChannelID ChannelID);
|
|
|
|
const cSchedule *GetSchedule(tChannelID ChannelID) const;
|
2006-01-14 15:52:40 +01:00
|
|
|
const cSchedule *GetSchedule(const cChannel *Channel, bool AddIfMissing = false) const;
|
2003-12-22 13:29:24 +01:00
|
|
|
};
|
|
|
|
|
2012-02-11 13:41:29 +01:00
|
|
|
class cEpgDataReader : public cThread {
|
|
|
|
public:
|
|
|
|
cEpgDataReader(void);
|
|
|
|
virtual void Action(void);
|
|
|
|
};
|
|
|
|
|
2012-09-24 13:41:54 +02:00
|
|
|
void ReportEpgBugFixStats(bool Force = false);
|
2003-12-22 13:29:24 +01:00
|
|
|
|
2012-03-10 15:10:43 +01:00
|
|
|
class cEpgHandler : public cListObject {
|
|
|
|
public:
|
|
|
|
cEpgHandler(void);
|
|
|
|
///< Constructs a new EPG handler and adds it to the list of EPG handlers.
|
|
|
|
///< Whenever an event is received from the EIT data stream, the EPG
|
|
|
|
///< handlers are queried in the order they have been created.
|
|
|
|
///< As soon as one of the EPG handlers returns true in a member function,
|
|
|
|
///< none of the remaining handlers will be queried. If none of the EPG
|
|
|
|
///< handlers returns true in a particular call, the default processing
|
|
|
|
///< will take place.
|
|
|
|
///< EPG handlers will be deleted automatically at the end of the program.
|
|
|
|
virtual ~cEpgHandler();
|
|
|
|
virtual bool IgnoreChannel(const cChannel *Channel) { return false; }
|
|
|
|
///< Before any EIT data for the given Channel is processed, the EPG handlers
|
|
|
|
///< are asked whether this Channel shall be completely ignored. If any of
|
|
|
|
///< the EPG handlers returns true in this function, no EIT data at all will
|
|
|
|
///< be processed for this Channel.
|
|
|
|
virtual bool HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version) { return false; }
|
|
|
|
///< Before the raw EitEvent for the given Schedule is processed, the
|
|
|
|
///< EPG handlers are queried to see if any of them would like to do the
|
|
|
|
///< complete processing by itself. TableID and Version are from the
|
|
|
|
///< incoming section data.
|
2012-06-04 10:19:23 +02:00
|
|
|
virtual bool HandledExternally(const cChannel *Channel) { return false; }
|
|
|
|
///< If any EPG handler returns true in this function, it is assumed that
|
|
|
|
///< the EPG for the given Channel is handled completely from some external
|
|
|
|
///< source. Incoming EIT data is processed as usual, but any new EPG event
|
|
|
|
///< will not be added to the respective schedule. It's up to the EPG
|
|
|
|
///< handler to take care of this.
|
2012-08-25 11:31:35 +02:00
|
|
|
virtual bool IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version) { return false; }
|
|
|
|
///< VDR can't perform the update check (version, tid) for externally handled events,
|
|
|
|
///< therefore the EPG handlers have to take care of this. Otherwise the parsing of
|
|
|
|
///< non-updates will waste a lot of resources.
|
2012-03-10 15:10:43 +01:00
|
|
|
virtual bool SetEventID(cEvent *Event, tEventID EventID) { return false; }
|
|
|
|
virtual bool SetTitle(cEvent *Event, const char *Title) { return false; }
|
|
|
|
virtual bool SetShortText(cEvent *Event, const char *ShortText) { return false; }
|
|
|
|
virtual bool SetDescription(cEvent *Event, const char *Description) { return false; }
|
|
|
|
virtual bool SetContents(cEvent *Event, uchar *Contents) { return false; }
|
|
|
|
virtual bool SetParentalRating(cEvent *Event, int ParentalRating) { return false; }
|
|
|
|
virtual bool SetStartTime(cEvent *Event, time_t StartTime) { return false; }
|
|
|
|
virtual bool SetDuration(cEvent *Event, int Duration) { return false; }
|
|
|
|
virtual bool SetVps(cEvent *Event, time_t Vps) { return false; }
|
2012-06-04 10:29:19 +02:00
|
|
|
virtual bool SetComponents(cEvent *Event, cComponents *Components) { return false; }
|
2012-03-10 15:10:43 +01:00
|
|
|
virtual bool FixEpgBugs(cEvent *Event) { return false; }
|
|
|
|
///< Fixes some known problems with EPG data.
|
|
|
|
virtual bool HandleEvent(cEvent *Event) { return false; }
|
|
|
|
///< After all modifications of the Event have been done, the EPG handler
|
|
|
|
///< can take a final look at it.
|
|
|
|
virtual bool SortSchedule(cSchedule *Schedule) { return false; }
|
|
|
|
///< Sorts the Schedule after the complete table has been processed.
|
|
|
|
virtual bool DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version) { return false; }
|
|
|
|
///< Takes a look at all EPG events between SegmentStart and SegmentEnd and
|
|
|
|
///< drops outdated events.
|
2013-08-23 10:54:49 +02:00
|
|
|
virtual bool BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus) { return false; }
|
|
|
|
///< Called directly after IgnoreChannel() before any other handler method is called.
|
|
|
|
///< Designed to give handlers the possibility to prepare a database transaction.
|
|
|
|
virtual bool EndSegmentTransfer(bool Modified, bool OnlyRunningStatus) { return false; }
|
|
|
|
///< Called after the segment data has been processed.
|
|
|
|
///< At this point handlers should close/commit/rollback any pending database transactions.
|
2012-03-10 15:10:43 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
class cEpgHandlers : public cList<cEpgHandler> {
|
|
|
|
public:
|
|
|
|
bool IgnoreChannel(const cChannel *Channel);
|
|
|
|
bool HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version);
|
2012-06-04 10:19:23 +02:00
|
|
|
bool HandledExternally(const cChannel *Channel);
|
2012-08-25 11:31:35 +02:00
|
|
|
bool IsUpdate(tEventID EventID, time_t StartTime, uchar TableID, uchar Version);
|
2012-03-10 15:10:43 +01:00
|
|
|
void SetEventID(cEvent *Event, tEventID EventID);
|
|
|
|
void SetTitle(cEvent *Event, const char *Title);
|
|
|
|
void SetShortText(cEvent *Event, const char *ShortText);
|
|
|
|
void SetDescription(cEvent *Event, const char *Description);
|
|
|
|
void SetContents(cEvent *Event, uchar *Contents);
|
|
|
|
void SetParentalRating(cEvent *Event, int ParentalRating);
|
|
|
|
void SetStartTime(cEvent *Event, time_t StartTime);
|
|
|
|
void SetDuration(cEvent *Event, int Duration);
|
|
|
|
void SetVps(cEvent *Event, time_t Vps);
|
2012-06-04 10:29:19 +02:00
|
|
|
void SetComponents(cEvent *Event, cComponents *Components);
|
2012-03-10 15:10:43 +01:00
|
|
|
void FixEpgBugs(cEvent *Event);
|
|
|
|
void HandleEvent(cEvent *Event);
|
|
|
|
void SortSchedule(cSchedule *Schedule);
|
|
|
|
void DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version);
|
2013-08-23 10:54:49 +02:00
|
|
|
void BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus);
|
|
|
|
void EndSegmentTransfer(bool Modified, bool OnlyRunningStatus);
|
2012-03-10 15:10:43 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
extern cEpgHandlers EpgHandlers;
|
|
|
|
|
2003-12-22 13:29:24 +01:00
|
|
|
#endif //__EPG_H
|