diff options
author | Klaus Schmidinger <kls (at) cadsoft (dot) de> | 2000-11-01 18:00:00 +0100 |
---|---|---|
committer | Klaus Schmidinger <kls (at) cadsoft (dot) de> | 2000-11-01 18:00:00 +0100 |
commit | a69b3211dc4f9b34eef440067d5ba304fbfbad94 (patch) | |
tree | 7701ccce8cef832eb5ab56293a7ae99aca40b78e | |
parent | a379eb714f7f5ef9a12efbe7588bb3509faba056 (diff) | |
download | vdr-patch-lnbsharing-a69b3211dc4f9b34eef440067d5ba304fbfbad94.tar.gz vdr-patch-lnbsharing-a69b3211dc4f9b34eef440067d5ba304fbfbad94.tar.bz2 |
Version 0.67vdr-0.67
- The EIT information is now gathered in a separate thread.
- The sytem time can now be synchronized to the time broadcast in the DVB data
stream. This can be enabled in the "Setup" menu by setting "SetSystemTime" to
1. Note that this works only if VDR is running under a user id that has
permisson to set the system time.
- The new item "Schedule" in the "Main" menu opens VDR's EPG (thanks to Robert
Schneider). See the MANUAL file for a detailed description.
- The new setup parameters MarginStart and MarginStop define how long (in
minutes) before the official start time of a broadcast VDR shall begin
recording, and how long after the official end time it shall stop recording.
These are used when a recording is programmed from the "Schedules" menu.
- The delay value in the dvb.c.071.diff patch to the driver has been increased
to '3', because on some systems the OSD was not displayed correctly. If you
are running an already patched version 0.71 driver and encounter problems
with the OSD, please make sure the parameter in the ddelay call is '3', not
'2'.
- Fixed initializing the RCU remote control code (didn't work after switching
on the system).
- Problematic characters in recording names (which can come from timers that
are programmed via the "Schedules" menu) are now replaced by suitable
substitutes.
-rw-r--r-- | BUGS | 2 | ||||
-rw-r--r-- | CONTRIBUTORS | 1 | ||||
-rw-r--r-- | HISTORY | 24 | ||||
-rw-r--r-- | MANUAL | 59 | ||||
-rw-r--r-- | Makefile | 20 | ||||
-rw-r--r-- | channels.conf | 3 | ||||
-rw-r--r-- | config.c | 100 | ||||
-rw-r--r-- | config.h | 10 | ||||
-rw-r--r-- | dvb.c.071.diff | 2 | ||||
-rw-r--r-- | dvbapi.c | 67 | ||||
-rw-r--r-- | dvbapi.h | 16 | ||||
-rw-r--r-- | dvbosd.c | 7 | ||||
-rw-r--r-- | dvbosd.h | 3 | ||||
-rw-r--r-- | eit.c | 1443 | ||||
-rw-r--r-- | eit.h | 173 | ||||
-rw-r--r-- | interface.c | 157 | ||||
-rw-r--r-- | interface.h | 10 | ||||
-rw-r--r-- | menu.c | 567 | ||||
-rw-r--r-- | menu.h | 16 | ||||
-rw-r--r-- | osd.c | 15 | ||||
-rw-r--r-- | osd.h | 16 | ||||
-rw-r--r-- | recording.c | 9 | ||||
-rw-r--r-- | setup.conf | 7 | ||||
-rw-r--r-- | thread.c | 19 | ||||
-rw-r--r-- | thread.h | 5 | ||||
-rw-r--r-- | timers.conf | 13 | ||||
-rw-r--r-- | tools.c | 15 | ||||
-rw-r--r-- | tools.h | 19 | ||||
-rw-r--r-- | vdr.c | 24 |
29 files changed, 2182 insertions, 640 deletions
@@ -4,3 +4,5 @@ Video Disk Recorder - Known Bugs * Sometimes the picture "jumps" as if a frame is skipped. Presumably this is a problem in the card driver or firmware? +* The first picture captured with the GRAB command in SVDRP + is empty. Also, these pictures appear to be too dark. diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 91c9540..db47d8b 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -26,6 +26,7 @@ Guido Fiala <gfiala@s.netic.de> Robert Schneider <Robert.Schneider@lotus.com> for implementing EIT support for displaying the current/next info + for extending EIT support to implement a complete EPG Niels de Carpentier <niels@casema.net> for adding a workaround for a driver timing problem in cDvbApi::Cmd() @@ -241,3 +241,27 @@ Video Disk Recorder Revision History - The "Blue" button in the "Main" menu can now be used to "Resume" a previously stopped replay session (suggested by Martin Hammerschmid). - The low and high LNB frequencies can now be changed in the "Setup" menu. + +2000-11-01: Version 0.67 + +- The EIT information is now gathered in a separate thread. +- The sytem time can now be synchronized to the time broadcast in the DVB data + stream. This can be enabled in the "Setup" menu by setting "SetSystemTime" to + 1. Note that this works only if VDR is running under a user id that has + permisson to set the system time. +- The new item "Schedule" in the "Main" menu opens VDR's EPG (thanks to Robert + Schneider). See the MANUAL file for a detailed description. +- The new setup parameters MarginStart and MarginStop define how long (in + minutes) before the official start time of a broadcast VDR shall begin + recording, and how long after the official end time it shall stop recording. + These are used when a recording is programmed from the "Schedules" menu. +- The delay value in the dvb.c.071.diff patch to the driver has been increased + to '3', because on some systems the OSD was not displayed correctly. If you + are running an already patched version 0.71 driver and encounter problems + with the OSD, please make sure the parameter in the ddelay call is '3', not + '2'. +- Fixed initializing the RCU remote control code (didn't work after switching + on the system). +- Problematic characters in recording names (which can come from timers that + are programmed via the "Schedules" menu) are now replaced by suitable + substitutes. @@ -53,20 +53,60 @@ Video Disk Recorder User's Manual At any point in the menu system, pressing the "Menu" key again will immediately leave the menu system (discarding any pending changes). +* The "Schedule" Menu + + The "Schedule" menu implements VDR's "Electronic Program Guide" (EPG). + + Select "Schedule" from the "Main" menu and you get a list of all upcoming + broadcasts on the current channel. + + "Up" and "Down" can be used to scroll through this list, and pressing "Ok" + displays detailed information about the selected programme. Pressing "Ok" + again (or pressing "Back") gets you back into the "Schedule" menu. + + From the "Schedule" menu, the "Green" button opens the "What's on now?" + menu, which displays all programmes that are currently running on all + channels that broadcast their programme information on the current + transponder, or from channels that have been current lately (VDR stores + all information it gathers in an internal list). The more channels you + have been switching through lately, the longer this list will be. + The "Yellow" button opens the "What's on next?" menu, which lists all + programmes that will start next on all channels. + + Inside the "What's on now/next?" menus the "Green" button toggles between + the "Now" and "Next" display, and the "Yellow" button gets you back to the + "Schedule" menu of the current channel. + + The "Red" button allows you to instantly program a timer to record the + selected programme. You will get into the "Edit Timer" menu in which + everything has already been filled in, and you can make any modifications + you may want to apply. Note that the Start and Stop time are offset by the + MarginStart and MarginStop parameters (see Setup) in order to make sure the + entire programme is recorded in case it doesn't exactly adhere to its + published start/stop times. Of course, no guarantee can be given that the + default margin values will be sufficient, so in case this recording is + really important you may want to add an extra margin ;-) + + The "Blue" button can be pressed to switch to the channel with the selected + programme. + * Selecting a Channel - There are three ways to select a channel: + There are four ways to select a channel: 1. With no On Screen Menu displayed press the "Up" or "Down" key to switch to the next higher or lower channel. 2. Press the "Menu" button to bring up the On Screen Menu, select "Channels" and browse through the list with the "Up" and "Down" key; to switch to the selected channel press "Ok". - 2. Directly type in the channel number with the numeric keys ('0'..'9'); + 3. Directly type in the channel number with the numeric keys ('0'..'9'); if no key is pressed for about half a second, the digits collected so far will define the channel number. + 4. From the "Now", "Next" and "Event" menus (accessible through the "Schedule" + menu) by pressing the "Blue" button. - Pressing the '0' key toggles between the current and the previous channel. + Pressing the '0' key in normal viewing mode toggles between the current and + the previous channel. After switching to a different channel the channel number and name, as well as the current time are displayed at the top of the screen. If available, the @@ -178,6 +218,9 @@ Video Disk Recorder User's Manual If this field is left blank, the channel name will be used to form the name of the recording. + A timer can also be programmed by pressing the "Red" button on the "Schedule", + "Now", "Next" or "Event" menus. + * Parameters in the "Setup" menu Select "Setup" from the "Main" menu to enter the setup menu. From there you can @@ -216,3 +259,13 @@ Video Disk Recorder User's Manual LnbFrequLo = 9750 The low and high LNB frequencies (in MHz) LnbFrequHi = 10600 + + SetSystemTime = 0 Defines whether the system time will be set according to + the time received from the DVB data stream. + 0 = system time will not be set + 1 = system time wil be set + Note that this works only if VDR is running under a user + id that has permisson to set the system time. + MarginStart = 2 Defines how many minutes before the official start time + MarginStop = 10 of a broadcast VDR shall start recording, and how long + after the official end time it shall stop recording. @@ -4,7 +4,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: Makefile 1.13 2000/10/07 16:24:08 kls Exp $ +# $Id: Makefile 1.14 2000/10/28 16:24:16 kls Exp $ DVBDIR = ../DVB @@ -35,20 +35,20 @@ font: genfontfile fontosd.c # Dependencies: -config.o : config.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h svdrp.h tools.h -dvbapi.o : dvbapi.c config.h dvbapi.h dvbosd.h font.h interface.h svdrp.h tools.h videodir.h +config.o : config.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h +dvbapi.o : dvbapi.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h videodir.h dvbosd.o : dvbosd.c dvbosd.h font.h tools.h -eit.o : eit.c eit.h tools.h +eit.o : eit.c eit.h thread.h tools.h font.o : font.c font.h fontosd.c tools.h interface.o: interface.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h -menu.o : menu.c config.h dvbapi.h dvbosd.h font.h interface.h menu.h osd.h recording.h svdrp.h tools.h -osd.o : osd.c config.h dvbapi.h dvbosd.h font.h interface.h osd.h svdrp.h tools.h -recording.o: recording.c config.h dvbapi.h dvbosd.h font.h interface.h recording.h svdrp.h tools.h videodir.h -remote.o : remote.c config.h dvbapi.h dvbosd.h font.h remote.h thread.h tools.h -svdrp.o : svdrp.c config.h dvbapi.h dvbosd.h font.h interface.h svdrp.h tools.h +menu.o : menu.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h +osd.o : osd.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h osd.h remote.h svdrp.h thread.h tools.h +recording.o: recording.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h +remote.o : remote.c config.h dvbapi.h dvbosd.h eit.h font.h remote.h thread.h tools.h +svdrp.o : svdrp.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h thread.o : thread.c thread.h tools.o : tools.c tools.h -vdr.o : vdr.c config.h dvbapi.h dvbosd.h font.h interface.h menu.h osd.h recording.h svdrp.h tools.h videodir.h +vdr.o : vdr.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h videodir.h videodir.o : videodir.c tools.h videodir.h # The main program: diff --git a/channels.conf b/channels.conf index 4a6cb0b..c61669c 100644 --- a/channels.conf +++ b/channels.conf @@ -6,6 +6,7 @@ ARD:11837:h:1:27500:101:102:0:28106 BR3:11837:h:1:27500:201:202:0:28107 Hessen-3:11837:h:1:27500:301:302:0:28108 N3:12110:h:1:27500:2401:2402:0:28224 +:Group 1 SR3:11837:h:1:27500:501:502:0:28110 WDR:11837:h:1:27500:601:602:0:28111 BR-alpha:11837:h:1:27500:701:702:0:28112 @@ -15,9 +16,11 @@ ZDF:11954:h:1:27500:110:120:0:28006 3sat:11954:h:1:27500:210:220:0:28007 KiKa:11954:h:1:27500:310:320:0:28008 arte:11836:h:1:27500:401:402:0:28109 +:Group 2 Eurosport:11954:h:1:27500:410:420:0:28009 ORF Sat:11954:h:1:27500:506:507:0:28010 ZDF.info:11954:h:1:27500:610:620:0:28011 +:Group 3 CNN:12168:v:1:27500:165:100:0:0 Super RTL:12188:h:1:27500:165:120:0:12040 VOX:12188:h:1:27500:167:136:0:12060 @@ -4,18 +4,15 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.c 1.26 2000/10/08 16:10:40 kls Exp $ + * $Id: config.c 1.29 2000/11/01 18:22:43 kls Exp $ */ #include "config.h" #include <ctype.h> #include <stdlib.h> #include "dvbapi.h" -#include "eit.h" #include "interface.h" -extern cEIT EIT; - // -- cKeys ------------------------------------------------------------------ tKey keyTable[] = { // "Up" and "Down" must be the first two keys! @@ -267,15 +264,14 @@ bool cChannel::Switch(cDvbApi *DvbApi) isyslog(LOG_INFO, "switching to channel %d", number); CurrentChannel = number; for (int i = 3; i--;) { - if (DvbApi->SetChannel(frequency, polarization, diseqc, srate, vpid, apid, ca, pnr)) { - EIT.SetProgramNumber(pnr); + if (DvbApi->SetChannel(frequency, polarization, diseqc, srate, vpid, apid, ca, pnr)) return true; - } esyslog(LOG_ERR, "retrying"); } return false; } - Interface->Info(DvbApi->Recording() ? "Channel locked (recording)!" : name); + if (DvbApi->Recording()) + Interface->Info("Channel locked (recording)!"); return false; } @@ -306,6 +302,47 @@ cTimer::cTimer(bool Instant) snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", ch->name); } +cTimer::cTimer(const cEventInfo *EventInfo) +{ + startTime = stopTime = 0; + recording = false; + active = true; + cChannel *ch = Channels.GetByServiceID(EventInfo->GetServiceID()); + channel = ch ? ch->number : 0; + time_t tstart = EventInfo->GetTime(); + time_t tstop = tstart + EventInfo->GetDuration() + Setup.MarginStop * 60; + tstart -= Setup.MarginStart * 60; + struct tm *time = localtime(&tstart); + day = time->tm_mday; + start = time->tm_hour * 100 + time->tm_min; + time = localtime(&tstop); + stop = time->tm_hour * 100 + time->tm_min; + if (stop >= 2400) + stop -= 2400; + priority = 99; + lifetime = 99; + *file = 0; + const char *Title = EventInfo->GetTitle(); + if (!isempty(Title)) + strn0cpy(file, EventInfo->GetTitle(), sizeof(file)); + summary = NULL; + const char *Subtitle = EventInfo->GetSubtitle(); + if (isempty(Subtitle)) + Subtitle = ""; + const char *Summary = EventInfo->GetExtendedDescription(); + if (isempty(Summary)) + Summary = ""; + if (*Subtitle || *Summary) { + asprintf(&summary, "%s%s%s", Subtitle, (*Subtitle && *Summary) ? "\n\n" : "", Summary); + char *p = summary; + while (*p) { + if (*p == '\n') + *p = '|'; + p++; + } + } +} + cTimer::~cTimer() { delete summary; @@ -388,14 +425,33 @@ bool cTimer::Parse(const char *s) char *buffer2 = NULL; delete summary; summary = NULL; + //XXX Apparently sscanf() doesn't work correctly if the last %a argument + //XXX results in an empty string (this firt occured when the EIT gathering + //XXX was put into a separate thread - don't know why this happens... + //XXX As a cure we copy the original string and add a blank. + //XXX If anybody can shed some light on why sscanf() failes here, I'd love + //XXX to hear about that! + char *s2 = NULL; + int l2 = strlen(s); + if (s[l2 - 2] == ':') { // note that 's' has a trailing '\n' + s2 = (char *)malloc(l2 + 2); + strcat(strn0cpy(s2, s, l2), " \n"); + s = s2; + } if (8 <= sscanf(s, "%d:%d:%a[^:]:%d:%d:%d:%d:%a[^:\n]:%a[^\n]", &active, &channel, &buffer1, &start, &stop, &priority, &lifetime, &buffer2, &summary)) { + if (summary && !*skipspace(summary)) { + delete summary; + summary = NULL; + } //TODO add more plausibility checks day = ParseDay(buffer1); strn0cpy(file, buffer2, MaxFileName); delete buffer1; delete buffer2; + delete s2; return day != 0; } + delete s2; return false; } @@ -550,6 +606,17 @@ cChannel *cChannels::GetByNumber(int Number) return NULL; } +cChannel *cChannels::GetByServiceID(unsigned short ServiceId) +{ + cChannel *channel = (cChannel *)First(); + while (channel) { + if (channel->pnr == ServiceId) + return channel; + channel = (cChannel *)channel->Next(); + } + return NULL; +} + bool cChannels::SwitchTo(int Number, cDvbApi *DvbApi) { cChannel *channel = GetByNumber(Number); @@ -562,14 +629,6 @@ const char *cChannels::GetChannelNameByNumber(int Number) return channel ? channel->name : NULL; } -eKeys cChannels::ShowChannel(int Number, bool Switched, bool Group) -{ - cChannel *channel = Group ? Get(Number) : GetByNumber(Number); - if (channel) - return Interface->DisplayChannel(channel->number, channel->name, !Switched || Setup.ShowInfoOnChSwitch); - return kNone; -} - // -- cTimers ---------------------------------------------------------------- cTimers Timers; @@ -599,6 +658,9 @@ cSetup::cSetup(void) MarkInstantRecord = 1; LnbFrequLo = 9750; LnbFrequHi = 10600; + SetSystemTime = 0; + MarginStart = 2; + MarginStop = 10; } bool cSetup::Parse(char *s) @@ -613,6 +675,9 @@ bool cSetup::Parse(char *s) else if (!strcasecmp(Name, "MarkInstantRecord")) MarkInstantRecord = atoi(Value); else if (!strcasecmp(Name, "LnbFrequLo")) LnbFrequLo = atoi(Value); else if (!strcasecmp(Name, "LnbFrequHi")) LnbFrequHi = atoi(Value); + else if (!strcasecmp(Name, "SetSystemTime")) SetSystemTime = atoi(Value); + else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value); + else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value); else return false; return true; @@ -660,6 +725,9 @@ bool cSetup::Save(const char *FileName) fprintf(f, "MarkInstantRecord = %d\n", MarkInstantRecord); fprintf(f, "LnbFrequLo = %d\n", LnbFrequLo); fprintf(f, "LnbFrequHi = %d\n", LnbFrequHi); + fprintf(f, "SetSystemTime = %d\n", SetSystemTime); + fprintf(f, "MarginStart = %d\n", MarginStart); + fprintf(f, "MarginStop = %d\n", MarginStop); fclose(f); isyslog(LOG_INFO, "saved setup to %s", FileName); return true; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 1.27 2000/10/08 16:33:48 kls Exp $ + * $Id: config.h 1.29 2000/11/01 13:42:29 kls Exp $ */ #ifndef __CONFIG_H @@ -15,9 +15,10 @@ #include <string.h> #include <time.h> #include "dvbapi.h" +#include "eit.h" #include "tools.h" -#define VDRVERSION "0.66" +#define VDRVERSION "0.67" #define MaxBuffer 10000 @@ -113,6 +114,7 @@ public: char file[MaxFileName]; char *summary; cTimer(bool Instant = false); + cTimer(const cEventInfo *EventInfo); ~cTimer(); cTimer& operator= (const cTimer &Timer); const char *ToText(void); @@ -204,10 +206,10 @@ public: int GetNextNormal(int Idx); // Get next normal channel (not group) void ReNumber(void); // Recalculate 'number' based on channel type cChannel *GetByNumber(int Number); + cChannel *GetByServiceID(unsigned short ServiceId); const char *GetChannelNameByNumber(int Number); bool SwitchTo(int Number, cDvbApi *DvbApi = NULL); int MaxNumber(void) { return maxNumber; } - eKeys ShowChannel(int Number, bool Switched, bool Group = false); }; class cTimers : public cConfig<cTimer> { @@ -234,6 +236,8 @@ public: int MarkInstantRecord; int LnbFrequLo; int LnbFrequHi; + int SetSystemTime; + int MarginStart, MarginStop; cSetup(void); bool Load(const char *FileName); bool Save(const char *FileName = NULL); diff --git a/dvb.c.071.diff b/dvb.c.071.diff index 647b0de..ac7ba3a 100644 --- a/dvb.c.071.diff +++ b/dvb.c.071.diff @@ -81,7 +81,7 @@ BlitBitmap(dvb, dvb->osdwin, x0, y0+i*lpb, 1); - data+=bpl; + data+=lpb*inc; //XXX kls: incrementing must be done in "one byte per pixel" -+ ddelay(2); //XXX kls: without this the block is sometimes not fully displayed - firmware bug? ++ ddelay(3); //XXX kls: without this the block is sometimes not fully displayed - firmware bug? } if (brest) { - LoadBitmap(dvb, BITMAP8, w, brest/bpl, inc, data); @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbapi.c 1.31 2000/10/08 16:14:45 kls Exp $ + * $Id: dvbapi.c 1.34 2000/11/01 09:19:27 kls Exp $ */ #include "dvbapi.h" @@ -25,6 +25,7 @@ extern "C" { #include "videodir.h" #define VIDEODEVICE "/dev/video" +#define VBIDEVICE "/dev/vbi" // The size of the array used to buffer video data: #define VIDEOBUFSIZE (1024*1024) @@ -1084,14 +1085,27 @@ int cDvbApi::NumDvbApis = 0; cDvbApi *cDvbApi::dvbApi[MAXDVBAPI] = { NULL }; cDvbApi *cDvbApi::PrimaryDvbApi = NULL; -cDvbApi::cDvbApi(const char *FileName) +cDvbApi::cDvbApi(const char *VideoFileName, const char *VbiFileName) { + siProcessor = NULL; pidRecord = pidReplay = 0; fromRecord = toRecord = -1; fromReplay = toReplay = -1; - videoDev = open(FileName, O_RDWR | O_NONBLOCK); - if (videoDev < 0) - LOG_ERROR; + videoDev = open(VideoFileName, O_RDWR | O_NONBLOCK); + if (videoDev >= 0) { + siProcessor = new cSIProcessor(VbiFileName); + if (!NumDvbApis) // only the first one shall set the system time + siProcessor->SetUseTSTime(Setup.SetSystemTime); + siProcessor->AddFilter(0x14, 0x70); // TDT + siProcessor->AddFilter(0x14, 0x73); // TOT + siProcessor->AddFilter(0x12, 0x4e); // event info, actual TS, present/following + siProcessor->AddFilter(0x12, 0x4f); // event info, other TS, present/following + siProcessor->AddFilter(0x12, 0x50); // event info, actual TS, schedule + siProcessor->AddFilter(0x12, 0x60); // event info, other TS, schedule + siProcessor->Start(); + } + else + LOG_ERROR_STR(VideoFileName); cols = rows = 0; ovlGeoSet = ovlStat = ovlFbSet = false; @@ -1104,7 +1118,7 @@ cDvbApi::cDvbApi(const char *FileName) nonl(); cbreak(); noecho(); - timeout(1000); + timeout(10); #endif #if defined(DEBUG_OSD) memset(&colorPairs, 0, sizeof(colorPairs)); @@ -1121,6 +1135,7 @@ cDvbApi::cDvbApi(const char *FileName) cDvbApi::~cDvbApi() { if (videoDev >= 0) { + delete siProcessor; Close(); Stop(); StopRecord(); @@ -1174,11 +1189,9 @@ int cDvbApi::Index(void) bool cDvbApi::Init(void) { - char fileName[strlen(VIDEODEVICE) + 10]; - int i; - NumDvbApis = 0; - for (i = 0; i < MAXDVBAPI; i++) { + for (int i = 0; i < MAXDVBAPI; i++) { + char fileName[strlen(VIDEODEVICE) + 10]; sprintf(fileName, "%s%d", VIDEODEVICE, i); if (access(fileName, F_OK | R_OK | W_OK) == 0) { dsyslog(LOG_INFO, "probing %s", fileName); @@ -1188,7 +1201,9 @@ bool cDvbApi::Init(void) int r = ioctl(f, VIDIOCGCAP, &cap); close(f); if (r == 0 && (cap.type & VID_TYPE_DVB)) { - dvbApi[i] = new cDvbApi(fileName); + char vbiFileName[strlen(VBIDEVICE) + 10]; + sprintf(vbiFileName, "%s%d", VBIDEVICE, i); + dvbApi[i] = new cDvbApi(fileName, vbiFileName); NumDvbApis++; } } @@ -1223,6 +1238,13 @@ void cDvbApi::Cleanup(void) PrimaryDvbApi = NULL; } +const cSchedules *cDvbApi::Schedules(cThreadLock *ThreadLock) const +{ + if (siProcessor && ThreadLock->Lock(siProcessor)) + return siProcessor->Schedules(); + return NULL; +} + bool cDvbApi::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY) { int result = 0; @@ -1576,6 +1598,24 @@ void cDvbApi::ClrEol(int x, int y, eDvbColor color) Fill(x, y, cols - x, 1, color); } +int cDvbApi::CellWidth(void) +{ +#ifdef DEBUG_OSD + return 1; +#else + return charWidth; +#endif +} + +int cDvbApi::Width(unsigned char c) +{ +#ifdef DEBUG_OSD + return 1; +#else + return osd->Width(c); +#endif +} + void cDvbApi::Text(int x, int y, const char *s, eDvbColor colorFg, eDvbColor colorBg) { if (x < 0) x = cols + x; @@ -1671,8 +1711,11 @@ bool cDvbApi::SetChannel(int FrequencyMHz, char Polarization, int Diseqc, int Sr front.fec = 8; front.AFC = 1; ioctl(videoDev, VIDIOCSFRONTEND, &front); - if (front.sync & 0x1F == 0x1F) + if (front.sync & 0x1F == 0x1F) { + if (siProcessor) + siProcessor->SetCurrentServiceID(Pnr); return true; + } esyslog(LOG_ERR, "ERROR: channel not sync'ed (front.sync=%X)!", front.sync); } return false; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbapi.h 1.18 2000/10/03 11:26:10 kls Exp $ + * $Id: dvbapi.h 1.20 2000/11/01 09:18:50 kls Exp $ */ #ifndef __DVBAPI_H @@ -21,6 +21,7 @@ typedef unsigned char __u8; #include <stdio.h> #include <dvb.h> #include "dvbosd.h" +#include "eit.h" // Overlay facilities #define MAXCLIPRECTS 100 @@ -44,7 +45,8 @@ public: class cDvbApi { private: int videoDev; - cDvbApi(const char *FileName); + cSIProcessor *siProcessor; + cDvbApi(const char *VideoFileName, const char *VbiFileName); public: ~cDvbApi(); @@ -71,6 +73,14 @@ public: // Closes down all DVB devices. // Must be called at the end of the program. + // EIT facilities + + const cSchedules *Schedules(cThreadLock *ThreadLock) const; + // Caller must provide a cThreadLock which has to survive the entire + // time the returned cSchedules is accessed. Once the cSchedules is no + // longer used, the cThreadLock must be destroyed. + void SetUseTSTime(bool On) { if (siProcessor) siProcessor->SetUseTSTime(On); } + // Image Grab facilities bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); @@ -112,6 +122,8 @@ public: void Clear(void); void Fill(int x, int y, int w, int h, eDvbColor color = clrBackground); void ClrEol(int x, int y, eDvbColor color = clrBackground); + int CellWidth(void); + int Width(unsigned char c); void Text(int x, int y, const char *s, eDvbColor colorFg = clrWhite, eDvbColor colorBg = clrBackground); void Flush(void); @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbosd.c 1.3 2000/10/07 14:42:48 kls Exp $ + * $Id: dvbosd.c 1.4 2000/11/01 09:13:32 kls Exp $ */ #include "dvbosd.h" @@ -76,6 +76,11 @@ void cBitmap::SetPixel(int x, int y, eDvbColor Color) } } +int cBitmap::Width(unsigned char c) +{ + return font ? font->Width(c) : -1; +} + void cBitmap::Text(int x, int y, const char *s, eDvbColor ColorFg, eDvbColor ColorBg) { if (bitmap) { @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbosd.h 1.1 2000/10/01 15:00:00 kls Exp $ + * $Id: dvbosd.h 1.2 2000/11/01 09:13:44 kls Exp $ */ #ifndef __DVBOSD_H @@ -56,6 +56,7 @@ public: void SetFont(eDvbFont Font); bool Dirty(void); void SetPixel(int x, int y, eDvbColor Color); + int Width(unsigned char c); void Text(int x, int y, const char *s, eDvbColor ColorFg = clrWhite, eDvbColor ColorBg = clrBackground); void Fill(int x1, int y1, int x2, int y2, eDvbColor Color); void Clear(void); @@ -13,20 +13,606 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: eit.c 1.4 2000/10/01 14:09:05 kls Exp $ + * $Id: eit.c 1.7 2000/11/02 17:06:19 kls Exp $ ***************************************************************************/ #include "eit.h" +#include <ctype.h> +#include <dvb_comcode.h> +#include <dvb_v4l.h> +#include <fcntl.h> +#include <fstream.h> +#include <iomanip.h> #include <iostream.h> +#include <minmax.h> #include <stdio.h> -#include <unistd.h> -#include <fcntl.h> -#include <time.h> +#include <stdlib.h> +#include <string.h> #include <sys/ioctl.h> -#include <dvb_comcode.h> -#include "tools.h" +#include <sys/poll.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.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; + + 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(LOG_INFO, "Time parsed = %s\n", ctime(&mjdtime)); +} + +/** */ +bool cMJD::SetSystemTime() +{ + struct tm *ptm; + time_t loctim; + + ptm = localtime(&mjdtime); + loctim = time(NULL); + + if (abs(mjdtime - loctim) > 2) + { + isyslog(LOG_INFO, "System Time = %s (%ld)\n", ctime(&loctim), loctim); + isyslog(LOG_INFO, "Local Time = %s (%ld)\n", ctime(&mjdtime), mjdtime); + if (stime(&mjdtime) < 0) + esyslog(LOG_ERR, "ERROR while setting system time: %s", strerror(errno)); + return true; + } + + return false; +} +/** */ +time_t cMJD::GetTime_t() +{ + return mjdtime; +} + +// --- cTDT ------------------------------------------------------------------ typedef struct { + u_char table_id : 8; + +#if BYTE_ORDER == BIG_ENDIAN + u_char section_syntax_indicator : 1; + u_char : 3; + u_char section_length_hi : 4; +#else + u_char section_length_hi : 4; + u_char : 3; + u_char section_syntax_indicator : 1; +#endif + + u_char section_length_lo : 8; + + + u_char utc_date_hi : 8; + u_char utc_date_lo : 8; + u_char utc_hour : 4; + u_char utc_hour_ten : 4; + u_char utc_min : 4; + u_char utc_min_ten : 4; + u_char utc_sec : 4; + u_char utc_sec_ten : 4; +} tdt_t; + +class cTDT { +public: + cTDT(tdt_t *ptdt); + ~cTDT(); + /** */ + bool SetSystemTime(); +protected: // Protected attributes + /** */ + tdt_t tdt; + /** */ + cMJD * mjd; +}; + +cTDT::cTDT(tdt_t *ptdt) +{ + tdt = *ptdt; + mjd = new cMJD(tdt.utc_date_hi, tdt.utc_date_lo, + tdt.utc_hour_ten * 10 + tdt.utc_hour, + tdt.utc_min_ten * 10 + tdt.utc_min, + tdt.utc_sec_ten * 10 + tdt.utc_sec); +} + +cTDT::~cTDT() +{ +} +/** */ +bool cTDT::SetSystemTime() +{ + return mjd->SetSystemTime(); +} + +// --- cEventInfo ------------------------------------------------------------ + +cEventInfo::cEventInfo(unsigned short serviceid, unsigned short eventid) +{ + pTitle = NULL; + pSubtitle = NULL; + pExtendedDescription = NULL; + bIsPresent = bIsFollowing = false; + lDuration = 0; + tTime = 0; + uEventID = eventid; + uServiceID = serviceid; + cExtendedDescriptorNumber = 0; + nChannelNumber = 0; +} + +cEventInfo::~cEventInfo() +{ + delete pTitle; + delete pSubtitle; + delete 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::SetFollowing(bool foll) +{ + bIsFollowing = foll; +} +/** */ +const char * cEventInfo::GetDate() const +{ + static char szDate[25]; + + strftime(szDate, sizeof(szDate), "%d.%m.%Y", localtime(&tTime)); + + return szDate; +} +/** */ +const char * cEventInfo::GetTimeString() const +{ + static char szTime[25]; + + strftime(szTime, sizeof(szTime), "%R", localtime(&tTime)); + + return szTime; +} +/** */ +const char * cEventInfo::GetEndTimeString() const +{ + static char szEndTime[25]; + time_t tEndTime = tTime + lDuration; + + strftime(szEndTime, sizeof(szEndTime), "%R", localtime(&tEndTime)); + + return szEndTime; +} +/** */ +time_t cEventInfo::GetTime() const +{ + return tTime; +} +/** */ +long cEventInfo::GetDuration() const +{ + return lDuration; +} +/** */ +unsigned short cEventInfo::GetEventID() const +{ + return uEventID; +} +/** */ +bool cEventInfo::SetTitle(char *string) +{ + if (string == NULL) + return false; + + pTitle = strdup(string); + if (pTitle == NULL) + return false; + + return true; +} +/** */ +bool cEventInfo::SetSubtitle(char *string) +{ + if (string == NULL) + return false; + + pSubtitle = strdup(string); + if (pSubtitle == NULL) + return false; + + return true; +} +/** */ +bool cEventInfo::AddExtendedDescription(char *string) +{ + int size = 0; + bool first = true; + char *p; + + if (string == NULL) + return false; + + if (pExtendedDescription) + { + first = false; + size += strlen(pExtendedDescription); + } + + size += (strlen(string) + 1); + + p = (char *)realloc(pExtendedDescription, size); + if (p == NULL) + return false; + + if (first) + *p = 0; + + strcat(p, string); + + pExtendedDescription = p; + + return true; +} +/** */ +void cEventInfo::SetTime(time_t t) +{ + tTime = t; +} +/** */ +void cEventInfo::SetDuration(long l) +{ + lDuration = l; +} +/** */ +void cEventInfo::SetEventID(unsigned short evid) +{ + uEventID = evid; +} +/** */ +void cEventInfo::SetServiceID(unsigned short servid) +{ + uServiceID = servid; +} +/** */ +u_char cEventInfo::GetExtendedDescriptorNumber() const +{ + return cExtendedDescriptorNumber; +} +/** */ +void cEventInfo::IncreaseExtendedDescriptorNumber() +{ + cExtendedDescriptorNumber++; +} + +/** */ +unsigned short cEventInfo::GetServiceID() const +{ + return uServiceID; +} + +// --- cSchedule ------------------------------------------------------------- + +cSchedule::cSchedule(unsigned short servid) +{ + pPresent = pFollowing = NULL; + uServiceID = servid; +} + + +cSchedule::~cSchedule() +{ +} +/** */ +const cEventInfo * cSchedule::GetPresentEvent() const +{ + // checking temporal sanity of present event (kls 2000-11-01) + time_t now = time(NULL); + if (pPresent && !(pPresent->GetTime() <= now && now <= pPresent->GetTime() + pPresent->GetDuration())) + { + cEventInfo *pe = Events.First(); + while (pe != NULL) + { + if (pe->GetTime() <= now && now <= pe->GetTime() + pe->GetDuration()) + return pe; + pe = Events.Next(pe); + } + } + return pPresent; +} +/** */ +const cEventInfo * cSchedule::GetFollowingEvent() const +{ + // checking temporal sanity of following event (kls 2000-11-01) + time_t now = time(NULL); + const cEventInfo *pr = GetPresentEvent(); // must have it verified! + if (pFollowing && !(pr && pr->GetTime() + pr->GetDuration() <= pFollowing->GetTime())) + { + int minDt = INT_MAX; + cEventInfo *pe = Events.First(), *pf = NULL; + while (pe != NULL) + { + int dt = pe->GetTime() - now; + if (dt > 0 && dt < minDt) + { + minDt = dt; + pf = pe; + } + pe = Events.Next(pe); + } + return pf; + } + return pFollowing; +} +/** */ +void cSchedule::SetServiceID(unsigned short servid) +{ + uServiceID = servid; +} +/** */ +unsigned short cSchedule::GetServiceID() const +{ + return uServiceID; +} +/** */ +const cEventInfo * cSchedule::GetEvent(unsigned short uEventID) const +{ + cEventInfo *pe = Events.First(); + while (pe != NULL) + { + if (pe->GetEventID() == uEventID) + return pe; + + pe = Events.Next(pe); + } + + return NULL; +} +/** */ +const cEventInfo * cSchedule::GetEvent(time_t tTime) const +{ + cEventInfo *pe = Events.First(); + while (pe != NULL) + { + if (pe->GetTime() == tTime) + return pe; + + pe = Events.Next(pe); + } + + return NULL; +} +/** */ +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() < tTime) + { + Events.Del(pEvent); + a--; + } + } +} + +// --- cSchedules ------------------------------------------------------------ + +cSchedules::cSchedules() +{ + pCurrentSchedule = NULL; + uCurrentServiceID = 0; +} + +cSchedules::~cSchedules() +{ +} +/** */ +bool cSchedules::SetCurrentServiceID(unsigned short servid) +{ + pCurrentSchedule = GetSchedule(servid); + if (pCurrentSchedule == NULL) + { + Add(new cSchedule(servid)); + pCurrentSchedule = GetSchedule(servid); + if (pCurrentSchedule == NULL) + return false; + } + + uCurrentServiceID = servid; + + return true; +} +/** */ +const cSchedule * cSchedules::GetSchedule() const +{ + return pCurrentSchedule; +} +/** */ +const cSchedule * cSchedules::GetSchedule(unsigned short servid) const +{ + cSchedule *p; + + p = First(); + while (p != NULL) + { + if (p->GetServiceID() == servid) + return p; + p = Next(p); + } + + return NULL; +} + +/** */ +void cSchedules::Cleanup() +{ + cSchedule *p; + + p = First(); + while (p != NULL) + { + p->Cleanup(time(NULL)); + p = Next(p); + } +} + +// --- cEIT ------------------------------------------------------------------ + +#define DEC(N) dec << setw(N) << setfill(int('0')) +#define HEX(N) hex << setw(N) << setfill(int('0')) + +#define EIT_STUFFING_DESCRIPTOR 0x42 +#define EIT_LINKAGE_DESCRIPTOR 0x4a +#define EIT_SHORT_EVENT_DESCRIPTOR 0x4d +#define EIT_EXTENDED_EVENT_DESCRIPTOR 0x4e +#define EIT_TIME_SHIFTED_EVENT_DESCRIPTOR 0x4f +#define EIT_COMPONENT_DESCRIPTOR 0x50 +#define EIT_CA_IDENTIFIER_DESCRIPTOR 0x53 +#define EIT_CONTENT_DESCRIPTOR 0x54 +#define EIT_PARENTAL_RATING_DESCRIPTOR 0x55 +#define EIT_TELEPHONE_DESCRIPTOR 0x57 +#define EIT_MULTILINGUAL_COMPONENT_DESCRIPTOR 0x5e +#define EIT_PRIVATE_DATE_SPECIFIER_DESCRIPTOR 0x5f +#define EIT_SHORT_SMOOTHING_BUFFER_DESCRIPTOR 0x61 +#define EIT_DATA_BROADCAST_DESCRIPTOR 0x64 +#define EIT_PDC_DESCRIPTOR 0x69 + +typedef struct eit_struct { u_char table_id : 8; #if BYTE_ORDER == BIG_ENDIAN @@ -64,9 +650,7 @@ typedef struct { u_char segment_last_table_id : 8; } eit_t; -#define EIT_SIZE 14 - -struct eit_loop_struct1 { +typedef struct eit_loop_struct { u_char event_id_hi : 8; u_char event_id_lo : 8; @@ -79,12 +663,12 @@ struct eit_loop_struct1 { u_char time_second : 4; u_char time_second_ten : 4; - u_char dur_hour_ten : 4; u_char dur_hour : 4; - u_char dur_minute_ten : 4; + u_char dur_hour_ten : 4; u_char dur_minute : 4; - u_char dur_second_ten : 4; + u_char dur_minute_ten : 4; u_char dur_second : 4; + u_char dur_second_ten : 4; #if BYTE_ORDER == BIG_ENDIAN u_char running_status : 3; @@ -97,12 +681,9 @@ struct eit_loop_struct1 { #endif u_char descriptors_loop_length_lo : 8; -}; +} eit_loop_t; -#define EIT_SHORT_EVENT_DESCRIPTOR 0x4d -#define EIT_SHORT_EVENT_DESCRIPTOR_SIZE 6 - -struct eit_short_event_descriptor_struct { +typedef struct eit_short_event_struct { u_char descriptor_tag : 8; u_char descriptor_length : 8; @@ -111,415 +692,583 @@ struct eit_short_event_descriptor_struct { u_char language_code_3 : 8; u_char event_name_length : 8; -}; - -#define EIT_EXTENDED_EVENT_DESCRIPOR 0x4e - -#define EIT_DESCRIPTOR_SIZE - -typedef struct eit_event_struct { - u_char event_id_hi : 8; - u_char event_id_lo : 8; - - u_char start_time_1 : 8; - u_char start_time_2 : 8; - u_char start_time_3 : 8; - u_char start_time_4 : 8; - u_char start_time_5 : 8; - - u_char duration_1 : 8; - u_char duration_2 : 8; - u_char duration_3 : 8; - -#if BYTE_ORDER == BIG_ENDIAN - u_char running_status : 3; - u_char free_CA_mode : 1; - u_char descriptors_loop_length_hi : 4; -#else - u_char descriptors_loop_length_hi : 4; - u_char free_CA_mode : 1; - u_char running_status : 3; -#endif - - u_char descriptors_loop_length_lo : 8; - -} eit_event_t; -#define EIT_LOOP_SIZE 12 - - -typedef struct tot_t { - u_char table_id : 8; - -#if BYTE_ORDER == BIG_ENDIAN - u_char section_syntax_indicator : 1; - u_char : 3; - u_char section_length_hi : 4; -#else - u_char section_length_hi : 4; - u_char : 3; - u_char section_syntax_indicator : 1; -#endif - - u_char date_hi : 8; - u_char date_lo : 8; - u_char time_hour : 4; - u_char time_hour_ten : 4; - u_char time_minute : 4; - u_char time_minute_ten : 4; - u_char time_second : 4; - u_char time_second_ten : 4; - -#if BYTE_ORDER == BIG_ENDIAN - u_char : 4; - u_char descriptor_loop_length_hi : 4; -#else - u_char descriptor_loop_length_hi : 4; - u_char : 4; -#endif - - u_char descriptor_loop_length_lo : 8; -} tot_t; - -typedef struct local_time_offset { +} eit_short_event_t; +typedef struct eit_extended_event_struct { u_char descriptor_tag : 8; u_char descriptor_length : 8; + u_char last_descriptor_number : 4; + u_char descriptor_number : 4; + u_char language_code_1 : 8; u_char language_code_2 : 8; u_char language_code_3 : 8; - u_char : 8; - - u_char offset_hour : 4; - u_char offset_hour_ten : 4; - u_char offset_minute : 4; - u_char offset_minute_ten : 4; + u_char length_of_items : 8; +} eit_extended_event_t; + +typedef struct eit_content_descriptor { + u_char descriptor_tag : 8; + u_char descriptor_length : 8; +} eit_content_descriptor_t; - u_char change_date_hi : 8; - u_char change_date_lo : 8; - u_char change_time_hour : 4; - u_char change_time_hour_ten : 4; - u_char change_time_minute : 4; - u_char change_time_minute_ten : 4; - u_char change_time_second : 4; - u_char change_time_second_ten : 4; +typedef struct eit_content_loop { + u_char content_nibble_level_2 : 4; + u_char content_nibble_level_1 : 4; + u_char user_nibble_2 : 4; + u_char user_nibble_1 : 4; +} eit_content_loop_t; - u_char next_offset_hour : 4; - u_char next_offset_hour_ten : 4; - u_char next_offset_minute : 4; - u_char next_offset_minute_ten : 4; -} local_time_offset; +class cEIT { +private: + cSchedules *schedules; +public: + cEIT(void *buf, int length, cSchedules *Schedules); + ~cEIT(); + /** */ + int ProcessEIT(); -cEIT::cEIT() +protected: // Protected methods + /** */ + int strdvbcpy(unsigned char *dst, unsigned char *src, int max); + /** returns true if this EIT covers a +present/following information, false if it's +schedule information */ + bool IsPresentFollowing(); + /** */ + bool WriteShortEventDescriptor(unsigned short service, eit_loop_t *eitloop, u_char *buf); + /** */ + bool WriteExtEventDescriptor(unsigned short service, eit_loop_t *eitloop, u_char *buf); +protected: // Protected attributes + int buflen; +protected: // Protected attributes + /** */ + u_char buffer[4097]; + /** Table ID of this EIT struct */ + u_char tid; + /** EITs service id (program number) */ + u_short pid; +}; + +cEIT::cEIT(void * buf, int length, cSchedules *Schedules) { - cszBitFilter = "/dev/vbi"; - if((fsvbi = open(cszBitFilter, O_RDWR))<0) - { - fsvbi = 0; - esyslog(LOG_ERR, "Failed to open DVB bitfilter device: %s", cszBitFilter); - return; - } + buflen = min((unsigned int)length, sizeof(buffer)); + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, buf, buflen); + tid = buffer[0]; + schedules = Schedules; } cEIT::~cEIT() { - if (fsvbi != 0) - close(fsvbi); - fsvbi = 0; } -/** Set the bitfilter in vbi device to return -correct tables */ -int cEIT::SetBitFilter(unsigned short pid, unsigned short section, unsigned short mode) -{ - struct bitfilter filt = { - pid, - { section, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, - mode,0, - FILTER_MEM, - {}, - }; - - if (ioctl(fsvbi, VIDIOCSBITFILTER, &filt) < 0) - return 0xffff; - return 0; -} /** */ -int cEIT::GetSection(unsigned char *buf, ushort PID, unsigned char sec) +int cEIT::ProcessEIT() { - int seclen=0; - unsigned short handle, pid; - unsigned char section, sectionnum=0xff, maxsec=0; - - if ((handle = SetBitFilter(PID, (sec<<8)|0x00ff, SECTION_CONTINUOS))==0xffff) - return -1; + int bufact = 0; + eit_t *eit; + eit_loop_t *eitloop; + u_char tmp[256]; + + if (bufact + (int)sizeof(eit_t) > buflen) + return 0; + eit = (eit_t *)buffer; + bufact += sizeof(eit_t); + + unsigned int service = (eit->service_id_hi << 8) | eit->service_id_lo; + + while(bufact + (int)sizeof(eit_loop_t) <= buflen) + { + eitloop = (eit_loop_t *)&buffer[bufact]; + bufact += sizeof(eit_loop_t); + + int descdatalen = (eitloop->descriptors_loop_length_hi << 8) + eitloop->descriptors_loop_length_lo; + int descdataact = 0; + + while (descdataact < descdatalen && bufact < buflen) + { + switch (buffer[bufact]) + { + eit_content_descriptor_t *cont; + eit_content_loop_t *contloop; + + case EIT_STUFFING_DESCRIPTOR : + //dsyslog(LOG_INFO, "Found EIT_STUFFING_DESCRIPTOR"); + break; + + case EIT_LINKAGE_DESCRIPTOR : + //dsyslog(LOG_INFO, "Found EIT_LINKAGE_DESCRIPTOR"); + break; + + case EIT_SHORT_EVENT_DESCRIPTOR: + WriteShortEventDescriptor(service, eitloop, &buffer[bufact]); + break; - seclen=0; - if (!cFile::AnyFileReady(fsvbi, 20000)) - { - //cerr << "Timeout\n"; - return -1; - } + case EIT_EXTENDED_EVENT_DESCRIPTOR: + WriteExtEventDescriptor(service, eitloop, &buffer[bufact]); + break; + + case EIT_TIME_SHIFTED_EVENT_DESCRIPTOR : + //dsyslog(LOG_INFO, "Found EIT_TIME_SHIFTED_EVENT_DESCRIPTOR"); + break; - read(fsvbi, buf, 8); - seclen=(buf[6]<<8)|buf[7]; - pid=(buf[4]<<8)|buf[5]; + case EIT_COMPONENT_DESCRIPTOR : + strdvbcpy(tmp, &buffer[bufact + 8], buffer[bufact + 1] - 6); + //dsyslog(LOG_INFO, "Found EIT_COMPONENT_DESCRIPTOR %c%c%c 0x%02x/0x%02x/0x%02x '%s'\n", buffer[bufact + 5], buffer[bufact + 6], buffer[bufact + 7], buffer[2], buffer[3], buffer[4], tmp); + break; - read(fsvbi, buf, seclen); - section=buf[0]; - sectionnum=buf[6]; - maxsec=buf[7]; + case EIT_CA_IDENTIFIER_DESCRIPTOR : + //dsyslog(LOG_INFO, "Found EIT_CA_IDENTIFIER_DESCRIPTOR"); + break; - //cerr << "secnum: " << HEX(2) << (int)sectionnum - // << ", secmax: " << HEX(2) << (int) msecnum << "\n"; + case EIT_CONTENT_DESCRIPTOR : + cont = (eit_content_descriptor_t *)buffer; + contloop = (eit_content_loop_t *)&buffer[sizeof(eit_content_descriptor_t)]; + //dsyslog(LOG_INFO, "Found EIT_CONTENT_DESCRIPTOR 0x%02x/0x%02x\n", contloop->content_nibble_level_1, contloop->content_nibble_level_2); + break; - CloseFilter(handle); + case EIT_PARENTAL_RATING_DESCRIPTOR : + //dsyslog(LOG_INFO, "Found EIT_PARENTAL_RATING_DESCRIPTOR"); + break; - return seclen; -} + case EIT_TELEPHONE_DESCRIPTOR : + //dsyslog(LOG_INFO, "Found EIT_TELEPHONE_DESCRIPTOR"); + break; + + case EIT_MULTILINGUAL_COMPONENT_DESCRIPTOR : + //dsyslog(LOG_INFO, "Found EIT_MULTILINGUAL_COMPONENT_DESCRIPTOR"); + break; + + case EIT_PRIVATE_DATE_SPECIFIER_DESCRIPTOR : + //dsyslog(LOG_INFO, "Found EIT_PRIVATE_DATE_SPECIFIER_DESCRIPTOR"); + break; + + case EIT_SHORT_SMOOTHING_BUFFER_DESCRIPTOR : + //dsyslog(LOG_INFO, "Found EIT_SHORT_SMOOTHING_BUFFER_DESCRIPTOR"); + break; + + case EIT_DATA_BROADCAST_DESCRIPTOR : + //dsyslog(LOG_INFO, "Found EIT_DATA_BROADCAST_DESCRIPTOR"); + break; + + case EIT_PDC_DESCRIPTOR : + //dsyslog(LOG_INFO, "Found EIT_PDC_DESCRIPTOR"); + break; + + default: + //dsyslog(LOG_INFO, "Found unhandled descriptor 0x%02x with length of %04d\n", (int)buffer[bufact], (int)buffer[bufact + 1]); + break; + } + descdataact += (buffer[bufact + 1] + 2); + bufact += (buffer[bufact + 1] + 2); + } + } -/** */ -int cEIT::CloseFilter(unsigned short handle) -{ - if (ioctl(fsvbi, VIDIOCSSHUTDOWNFILTER, &handle)<0) - return -1; return 0; } /** */ -char * cEIT::mjd2string(unsigned short mjd) +int cEIT::strdvbcpy(unsigned char *dst, unsigned char *src, int max) { - int y, m, d, k; - static char buf[20]; + int a = 0; - y = (int) ((mjd - 15078.2) / 365.25); - m = (int) ((mjd - 14956.1 - (int)(y * 365.25)) / 30.6001); - d = (int) (mjd - 14956 - (int)(y * 365.25) - (int)(m * 30.6001)); - k = (m == 14 || m == 15) ? 1 : 0; - y = y + k; - m = m - 1 - k * 12; - sprintf(buf, "%d.%d.%4d", d, m, y + 1900); + if (*src == 0x05 || (*src >= 0x20 && *src <= 0xff)) + { + for (a = 0; a < max; a++) + { + if (*src == 0) + break; + + if ((*src >= ' ' && *src <= '~') || (*src >= 0xa0 && *src <= 0xff)) + *dst++ = *src++; + else + { + // if ((*src > '~' && *src < 0xa0) || *src == 0xff) + // cerr << "found special character 0x" << HEX(2) << (int)*src << endl; + src++; + } + } + *dst = 0; + } + else + { + const char *ret; + + switch (*src) + { + case 0x01: ret = "Coding according to character table 1"; break; + case 0x02: ret = "Coding according to character table 2"; break; + case 0x03: ret = "Coding according to character table 3"; break; + case 0x04: ret = "Coding according to character table 4"; break; + case 0x10: ret = "Coding according to ISO/IEC 8859"; break; + case 0x11: ret = "Coding according to ISO/IEC 10646"; break; + case 0x12: ret = "Coding according to KSC 5601"; break; + default: ret = "Unknown coding"; break; + } + strncpy((char *)dst, ret, max); + } + return a; +} + +/** 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(buf); + return false; } /** */ -int cEIT::GetEIT() +bool cEIT::WriteShortEventDescriptor(unsigned short service, eit_loop_t *eitloop, u_char *buf) { - unsigned char buf[4096+1]; // max. allowed size for any EIT section (+1 for safety ;-) - eit_t *eit; - struct eit_loop_struct1 *eitloop; - struct eit_short_event_descriptor_struct *eitevt; - unsigned int seclen; - unsigned short handle, pid; - eit_event * pevt = (eit_event *)0; - time_t tstart; + u_char tmp[256]; + eit_short_event_t *evt = (eit_short_event_t *)buf; + unsigned short eventid = (unsigned short)((eitloop->event_id_hi << 8) | eitloop->event_id_lo); + cEventInfo *pEvent; + + //isyslog(LOG_INFO, "Found Short Event Descriptor"); - if ((handle = SetBitFilter(0x12, (0x4e << 8) | 0x00ff, SECTION_CONTINUOS))==0xffff) + cSchedule *pSchedule = (cSchedule *)schedules->GetSchedule(service); + if (pSchedule == NULL) { - return -1; + schedules->Add(new cSchedule(service)); + pSchedule = (cSchedule *)schedules->GetSchedule(service); + if (pSchedule == NULL) + return false; } -/* - pid_t process = fork(); - if (process < 0) + + /* cSchedule::GetPresentEvent() and cSchedule::GetFollowingEvent() verify + the temporal sanity of these events, so calling them here appears to + be a bad idea... (kls 2000-11-01) + // + // if we are working on a present/following info, let's see whether + // we already have present/following info for this service and if yes + // check whether it's the same eventid, if yes, just return, nothing + // left to do. + // + if (IsPresentFollowing()) { - cerr << "GetEIT -1" << endl; - return -1; + if (eitloop->running_status == 4 || eitloop->running_status == 3) + pEvent = (cEventInfo *)pSchedule->GetPresentEvent(); + else + pEvent = (cEventInfo *)pSchedule->GetFollowingEvent(); + + if (pEvent != NULL) + if (pEvent->GetEventID() == eventid) + return true; } - - if (process != 0) + */ + + // + // let's see whether we have that eventid already + // in case not, we have to create a new cEventInfo for it + // + pEvent = (cEventInfo *)pSchedule->GetEvent(eventid); + if (pEvent == NULL) { - cerr << "GetEIT 0" << endl; - return 0; + pSchedule->Events.Add(new cEventInfo(service, eventid)); + pEvent = (cEventInfo *)pSchedule->GetEvent(eventid); + if (pEvent == NULL) + return false; + + strdvbcpy(tmp, &buf[sizeof(eit_short_event_t)], evt->event_name_length); + pEvent->SetTitle((char *)tmp); + strdvbcpy(tmp, &buf[sizeof(eit_short_event_t) + evt->event_name_length + 1], + (int)buf[sizeof(eit_short_event_t) + evt->event_name_length]); + pEvent->SetSubtitle((char *)tmp); + cMJD mjd(eitloop->date_hi, eitloop->date_lo, + eitloop->time_hour_ten * 10 + eitloop->time_hour, + eitloop->time_minute_ten * 10 + eitloop->time_minute, + eitloop->time_second_ten * 10 + eitloop->time_second); + pEvent->SetTime(mjd.GetTime_t()); + pEvent->SetDuration((long)((long)((eitloop->dur_hour_ten * 10 + eitloop->dur_hour) * 60l * 60l) + + (long)((eitloop->dur_minute_ten * 10 + eitloop->dur_minute) * 60l) + + (long)(eitloop->dur_second_ten * 10 + eitloop->dur_second))); } -*/ - int nReceivedEITs = 0; - tstart = time(NULL); - while ((!evtRunning.bIsValid || !evtNext.bIsValid) && nReceivedEITs < 20 && difftime(time(NULL), tstart) < 4) + + if (IsPresentFollowing()) { - if (!cFile::AnyFileReady(fsvbi, 5000)) - { - //cerr << "Timeout\n"; - CloseFilter(handle); - return -1; - } + if (eitloop->running_status == 4 || eitloop->running_status == 3) + pSchedule->SetPresentEvent(pEvent); + else if (eitloop->running_status == 1 || eitloop->running_status == 2 || eitloop->running_status == 0) + pSchedule->SetFollowingEvent(pEvent); + } + + return true; +} + +/** */ +bool cEIT::WriteExtEventDescriptor(unsigned short service, eit_loop_t *eitloop, u_char *buf) +{ + u_char tmp[256]; + eit_extended_event_t *evt = (eit_extended_event_t *)buf; + int bufact, buflen; + unsigned short eventid = (unsigned short)((eitloop->event_id_hi << 8) | eitloop->event_id_lo); + cEventInfo *pEvent; - read(fsvbi, buf, 8); - seclen=(buf[6]<<8)|buf[7]; - pid=(buf[4]<<8)|buf[5]; + //isyslog(LOG_INFO, "Found Extended Event Descriptor"); - if (seclen >= sizeof(buf)) - seclen = sizeof(buf) - 1; - read(fsvbi, buf, seclen); + cSchedule *pSchedule = (cSchedule *)schedules->GetSchedule(service); + if (pSchedule == NULL) + { + schedules->Add(new cSchedule(service)); + pSchedule = (cSchedule *)schedules->GetSchedule(service); + if (pSchedule == NULL) + return false; + } + + pEvent = (cEventInfo *)pSchedule->GetEvent(eventid); + if (pEvent == NULL) + return false; - if (seclen < (int)(sizeof(eit_t) - + sizeof(struct eit_loop_struct1) - + sizeof(struct eit_short_event_descriptor_struct))) - continue; + if (evt->descriptor_number != pEvent->GetExtendedDescriptorNumber()) + return false; - eit = (eit_t *)buf; - eitloop = (struct eit_loop_struct1 *)&eit[1]; - eitevt = (struct eit_short_event_descriptor_struct *)&eitloop[1]; + bufact = sizeof(eit_extended_event_t); + buflen = buf[1] + 2; - if (eitevt->descriptor_tag != EIT_SHORT_EVENT_DESCRIPTOR) + if (evt->length_of_items > 0) + { + while (bufact - sizeof(eit_extended_event_t) < evt->length_of_items) { - // printf("Tag = '%c'\n", eitevt->descriptor_tag); - continue; + strdvbcpy(tmp, &buf[bufact + 1], (int)buf[bufact]); + // could use value in tmp now to do something, + // haven't seen any items as of yet transmitted from satellite + bufact += (buf[bufact] + 1); } + } - if (((eit->service_id_hi << 8) | eit->service_id_lo) != uProgramNumber) - { - // printf("Wrong program %04x need %04x\n", (eit->service_id_hi << 8) | eit->service_id_lo, uProgramNumber); - continue; - } - - nReceivedEITs++; + strdvbcpy(tmp, &buf[bufact + 1], (int)buf[bufact]); + if (pEvent->AddExtendedDescription((char *)tmp)) + { + pEvent->IncreaseExtendedDescriptorNumber(); + return true; + } - pevt = (eit_event *)0; - if (eitloop->running_status == 4 | eitloop->running_status == 3) - pevt = (eit_event *)&evtRunning; - else if (eitloop->running_status == 1 || eitloop->running_status == 2 || eitloop->running_status == 0) - pevt = (eit_event *)&evtNext; + return false; +} + +// --- cSIProcessor ---------------------------------------------------------- + +#define MAX_FILTERS 20 + +/** */ +cSIProcessor::cSIProcessor(const char *FileName) +{ + useTStime = false; + filters = NULL; + schedules = NULL; + if ((fsvbi = open(FileName, O_RDONLY)) >= 0) + { + schedules = new cSchedules; + filters = (SIP_FILTER *)calloc(MAX_FILTERS, sizeof(SIP_FILTER)); + } + else + LOG_ERROR_STR(FileName); +} + +cSIProcessor::~cSIProcessor() +{ + if (fsvbi >= 0) + { + Stop(); + ShutDownFilters(); + delete filters; + delete schedules; + close(fsvbi); + } +} + +/** 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() +{ + if (fsvbi < 0) { + esyslog(LOG_ERR, "cSIProcessor::Action() called without open file - returning"); + return; + } + + dsyslog(LOG_INFO, "EIT processing thread started (pid=%d)", getpid()); + + unsigned char buf[4096+1]; // max. allowed size for any EIT section (+1 for safety ;-) + unsigned int seclen; + unsigned int pid; + time_t lastCleanup = time(NULL); + struct pollfd pfd; + + while(true) + { + time_t now = time(NULL); + struct tm *ptm = localtime(&now); + if (now - lastCleanup > 3600 && ptm->tm_hour == 5) + { + LOCK_THREAD; - if (pevt) + isyslog(LOG_INFO, "Now cleaning up things"); + schedules->Cleanup(); + lastCleanup = now; + } + + /* wait data become ready from the bitfilter */ + pfd.fd = fsvbi; + pfd.events = POLLIN; + if(poll(&pfd, 1, 1000) != 0) /* timeout is 5 secs */ { - unsigned char *p = (unsigned char *)&eitevt[1]; - strdvbcpy((unsigned char *)pevt->szTitle, p, eitevt->event_name_length); - pevt->szSubTitle[0] = 0; - strdvbcpy((unsigned char *)pevt->szSubTitle, &p[eitevt->event_name_length+1], (int)p[eitevt->event_name_length]); - strcpy(pevt->szDate, mjd2string((eitloop->date_hi << 8) + eitloop->date_lo)); - int hr = eitloop->time_hour + (eitloop->time_hour_ten * 10); - hr += 2; - if (hr >=24) + // fprintf(stderr, "<data>\n"); + /* read section */ + read(fsvbi, buf, 8); + seclen = (buf[6] << 8) | buf[7]; + pid = (buf[4] << 8) | buf[5]; + read(fsvbi, buf, seclen); + + //dsyslog(LOG_INFO, "Received pid 0x%02x with table ID 0x%02x and length of %04d\n", pid, buf[0], seclen); + + switch (pid) { - hr -= 24; - // need to switch date one day ahead here + case 0x14: + if (buf[0] == 0x70) + { + if (useTStime) + { + cTDT ctdt((tdt_t *)buf); + ctdt.SetSystemTime(); + } + } + /*XXX this comes pretty often: + else + dsyslog(LOG_INFO, "Time packet was not 0x70 but 0x%02x\n", (int)buf[0]); + XXX*/ + break; + + case 0x12: + if (buf[0] != 0x72) + { + LOCK_THREAD; + + cEIT ceit(buf, seclen, schedules); + ceit.ProcessEIT(); + } + else + dsyslog(LOG_INFO, "Received stuffing section in EIT\n"); + break; + + default: + break; } - sprintf(pevt->szTime, "%d:%c%c", hr, - eitloop->time_minute_ten + '0', - eitloop->time_minute + '0'); - pevt->bIsValid = true; } - } - - CloseFilter(handle); + else + { + LOCK_THREAD; - return 1; + //XXX this comes pretty often + //isyslog(LOG_INFO, "Received timeout from poll, refreshing filters\n"); + RefreshFilters(); + } +// WakeUp(); + } } -/** */ -int cEIT::SetProgramNumber(unsigned short pnr) +/** Add a filter with packet identifier pid and +table identifer tid */ +bool cSIProcessor::AddFilter(u_char pid, u_char tid) { - if (pnr == 0) - { - evtRunning.bIsValid = false; - evtNext.bIsValid = false; - return -1; - } + if (fsvbi < 0) + return false; + + int section = ((int)tid << 8) | 0x00ff; + + struct bitfilter filt = { + pid, + { section, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + SECTION_CONTINUOS, 0, + FILTER_MEM, + {}, + }; - if (pnr != uProgramNumber) + if (ioctl(fsvbi, VIDIOCSBITFILTER, &filt) < 0) + return false; + + for (int a = 0; a < MAX_FILTERS; a++) { - evtRunning.bIsValid = false; - evtNext.bIsValid = false; - uProgramNumber = pnr; + if (filters[a].inuse == false) + { + filters[a].pid = pid; + filters[a].tid = tid; + filters[a].handle = filt.handle; + filters[a].inuse = true; + // dsyslog(LOG_INFO, " Registered filter handle %04x, pid = %02d, tid = %02d", filters[a].handle, filters[a].pid, filters[a].tid); + return true; + } } - return 1; + + return false; } -/** retrieves the string for the running title */ -char * cEIT::GetRunningTitle() -{ - if (evtRunning.bIsValid) - return evtRunning.szTitle; - else - return "---"; -} -/** Retrieves the string for the running subtitle */ -char * cEIT::GetRunningSubtitle() -{ - if (evtRunning.bIsValid) - return evtRunning.szSubTitle; - else - return "---"; -} -/** Retrieves the string representing the -date of the current event - */ -char * cEIT::GetRunningDate() +/** set whether local systems time should be +set by the received TDT or TOT packets */ +bool cSIProcessor::SetUseTSTime(bool use) { - if (evtRunning.bIsValid) - return evtRunning.szDate; - else - return "---"; -} -/** Retrieves the string representing the -time of the current event */ -char * cEIT::GetRunningTime() -{ - if (evtRunning.bIsValid) - return evtRunning.szTime; - else - return "---"; -} -/** retrieves the string for the running title */ -char * cEIT::GetNextTitle() -{ - if (evtNext.bIsValid) - return evtNext.szTitle; - else - return "---"; + useTStime = use; + return useTStime; } -/** Retrieves the string for the running subtitle */ -char * cEIT::GetNextSubtitle() -{ - if (evtNext.bIsValid) - return evtNext.szSubTitle; - else - return "---"; -} -/** Retrieves the string representing the -date of the current event - */ -char * cEIT::GetNextDate() -{ - if (evtNext.bIsValid) - return evtNext.szDate; - else - return "---"; -} -/** Retrieves the string representing the -time of the current event */ -char * cEIT::GetNextTime() + +/** */ +bool cSIProcessor::ShutDownFilters() { - if (evtNext.bIsValid) - return evtNext.szTime; - else - return "---"; + if (fsvbi < 0) + return false; + + bool ret = true; + + for (int a = 0; a < MAX_FILTERS; a++) + { + if (filters[a].inuse == true) + { + if (ioctl(fsvbi, VIDIOCSSHUTDOWNFILTER, &filters[a].handle) < 0) + ret = false; + + // dsyslog(LOG_INFO, "Deregistered filter handle %04x, pid = %02d, tid = %02d", filters[a].handle, filters[a].pid, filters[a].tid); + + filters[a].inuse = false; + } + } + + return ret; } -/** */ -bool cEIT::IsValid() +/** */ +bool cSIProcessor::SetCurrentServiceID(unsigned short servid) { - GetEIT(); - return (evtRunning.bIsValid && evtNext.bIsValid); + LOCK_THREAD; + return schedules ? schedules->SetCurrentServiceID(servid) : false; } /** */ -int cEIT::strdvbcpy(unsigned char *dst, unsigned char *src, int max) +bool cSIProcessor::RefreshFilters() { - int a; - for (a = 0; a < max; a++) + if (fsvbi < 0) + return false; + + bool ret = true; + + ret = ShutDownFilters(); + + for (int a = 0; a < MAX_FILTERS; a++) { - if (*src == 0) - break; - - if ((*src >= ' ' && *src <= '~') || (*src >= 0xa0 && *src <= 0xff)) - *dst++ = *src++; - else - src++; + if (filters[a].inuse == false && filters[a].pid != 0 && filters[a].tid != 0) + { + if (!AddFilter(filters[a].pid, filters[a].tid)) + ret = false; + } } - *dst = 0; - return a; + + return ret; } + @@ -13,80 +13,127 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: eit.h 1.1 2000/09/03 10:23:24 kls Exp $ + * $Id: eit.h 1.2 2000/10/29 10:21:56 kls Exp $ ***************************************************************************/ #ifndef __EIT_H #define __EIT_H -#include <dvb_v4l.h> +#include "thread.h" +#include "tools.h" -typedef struct eit_event { +class cEventInfo : public cListObject { + friend class cSchedule; + friend class cEIT; +private: + unsigned short uServiceID; // Service ID of program for that event + bool bIsFollowing; // true if this is the next event on this channel + bool bIsPresent; // true if this is the present event running + char *pExtendedDescription; // Extended description of this event + char *pSubtitle; // Subtitle of event + char *pTitle; // Title of event + unsigned short uEventID; // Event ID of this event + long lDuration; // duration of event in seconds + time_t tTime; // Start time + u_char cExtendedDescriptorNumber; // current extended descriptor number that has to be inserted + int nChannelNumber; // the actual channel number from VDR's channel list (used in cMenuSchedule for sorting by channel number) +protected: + void SetFollowing(bool foll); + void SetPresent(bool pres); + bool SetTitle(char *string); + void SetServiceID(unsigned short servid); + void SetEventID(unsigned short evid); + void SetDuration(long l); + void SetTime(time_t t); + bool AddExtendedDescription(char *string); + bool SetSubtitle(char *string); + void IncreaseExtendedDescriptorNumber(void); + cEventInfo(unsigned short serviceid, unsigned short eventid); +public: + ~cEventInfo(); + const char *GetTimeString(void) const; + const char *GetEndTimeString(void) const; + const char *GetDate(void) const; + bool IsFollowing(void) const; + bool IsPresent(void) const; + const char *GetExtendedDescription(void) const; + const char *GetSubtitle(void) const; + const char *GetTitle(void) const; + unsigned short GetEventID(void) const; + long GetDuration(void) const; + time_t GetTime(void) const; + u_char GetExtendedDescriptorNumber(void) const; + unsigned short GetServiceID(void) const; + 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' + }; + +class cSchedule : public cListObject { + friend class cSchedules; + friend class cEIT; +private: + cEventInfo *pPresent; + cEventInfo *pFollowing; + unsigned short uServiceID; + cList<cEventInfo> Events; +protected: + void SetServiceID(unsigned short servid); + bool SetFollowingEvent(cEventInfo *pEvent); + bool SetPresentEvent(cEventInfo *pEvent); + void Cleanup(time_t tTime); + void Cleanup(void); + cSchedule(unsigned short servid = 0); +public: + ~cSchedule(); + const cEventInfo *GetPresentEvent(void) const; + const cEventInfo *GetFollowingEvent(void) const; + unsigned short GetServiceID(void) const; + const cEventInfo *GetEvent(unsigned short uEventID) const; + const cEventInfo *GetEvent(time_t tTime) const; + const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); } + int NumEvents(void) const { return Events.Count(); } + }; - bool bIsValid; - char szTitle[512]; - char szSubTitle[512]; - char szDate[12]; - char szTime[12]; +class cSchedules : public cList<cSchedule> { + friend class cSIProcessor; +private: + const cSchedule *pCurrentSchedule; + unsigned short uCurrentServiceID; +protected: + bool SetCurrentServiceID(unsigned short servid); + void Cleanup(); +public: + cSchedules(void); + ~cSchedules(); + const cSchedule *GetSchedule(unsigned short servid) const; + const cSchedule *GetSchedule(void) const; +}; -}eit_event; +typedef struct sip_filter { -/** - *@author Robert Schneider - */ + u_char pid; + u_char tid; + int handle; + bool inuse; -class cEIT { -public: - cEIT(); - ~cEIT(); - /** */ - int GetEIT(); - /** */ - int SetProgramNumber(unsigned short pnr); - /** Retrieves the string representing the time of the current event */ - char * GetRunningTime(); - /** Retrieves the string representing the date of the current event */ - char * GetRunningDate(); - /** Retrieves the string for the running subtitle */ - char * GetRunningSubtitle(); - /** retrieves the string for the running title */ - char * GetRunningTitle(); - /** Retrieves the string representing the time of the next event */ - char * GetNextTime(); - /** Retrieves the string representing the date of the next event */ - char * GetNextDate(); - /** Retrieves the string for the next subtitle */ - char * GetNextSubtitle(); - /** retrieves the string for the next title */ - char * GetNextTitle(); - /** */ - bool IsValid(); +}SIP_FILTER; -protected: // Protected attributes - /** Device name of VBI device */ - const char * cszBitFilter; -protected: // Protected attributes - /** handle to VBI device (usually /dev/vbi) */ +class cSIProcessor : public cThread { +private: + cSchedules *schedules; + bool useTStime; + SIP_FILTER *filters; int fsvbi; - /** Describes the event next on */ - eit_event evtNext; - /** Describes the running event */ - eit_event evtRunning; -protected: // Protected methods - /** Set the bitfilter in vbi device to return -correct tables */ - int SetBitFilter(unsigned short pid, unsigned short section, unsigned short mode); - /** */ - int GetSection(unsigned char *buf, ushort PID, unsigned char sec); - /** */ - int CloseFilter(unsigned short handle); - /** */ - char * mjd2string(unsigned short mjd); - /** */ - int strdvbcpy(unsigned char *dst, unsigned char *src, int max); -public: // Public attributes - /** */ - unsigned short uProgramNumber; -}; + bool RefreshFilters(void); + void Action(void); +public: + cSIProcessor(const char *FileName); + ~cSIProcessor(); + bool SetUseTSTime(bool use); + bool AddFilter(u_char pid, u_char tid); + bool ShutDownFilters(void); + bool SetCurrentServiceID(unsigned short servid); + const cSchedules *Schedules(void) { return schedules; } + }; #endif diff --git a/interface.c b/interface.c index 5940195..810270f 100644 --- a/interface.c +++ b/interface.c @@ -4,14 +4,12 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: interface.c 1.25 2000/10/08 16:34:17 kls Exp $ + * $Id: interface.c 1.28 2000/11/01 15:27:52 kls Exp $ */ #include "interface.h" +#include <ctype.h> #include <unistd.h> -#include "eit.h" - -cEIT EIT; cInterface *Interface = NULL; @@ -19,6 +17,7 @@ cInterface::cInterface(int SVDRPport) { open = 0; cols[0] = 0; + width = height = 0; keyFromWait = kNone; rcIo = NULL; SVDRP = NULL; @@ -43,15 +42,17 @@ cInterface::~cInterface() void cInterface::Open(int NumCols, int NumLines) { if (!open++) - cDvbApi::PrimaryDvbApi->Open(NumCols, NumLines); + cDvbApi::PrimaryDvbApi->Open(width = NumCols, height = NumLines); } void cInterface::Close(void) { if (open == 1) Clear(); - if (!--open) + if (!--open) { cDvbApi::PrimaryDvbApi->Close(); + width = height = 0; + } } unsigned int cInterface::GetCh(bool Wait, bool *Repeat, bool *Release) @@ -114,6 +115,18 @@ void cInterface::ClearEol(int x, int y, eDvbColor Color) cDvbApi::PrimaryDvbApi->ClrEol(x, y, Color); } +void cInterface::Fill(int x, int y, int w, int h, eDvbColor Color) +{ + if (open) + cDvbApi::PrimaryDvbApi->Fill(x, y, w, h, Color); +} + +void cInterface::Flush(void) +{ + if (open) + cDvbApi::PrimaryDvbApi->Flush(); +} + void cInterface::SetCols(int *c) { for (int i = 0; i < MaxCols; i++) { @@ -123,6 +136,74 @@ void cInterface::SetCols(int *c) } } +char *cInterface::WrapText(const char *Text, int Width, int *Height) +{ + // Wraps the Text to make it fit into the area defined by the given Width + // (which is given in character cells). + // The actual number of lines resulting from this operation is returned in + // Height. + // The returned string is newly created on the heap and the caller + // is responsible for deleting it once it is no longer used. + // Wrapping is done by inserting the necessary number of newline + // characters into the string. + + int Lines = 1; + char *t = strdup(Text); + char *Blank = NULL; + char *Delim = NULL; + int w = 0; + + Width *= cDvbApi::PrimaryDvbApi->CellWidth(); + + while (*t && t[strlen(t) - 1] == '\n') + t[strlen(t) - 1] = 0; // skips trailing newlines + + for (char *p = t; *p; ) { + if (*p == '\n') { + Lines++; + w = 0; + Blank = Delim = NULL; + p++; + continue; + } + else if (isspace(*p)) + Blank = p; + int cw = cDvbApi::PrimaryDvbApi->Width(*p); + if (w + cw > Width) { + if (Blank) { + *Blank = '\n'; + p = Blank; + continue; + } + else { + // Here's the ugly part, where we don't have any whitespace to + // punch in a newline, so we need to make room for it: + if (Delim) + p = Delim + 1; // let's fall back to the most recent delimiter + char *s = new char[strlen(t) + 2]; // The additional '\n' plus the terminating '\0' + int l = p - t; + strncpy(s, t, l); + s[l] = '\n'; + strcpy(s + l + 1, p); + delete t; + t = s; + p = t + l; + continue; + } + } + else + w += cw; + if (strchr("-.,:;!?_", *p)) { + Delim = p; + Blank = NULL; + } + p++; + } + + *Height = Lines; + return t; +} + void cInterface::Write(int x, int y, const char *s, eDvbColor FgColor, eDvbColor BgColor) { if (open) @@ -157,7 +238,7 @@ void cInterface::WriteText(int x, int y, const char *s, eDvbColor FgColor, eDvbC void cInterface::Title(const char *s) { - int x = (MenuColumns - strlen(s)) / 2; + int x = (Width() - strlen(s)) / 2; if (x < 0) x = 0; ClearEol(0, 0, clrCyan); @@ -206,7 +287,7 @@ bool cInterface::Confirm(const char *s) void cInterface::HelpButton(int Index, const char *Text, eDvbColor FgColor, eDvbColor BgColor) { if (open && Text) { - const int w = MenuColumns / 4; + const int w = Width() / 4; int l = (w - strlen(Text)) / 2; if (l < 0) l = 0; @@ -334,65 +415,9 @@ void cInterface::LearnKeys(void) } } -eKeys cInterface::DisplayChannel(int Number, const char *Name, bool WithInfo) +void cInterface::DisplayChannelNumber(int Number) { - // Number = 0 is used for channel group display and no EIT - if (Number) - rcIo->Number(Number); - if (Name && !Recording()) { - Open(MenuColumns, 5); - cDvbApi::PrimaryDvbApi->Fill(0, 0, MenuColumns, 1, clrBackground); - int BufSize = MenuColumns + 1; - char buffer[BufSize]; - if (Number) - snprintf(buffer, BufSize, "%d %s", Number, Name ? Name : ""); - else - snprintf(buffer, BufSize, "%s", Name ? Name : ""); - Write(0, 0, buffer); - time_t t = time(NULL); - struct tm *now = localtime(&t); - snprintf(buffer, BufSize, "%02d:%02d", now->tm_hour, now->tm_min); - Write(-5, 0, buffer); - cDvbApi::PrimaryDvbApi->Flush(); - - char *RunningTitle = "", *RunningSubtitle = "", *NextTitle = "", *NextSubtitle = ""; - int Lines = 0; - if (Number && WithInfo && EIT.IsValid()) { - if (*(RunningTitle = EIT.GetRunningTitle())) Lines++; - if (*(RunningSubtitle = EIT.GetRunningSubtitle())) Lines++; - if (*(NextTitle = EIT.GetNextTitle())) Lines++; - if (*(NextSubtitle = EIT.GetNextSubtitle())) Lines++; - } - if (Lines > 0) { - const int t = 6; - int l = 1; - cDvbApi::PrimaryDvbApi->Fill(0, 1, MenuColumns, Lines, clrBackground); - if (*RunningTitle) { - Write(0, l, EIT.GetRunningTime(), clrYellow, clrBackground); - Write(t, l, RunningTitle, clrCyan, clrBackground); - l++; - } - if (*RunningSubtitle) { - Write(t, l, RunningSubtitle, clrCyan, clrBackground); - l++; - } - if (*NextTitle) { - Write(0, l, EIT.GetNextTime(), clrYellow, clrBackground); - Write(t, l, NextTitle, clrCyan, clrBackground); - l++; - } - if (*NextSubtitle) { - Write(t, l, NextSubtitle, clrCyan, clrBackground); - } - cDvbApi::PrimaryDvbApi->Flush(); - } - eKeys Key = Wait(5, true); - if (Key == kOk) - GetKey(); - Close(); - return Key; - } - return kNone; + rcIo->Number(Number); } void cInterface::DisplayRecording(int Index, bool On) diff --git a/interface.h b/interface.h index 8d0f8f6..5dd8ee3 100644 --- a/interface.h +++ b/interface.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: interface.h 1.16 2000/10/08 12:15:49 kls Exp $ + * $Id: interface.h 1.19 2000/11/01 15:27:23 kls Exp $ */ #ifndef __INTERFACE_H @@ -19,6 +19,7 @@ class cInterface { public: enum { MaxCols = 5 }; private: + int width, height; int open; int cols[MaxCols]; eKeys keyFromWait; @@ -33,11 +34,16 @@ public: ~cInterface(); void Open(int NumCols = MenuColumns, int NumLines = MenuLines); void Close(void); + int Width(void) { return width; } + int Height(void) { return height; } eKeys GetKey(bool Wait = true); void PutKey(eKeys Key); void Clear(void); void ClearEol(int x, int y, eDvbColor Color = clrBackground); + void Fill(int x, int y, int w, int h, eDvbColor color = clrBackground); + void Flush(void); void SetCols(int *c); + char *WrapText(const char *Text, int Width, int *Height); void Write(int x, int y, const char *s, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground); void WriteText(int x, int y, const char *s, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground); void Title(const char *s); @@ -47,7 +53,7 @@ public: bool Confirm(const char *s); void Help(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); void LearnKeys(void); - eKeys DisplayChannel(int Number, const char *Name = NULL, bool WithInfo = false); + void DisplayChannelNumber(int Number); void DisplayRecording(int Index, bool On); bool Recording(void); }; @@ -4,15 +4,16 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 1.36 2000/10/08 16:11:22 kls Exp $ + * $Id: menu.c 1.40 2000/11/01 16:14:48 kls Exp $ */ #include "menu.h" -#include <ctype.h> #include <limits.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "config.h" +#include "eit.h" #define MENUTIMEOUT 120 // seconds @@ -725,7 +726,107 @@ eOSState cMenuChannels::ProcessKey(eKeys Key) return state; } -// --- cMenuSummary -------------------------------------------------------- +// --- cMenuTextItem --------------------------------------------------------- + +class cMenuTextItem : public cOsdItem { +private: + char *text; + int x, y, w, h, lines, offset; + eDvbColor fgColor, bgColor; +public: + cMenuTextItem(const char *Text, int X, int Y, int W, int H = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground); + ~cMenuTextItem(); + int Height(void) { return h; } + void Clear(void); + virtual void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground); + bool CanScrollUp(void) { return offset > 0; } + bool CanScrollDown(void) { return h + offset < lines; } + void ScrollUp(void); + void ScrollDown(void); + virtual eOSState ProcessKey(eKeys Key); + }; + +cMenuTextItem::cMenuTextItem(const char *Text, int X, int Y, int W, int H, eDvbColor FgColor, eDvbColor BgColor) +{ + x = X; + y = Y; + w = W; + h = H; + fgColor = FgColor; + bgColor = BgColor; + offset = 0; + text = Interface->WrapText(Text, w - 1, &lines); + if (h < 0) + h = lines; +} + +cMenuTextItem::~cMenuTextItem() +{ + delete text; +} + +void cMenuTextItem::Clear(void) +{ + Interface->Fill(x, y, w, h, bgColor); +} + +void cMenuTextItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor) +{ + int l = 0; + char *t = text; + while (*t) { + char *n = strchr(t, '\n'); + if (l >= offset) { + if (n) + *n = 0; + Interface->Write(x, y + l - offset, t, fgColor, bgColor); + if (n) + *n = '\n'; + else + break; + } + if (!n) + break; + t = n + 1; + if (++l >= h + offset) + break; + } + // scroll indicators use inverted color scheme! + if (CanScrollUp()) Interface->Write(x + w - 1, y, "^", bgColor, fgColor); + if (CanScrollDown()) Interface->Write(x + w - 1, y + h - 1, "v", bgColor, fgColor); +} + +void cMenuTextItem::ScrollUp(void) +{ + if (CanScrollUp()) { + Clear(); + offset--; + Display(); + } +} + +void cMenuTextItem::ScrollDown(void) +{ + if (CanScrollDown()) { + Clear(); + offset++; + Display(); + } +} + +eOSState cMenuTextItem::ProcessKey(eKeys Key) +{ + switch (Key) { + case kUp|k_Repeat: + case kUp: ScrollUp(); break; + case kDown|k_Repeat: + case kDown: ScrollDown(); break; + default: return osUnknown; + } + return osContinue; +} + +// --- cMenuSummary ---------------------------------------------------------- class cMenuSummary : public cOsdMenu { public: @@ -736,29 +837,7 @@ public: cMenuSummary::cMenuSummary(const char *Text) :cOsdMenu("Summary") { - while (*Text) { - char line[MenuColumns + 1]; - char *p = line; - const char *b = NULL; - *p++ = ' '; - while (*Text && p - line < MenuColumns - 2) { - if (isspace(*Text)) - b = Text; // remember the blank - if (*Text == '\n') - break; - *p++ = *Text++; - } - if (*Text) { - if (b && Text - b > 0) { - p -= Text - b; - Text = b + 1; - } - else - Text++; - } - *p = 0; - Add(new cOsdItem(line, osBack)); - } + Add(new cMenuTextItem(Text, 1, 2, MenuColumns - 2, MAXOSDITEMS)); } eOSState cMenuSummary::ProcessKey(eKeys Key) @@ -973,6 +1052,278 @@ eOSState cMenuTimers::ProcessKey(eKeys Key) return state; } +// --- cMenuEvent ------------------------------------------------------------ + +class cMenuEvent : public cOsdMenu { +private: + const cEventInfo *eventInfo; +public: + cMenuEvent(const cEventInfo *EventInfo, bool CanSwitch = false); + cMenuEvent(bool Now); + virtual eOSState ProcessKey(eKeys Key); +}; + +cMenuEvent::cMenuEvent(const cEventInfo *EventInfo, bool CanSwitch) +:cOsdMenu("Event") +{ + eventInfo = EventInfo; + if (eventInfo) { + cChannel *channel = Channels.GetByServiceID(eventInfo->GetServiceID()); + if (channel) { + const char *p; + char *buffer; + asprintf(&buffer, "%-17.*s %.*s %s - %s", 17, channel->name, 5, eventInfo->GetDate(), eventInfo->GetTimeString(), eventInfo->GetEndTimeString()); + SetTitle(buffer, false); + int Line = 2; + cMenuTextItem *item; + if (!isempty(p = eventInfo->GetTitle())) { + Add(item = new cMenuTextItem(p, 1, Line, MenuColumns - 2, -1, clrCyan)); + Line += item->Height() + 1; + } + if (!isempty(p = eventInfo->GetSubtitle())) { + Add(item = new cMenuTextItem(p, 1, Line, MenuColumns - 2, -1, clrYellow)); + Line += item->Height() + 1; + } + if (!isempty(p = eventInfo->GetExtendedDescription())) + Add(new cMenuTextItem(p, 1, Line, MenuColumns - 2, Height() - Line - 2, clrCyan), true); + SetHelp("Record", NULL, NULL, CanSwitch ? "Switch" : NULL); + } + } +} + +eOSState cMenuEvent::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + + if (state == osUnknown) { + switch (Key) { + case kOk: return osBack; + default: break; + } + } + return state; +} + +// --- cMenuWhatsOnItem ------------------------------------------------------ + +class cMenuWhatsOnItem : public cOsdItem { +public: + const cEventInfo *eventInfo; + cMenuWhatsOnItem(const cEventInfo *EventInfo); +}; + +cMenuWhatsOnItem::cMenuWhatsOnItem(const cEventInfo *EventInfo) +{ + eventInfo = EventInfo; + char *buffer = NULL; + cChannel *channel = Channels.GetByNumber(eventInfo->GetChannelNumber()); + asprintf(&buffer, "%d\t%.*s\t%.*s\t%s", eventInfo->GetChannelNumber(), 6, channel ? channel->name : "???", 5, eventInfo->GetTimeString(), eventInfo->GetTitle()); + SetText(buffer, false); +} + +// --- cMenuWhatsOn ---------------------------------------------------------- + +class cMenuWhatsOn : public cOsdMenu { +private: + eOSState Record(void); + eOSState Switch(void); +public: + cMenuWhatsOn(const cSchedules *Schedules, bool Now); + virtual eOSState ProcessKey(eKeys Key); + }; + +static int CompareEventChannel(const void *p1, const void *p2) +{ + return (int)( (*(const cEventInfo **)p1)->GetChannelNumber() - (*(const cEventInfo **)p2)->GetChannelNumber()); +} + +cMenuWhatsOn::cMenuWhatsOn(const cSchedules *Schedules, bool Now) +:cOsdMenu(Now ? "What's on now?" : "What's on next?", 4, 7, 6) +{ + const cSchedule *Schedule = Schedules->First(); + const cEventInfo **pArray = NULL; + int num = 0; + + while (Schedule) { + pArray = (const cEventInfo **)realloc(pArray, (num + 1) * sizeof(cEventInfo *)); + + pArray[num] = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent(); + if (pArray[num]) { + cChannel *channel = Channels.GetByServiceID(pArray[num]->GetServiceID()); + if (channel) { + pArray[num]->SetChannelNumber(channel->number); + num++; + } + } + Schedule = (const cSchedule *)Schedules->Next(Schedule); + } + + qsort(pArray, num, sizeof(cEventInfo *), CompareEventChannel); + + for (int a = 0; a < num; a++) + Add(new cMenuWhatsOnItem(pArray[a])); + + delete pArray; + SetHelp("Record", Now ? "Next" : "Now", "Schedule", "Switch"); +} + +eOSState cMenuWhatsOn::Switch(void) +{ + cMenuWhatsOnItem *item = (cMenuWhatsOnItem *)Get(Current()); + if (item) { + cChannel *channel = Channels.GetByServiceID(item->eventInfo->GetServiceID()); + if (channel && channel->Switch()) + return osEnd; + } + Interface->Error("Can't switch channel!"); + return osContinue; +} + +eOSState cMenuWhatsOn::Record(void) +{ + cMenuWhatsOnItem *item = (cMenuWhatsOnItem *)Get(Current()); + if (item) { + cTimer *timer = new cTimer(item->eventInfo); + Timers.Add(timer); + Timers.Save(); + isyslog(LOG_INFO, "timer %d added", timer->Index() + 1); + return AddSubMenu(new cMenuEditTimer(timer->Index(), true)); + } + return osContinue; +} + +eOSState cMenuWhatsOn::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + + if (state == osUnknown) { + switch (Key) { + case kRed: return Record(); + case kYellow: return osBack; + case kBlue: return Switch(); + case kOk: if (Count()) + return AddSubMenu(new cMenuEvent(((cMenuWhatsOnItem *)Get(Current()))->eventInfo, true)); + break; + default: break; + } + } + return state; +} + +// --- cMenuScheduleItem ----------------------------------------------------- + +class cMenuScheduleItem : public cOsdItem { +public: + const cEventInfo *eventInfo; + cMenuScheduleItem(const cEventInfo *EventInfo); +}; + +cMenuScheduleItem::cMenuScheduleItem(const cEventInfo *EventInfo) +{ + eventInfo = EventInfo; + char *buffer = NULL; + asprintf(&buffer, "%.*s\t%.*s\t%s", 5, eventInfo->GetDate(), 5, eventInfo->GetTimeString(), eventInfo->GetTitle()); + SetText(buffer, false); +} + +// --- cMenuSchedule --------------------------------------------------------- + +class cMenuSchedule : public cOsdMenu { +private: + cThreadLock threadLock; + const cSchedules *schedules; + bool now, next; + eOSState Record(void); + void PrepareSchedule(void); + void PrepareWhatsOnNext(bool On); +public: + cMenuSchedule(void); + virtual eOSState ProcessKey(eKeys Key); + }; + +cMenuSchedule::cMenuSchedule(void) +:cOsdMenu("Schedule", 6, 6) +{ + now = next = false; + cChannel *channel = Channels.GetByNumber(CurrentChannel); + if (channel) { + char *buffer = NULL; + asprintf(&buffer, "Schedule - %s", channel->name); + SetTitle(buffer, false); + } + PrepareSchedule(); + SetHelp("Record", "Now", "Next"); +} + +static int CompareEventTime(const void *p1, const void *p2) +{ + return (int)((*(cEventInfo **)p1)->GetTime() - (*(cEventInfo **)p2)->GetTime()); +} + +void cMenuSchedule::PrepareSchedule(void) +{ + schedules = cDvbApi::PrimaryDvbApi->Schedules(&threadLock); + if (schedules) { + const cSchedule *Schedule = schedules->GetSchedule(); + int num = Schedule->NumEvents(); + const cEventInfo **pArray = (const cEventInfo **)malloc(num * sizeof(cEventInfo *)); + if (pArray) { + time_t now = time(NULL); + int numreal = 0; + for (int a = 0; a < num; a++) { + const cEventInfo *EventInfo = Schedule->GetEventNumber(a); + if (EventInfo->GetTime() + EventInfo->GetDuration() > now) + pArray[numreal++] = EventInfo; + } + + qsort(pArray, numreal, sizeof(cEventInfo *), CompareEventTime); + + for (int a = 0; a < numreal; a++) + Add(new cMenuScheduleItem(pArray[a])); + delete pArray; + } + } +} + +eOSState cMenuSchedule::Record(void) +{ + cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); + if (item) { + cTimer *timer = new cTimer(item->eventInfo); + Timers.Add(timer); + Timers.Save(); + isyslog(LOG_INFO, "timer %d added", timer->Index() + 1); + return AddSubMenu(new cMenuEditTimer(timer->Index(), true)); + } + return osContinue; +} + +eOSState cMenuSchedule::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + + if (state == osUnknown) { + switch (Key) { + case kRed: return Record(); + case kGreen: if (!now && !next) { + now = true; + return AddSubMenu(new cMenuWhatsOn(schedules, true)); + } + now = !now; + next = !next; + return AddSubMenu(new cMenuWhatsOn(schedules, now)); + case kYellow: return AddSubMenu(new cMenuWhatsOn(schedules, false)); + case kOk: if (Count()) + return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->eventInfo)); + break; + default: break; + } + } + else if (!HasSubMenu()) + now = next = false; + return state; +} + // --- cMenuRecordingItem ---------------------------------------------------- class cMenuRecordingItem : public cOsdItem { @@ -1089,6 +1440,9 @@ cMenuSetup::cMenuSetup(void) Add(new cMenuEditBoolItem("MarkInstantRecord", &data.MarkInstantRecord)); Add(new cMenuEditIntItem( "LnbFrequLo", &data.LnbFrequLo)); Add(new cMenuEditIntItem( "LnbFrequHi", &data.LnbFrequHi)); + Add(new cMenuEditIntItem( "SetSystemTime", &data.SetSystemTime)); + Add(new cMenuEditIntItem( "MarginStart", &data.MarginStart)); + Add(new cMenuEditIntItem( "MarginStop", &data.MarginStop)); } eOSState cMenuSetup::ProcessKey(eKeys Key) @@ -1098,6 +1452,7 @@ eOSState cMenuSetup::ProcessKey(eKeys Key) if (state == osUnknown) { switch (Key) { case kOk: state = (Setup.PrimaryDVB != data.PrimaryDVB) ? osSwitchDvb : osBack; + cDvbApi::PrimaryDvbApi->SetUseTSTime(data.SetSystemTime); Setup = data; Setup.Save(); break; @@ -1114,6 +1469,7 @@ eOSState cMenuSetup::ProcessKey(eKeys Key) cMenuMain::cMenuMain(bool Replaying) :cOsdMenu("Main") { + Add(new cOsdItem("Schedule", osSchedule)); Add(new cOsdItem("Channels", osChannels)); Add(new cOsdItem("Timer", osTimer)); Add(new cOsdItem("Recordings", osRecordings)); @@ -1137,6 +1493,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key) eOSState state = cOsdMenu::ProcessKey(Key); switch (state) { + case osSchedule: return AddSubMenu(new cMenuSchedule); case osChannels: return AddSubMenu(new cMenuChannels); case osTimer: return AddSubMenu(new cMenuTimers); case osRecordings: return AddSubMenu(new cMenuRecordings); @@ -1166,58 +1523,166 @@ eOSState cMenuMain::ProcessKey(eKeys Key) return state; } -// --- cDirectChannelSelect -------------------------------------------------- +// --- cDisplayChannel ------------------------------------------------------- -#define DIRECTCHANNELTIMEOUT 500 //ms +#define DIRECTCHANNELTIMEOUT 500 //ms +#define INFOTIMEOUT 5000 //ms -cDirectChannelSelect::cDirectChannelSelect(eKeys FirstKey) +cDisplayChannel::cDisplayChannel(int Number, bool Switched, bool Group) +:cOsdBase(true) +{ + group = Group; + withInfo = !group && (!Switched || Setup.ShowInfoOnChSwitch); + lines = 0; + oldNumber = number = 0; + cChannel *channel = Group ? Channels.Get(Number) : Channels.GetByNumber(Number); + Interface->Open(MenuColumns, 5); + if (channel) { + DisplayChannel(channel); + DisplayInfo(); + } + lastTime = time_ms(); +} + +cDisplayChannel::cDisplayChannel(eKeys FirstKey) :cOsdBase(true) { oldNumber = CurrentChannel; number = 0; lastTime = time_ms(); - Interface->Open(MenuColumns, 1); + Interface->Open(MenuColumns, 5); ProcessKey(FirstKey); } -cDirectChannelSelect::~cDirectChannelSelect() +cDisplayChannel::~cDisplayChannel() { if (number < 0) - Interface->DisplayChannel(oldNumber); + Interface->DisplayChannelNumber(oldNumber); Interface->Close(); } -eOSState cDirectChannelSelect::ProcessKey(eKeys Key) +void cDisplayChannel::DisplayChannel(const cChannel *Channel) +{ + if (!Interface->Recording()) { + if (Channel && Channel->number) + Interface->DisplayChannelNumber(Channel->number); + int BufSize = Width() + 1; + char buffer[BufSize]; + if (Channel && Channel->number) + snprintf(buffer, BufSize, "%d %s", Channel->number, Channel->name); + else + snprintf(buffer, BufSize, "%s", Channel ? Channel->name : "*** Invalid Channel ***"); + Interface->Fill(0, 0, MenuColumns, 1, clrBackground); + Interface->Write(0, 0, buffer); + time_t t = time(NULL); + struct tm *now = localtime(&t); + snprintf(buffer, BufSize, "%02d:%02d", now->tm_hour, now->tm_min); + Interface->Write(-5, 0, buffer); + Interface->Flush(); + } +} + +void cDisplayChannel::DisplayInfo(void) +{ + if (withInfo) { + const cEventInfo *Present = NULL, *Following = NULL; + cThreadLock ThreadLock; + const cSchedules *Schedules = cDvbApi::PrimaryDvbApi->Schedules(&ThreadLock); + if (Schedules) { + const cSchedule *Schedule = Schedules->GetSchedule(); + if (Schedule) { + const char *PresentTitle = NULL, *PresentSubtitle = NULL, *FollowingTitle = NULL, *FollowingSubtitle = NULL; + int Lines = 0; + if ((Present = Schedule->GetPresentEvent()) != NULL) { + PresentTitle = Present->GetTitle(); + if (!isempty(PresentTitle)) + Lines++; + PresentSubtitle = Present->GetSubtitle(); + if (!isempty(PresentSubtitle)) + Lines++; + } + if ((Following = Schedule->GetFollowingEvent()) != NULL) { + FollowingTitle = Following->GetTitle(); + if (!isempty(FollowingTitle)) + Lines++; + FollowingSubtitle = Following->GetSubtitle(); + if (!isempty(FollowingSubtitle)) + Lines++; + } + if (Lines > lines) { + const int t = 6; + int l = 1; + Interface->Fill(0, 1, MenuColumns, Lines, clrBackground); + if (!isempty(PresentTitle)) { + Interface->Write(0, l, Present->GetTimeString(), clrYellow, clrBackground); + Interface->Write(t, l, PresentTitle, clrCyan, clrBackground); + l++; + } + if (!isempty(PresentSubtitle)) { + Interface->Write(t, l, PresentSubtitle, clrCyan, clrBackground); + l++; + } + if (!isempty(FollowingTitle)) { + Interface->Write(0, l, Following->GetTimeString(), clrYellow, clrBackground); + Interface->Write(t, l, FollowingTitle, clrCyan, clrBackground); + l++; + } + if (!isempty(FollowingSubtitle)) { + Interface->Write(t, l, FollowingSubtitle, clrCyan, clrBackground); + } + Interface->Flush(); + lines = Lines; + lastTime = time_ms(); + } + } + } + } +} + +eOSState cDisplayChannel::ProcessKey(eKeys Key) { switch (Key) { - case k0 ... k9: + case k0: + if (number == 0) { + // keep the "Toggle channels" function working + Interface->PutKey(Key); + return osEnd; + } + case k1 ... k9: if (number >= 0) { number = number * 10 + Key - k0; - cChannel *channel = Channels.GetByNumber(number); - const char *Name = channel ? channel->name : "*** Invalid Channel ***"; - int BufSize = MenuColumns + 1; - char buffer[BufSize]; - snprintf(buffer, BufSize, "%d %s", number, Name); - Interface->DisplayChannel(number); - Interface->Clear(); - Interface->Write(0, 0, buffer); - lastTime = time_ms(); - if (!channel) { - number = -1; - lastTime += 1000; + if (number > 0) { + cChannel *channel = Channels.GetByNumber(number); + DisplayChannel(channel); + lastTime = time_ms(); + if (!channel) { + number = -1; + lastTime += 1000; + } } } break; case kNone: - if (time_ms() - lastTime > DIRECTCHANNELTIMEOUT) { + if (number && time_ms() - lastTime > DIRECTCHANNELTIMEOUT) { if (number > 0 && !Channels.SwitchTo(number)) number = -1; + return osEnd; } - else - break; - default: return osEnd; + break; + //TODO + //XXX case kGreen: return osEventNow; + //XXX case kYellow: return osEventNext; + case kOk: if (group) + Channels.SwitchTo(Channels.Get(Channels.GetNextNormal(CurrentGroup))->number); + return osEnd; + default: Interface->PutKey(Key); + return osEnd; }; - return osContinue; + if (time_ms() - lastTime < INFOTIMEOUT) { + DisplayInfo(); + return osContinue; + } + return osEnd; } // --- cRecordControl -------------------------------------------------------- @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.h 1.12 2000/10/08 15:21:52 kls Exp $ + * $Id: menu.h 1.13 2000/11/01 14:03:09 kls Exp $ */ #ifndef _MENU_H @@ -24,14 +24,18 @@ public: virtual eOSState ProcessKey(eKeys Key); }; -class cDirectChannelSelect : public cOsdBase { +class cDisplayChannel : public cOsdBase { private: - int oldNumber; - int number; + bool withInfo, group; + int lines; int lastTime; + int oldNumber, number; + void DisplayChannel(const cChannel *Channel); + void DisplayInfo(void); public: - cDirectChannelSelect(eKeys FirstKey); - virtual ~cDirectChannelSelect(); + cDisplayChannel(int Number, bool Switched, bool Group = false); + cDisplayChannel(eKeys FirstKey); + virtual ~cDisplayChannel(); virtual eOSState ProcessKey(eKeys Key); }; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.c 1.9 2000/10/08 12:20:34 kls Exp $ + * $Id: osd.c 1.11 2000/11/01 11:21:51 kls Exp $ */ #include "osd.h" @@ -24,7 +24,7 @@ cOsdItem::cOsdItem(eOSState State) bgColor = clrBackground; } -cOsdItem::cOsdItem(char *Text, eOSState State) +cOsdItem::cOsdItem(const char *Text, eOSState State) { text = NULL; offset = -1; @@ -74,7 +74,7 @@ eOSState cOsdItem::ProcessKey(eKeys Key) // --- cOsdMenu -------------------------------------------------------------- -cOsdMenu::cOsdMenu(char *Title, int c0, int c1, int c2, int c3, int c4) +cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4) { visible = false; title = strdup(Title); @@ -108,6 +108,12 @@ void cOsdMenu::SetStatus(const char *s) Interface->Status(status); } +void cOsdMenu::SetTitle(const char *Title, bool Copy) +{ + delete title; + title = Copy ? strdup(Title) : Title; +} + void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue) { // strings are NOT copied - must be constants!!! @@ -164,7 +170,8 @@ void cOsdMenu::Display(void) break; } } - Interface->Status(status); + if (!isempty(status)) + Interface->Status(status); } void cOsdMenu::RefreshCurrent(void) @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.h 1.11 2000/09/10 09:50:38 kls Exp $ + * $Id: osd.h 1.14 2000/11/01 14:29:07 kls Exp $ */ #ifndef __OSD_H @@ -19,6 +19,7 @@ enum eOSState { osUnknown, osMenu, osContinue, + osSchedule, osChannels, osTimer, osRecordings, @@ -43,13 +44,13 @@ protected: eDvbColor fgColor, bgColor; public: cOsdItem(eOSState State = osUnknown); - cOsdItem(char *Text, eOSState State = osUnknown); + cOsdItem(const char *Text, eOSState State = osUnknown); virtual ~cOsdItem(); bool HasUserColor(void) { return userColor; } void SetText(const char *Text, bool Copy = true); void SetColor(eDvbColor FgColor, eDvbColor BgColor = clrBackground); const char *Text(void) { return text; } - void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground); + virtual void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground); virtual void Set(void) {} virtual eOSState ProcessKey(eKeys Key); }; @@ -60,13 +61,15 @@ protected: public: cOsdBase(bool FastResponse = false) { needsFastResponse = FastResponse; } virtual ~cOsdBase() {} - virtual eOSState ProcessKey(eKeys Key) = 0; + int Width(void) { return Interface->Width(); } + int Height(void) { return Interface->Height(); } bool NeedsFastResponse(void) { return needsFastResponse; } + virtual eOSState ProcessKey(eKeys Key) = 0; }; class cOsdMenu : public cOsdBase, public cList<cOsdItem> { private: - char *title; + const char *title; int cols[cInterface::MaxCols]; int first, current, marked; cOsdMenu *subMenu; @@ -83,10 +86,11 @@ protected: eOSState AddSubMenu(cOsdMenu *SubMenu); bool HasSubMenu(void) { return subMenu; } void SetStatus(const char *s); + void SetTitle(const char *Title, bool Copy = true); void SetHelp(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); virtual void Del(int Index); public: - cOsdMenu(char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0); + cOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0); virtual ~cOsdMenu(); int Current(void) { return current; } void Add(cOsdItem *Item, bool Current = false); diff --git a/recording.c b/recording.c index f9ab016..3ddf8d3 100644 --- a/recording.c +++ b/recording.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recording.c 1.19 2000/10/08 12:20:53 kls Exp $ + * $Id: recording.c 1.20 2000/11/01 16:00:36 kls Exp $ */ #define _GNU_SOURCE @@ -86,6 +86,13 @@ cRecording::cRecording(cTimer *Timer) titleBuffer = NULL; fileName = NULL; name = strdup(Timer->file); + // substitute characters that would cause problems in file names: + for (char *p = name; *p; p++) { + switch (*p) { + case '\n': *p = ' '; break; + case '/': *p = '-'; break; + } + } summary = Timer->summary ? strdup(Timer->summary) : NULL; if (summary) strreplace(summary, '|', '\n'); diff --git a/setup.conf b/setup.conf deleted file mode 100644 index e7ec267..0000000 --- a/setup.conf +++ /dev/null @@ -1,7 +0,0 @@ -# VDR Setup -PrimaryDVB = 1 -ShowInfoOnChSwitch = 1 -MenuScrollPage = 1 -MarkInstantRecord = 1 -LnbFrequLo = 9750 -LnbFrequHi = 10600 @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: thread.c 1.2 2000/10/08 16:45:50 kls Exp $ + * $Id: thread.c 1.3 2000/10/28 15:26:02 kls Exp $ */ #include "thread.h" @@ -88,16 +88,27 @@ void cThread::WakeUp(void) cThreadLock::cThreadLock(cThread *Thread) { - thread = Thread; - locked = Thread->Lock(); + thread = NULL; + locked = false; + Lock(Thread); } cThreadLock::~cThreadLock() { - if (locked) + if (thread && locked) thread->Unlock(); } +bool cThreadLock::Lock(cThread *Thread) +{ + if (Thread && !thread) { + thread = Thread; + locked = Thread->Lock(); + return locked; + } + return false; +} + bool cThreadLock::Locked(void) { return locked; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: thread.h 1.1 2000/10/08 08:36:21 kls Exp $ + * $Id: thread.h 1.2 2000/10/28 15:08:09 kls Exp $ */ #ifndef __THREAD_H @@ -47,8 +47,9 @@ private: cThread *thread; bool locked; public: - cThreadLock(cThread *Thread); + cThreadLock(cThread *Thread = NULL); ~cThreadLock(); + bool Lock(cThread *Thread); bool Locked(void); }; diff --git a/timers.conf b/timers.conf index fc0e12a..8f6bcf2 100644 --- a/timers.conf +++ b/timers.conf @@ -1,16 +1,15 @@ 1:15:M------:2128:2205:99:7:Neues: 1:3:-T-----:2013:2125:99:99:SevenDays: 1:10:-T-----:2058:2202:99:10:Quarks: -1:26:-T-----:2320:0040:99:99:UFO: +1:26:-T-----:2250:0005:99:99:UFO: 1:14:--W----:1920:2020:99:99:Rettungsflieger: -1:2:--W----:2110:2325:99:99:BulleVonToelz: +0:2:--W----:2110:2325:99:99:BulleVonToelz: 1:3:---T---:2210:2315:99:10:IngoAppelt: -0:2:----F--:2140:2225:10:10:WWW: -1:1:----F--:2212:2325:99:99:7Tage7Koepfe: +1:2:----F--:2013:2125:99:99:Farscape: +1:1:----F--:2215:2325:99:99:7Tage7Koepfe: 1:11:-----S-:2058:2135:99:99:Computer: -1:2:-----S-:2211:2340:99:30:Wochenshow: +1:2:-----S-:2250:0005:99:30:Wochenshow: 1:11:------S:2013:2035:99:10:Centauri: -1:14:------S:2158:2235:99:14:MaxUndLisa: 1:15:MTWTF--:1828:1901:10:5:nano: -1:1:-TWTF--:0955:1040:99:99:Ellen: 1:1:MTWTF--:1553:1710:99:99:Hammerman: +1:3:3:0220:0350:99:99:Seven Days - Das Tor zur Zeit:Die Rache des Alien||Als das Zeitsprung-Team die Leiche eines bei Roswell gefundenen Aliens obduziert, entdecken sie einen Chip in seiner Wirbelsäule. Dr. Ballard, der ähnliche Rückenprobleme wie der Alien hat, lässt sich den Chip einsetzen. Tatsächlich regeneriert Ballard vollständig. Doch dann ändert sich sein Verhalten: Er wird dem Alien immer ähnlicher. Schließlich gelingt es ihm, mit einer Superwaffe Millionen Menschen zu töten - einschließlich des Zeitsprung-Teams. Nur Parker überlebt. @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.c 1.21 2000/10/07 18:02:24 kls Exp $ + * $Id: tools.c 1.22 2000/10/29 11:21:55 kls Exp $ */ #define _GNU_SOURCE @@ -85,11 +85,16 @@ char *strreplace(char *s, char c1, char c2) return s; } -char *skipspace(char *s) +char *skipspace(const char *s) { while (*s && isspace(*s)) s++; - return s; + return (char *)s; +} + +bool isempty(const char *s) +{ + return !(s && *skipspace(s)); } int time_ms(void) @@ -520,7 +525,7 @@ void cListBase::Clear(void) objects = lastObject = NULL; } -cListObject *cListBase::Get(int Index) +cListObject *cListBase::Get(int Index) const { if (Index < 0) return NULL; @@ -530,7 +535,7 @@ cListObject *cListBase::Get(int Index) return object; } -int cListBase::Count(void) +int cListBase::Count(void) const { int n = 0; cListObject *object = objects; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.h 1.17 2000/10/07 18:00:21 kls Exp $ + * $Id: tools.h 1.18 2000/10/29 11:19:20 kls Exp $ */ #ifndef __TOOLS_H @@ -40,7 +40,8 @@ void purge(int filedes); char *readline(FILE *f); char *strn0cpy(char *dest, const char *src, size_t n); char *strreplace(char *s, char c1, char c2); -char *skipspace(char *s); +char *skipspace(const char *s); +bool isempty(const char *s); int time_ms(void); void delay_ms(int ms); bool isnumber(const char *s); @@ -80,8 +81,8 @@ public: void Append(cListObject *Object); void Unlink(void); int Index(void); - cListObject *Prev(void) { return prev; } - cListObject *Next(void) { return next; } + cListObject *Prev(void) const { return prev; } + cListObject *Next(void) const { return next; } }; class cListBase { @@ -95,15 +96,15 @@ public: virtual void Move(int From, int To); void Move(cListObject *From, cListObject *To); void Clear(void); - cListObject *Get(int Index); - int Count(void); + cListObject *Get(int Index) const; + int Count(void) const; }; template<class T> class cList : public cListBase { public: - T *Get(int Index) { return (T *)cListBase::Get(Index); } - T *First(void) { return (T *)objects; } - T *Next(T *object) { return (T *)object->Next(); } + T *Get(int Index) const { return (T *)cListBase::Get(Index); } + T *First(void) const { return (T *)objects; } + T *Next(const T *object) const { return (T *)object->Next(); } }; #endif //__TOOLS_H @@ -22,7 +22,7 @@ * * The project's page is at http://www.cadsoft.de/people/kls/vdr * - * $Id: vdr.c 1.39 2000/10/08 14:49:25 kls Exp $ + * $Id: vdr.c 1.41 2000/11/01 14:31:32 kls Exp $ */ #include <getopt.h> @@ -161,10 +161,6 @@ int main(int argc, char *argv[]) if (!cDvbApi::Init()) abort(); - // User interface: - - Interface = new cInterface(SVDRPport); - // Configuration data: if (!ConfigDirectory) @@ -176,14 +172,21 @@ int main(int argc, char *argv[]) #ifdef REMOTE_LIRC Keys.SetDummyValues(); #else - if (!Keys.Load(AddDirectory(ConfigDirectory, KEYS_CONF))) - Interface->LearnKeys(); + bool KeysLoaded = Keys.Load(AddDirectory(ConfigDirectory, KEYS_CONF)); #endif cDvbApi::SetPrimaryDvbApi(Setup.PrimaryDVB); Channels.SwitchTo(CurrentChannel); + // User interface: + + Interface = new cInterface(SVDRPport); +#ifndef REMOTE_LIRC + if (!KeysLoaded) + Interface->LearnKeys(); +#endif + // Signal handlers: if (signal(SIGHUP, SignalHandler) == SIG_IGN) signal(SIGHUP, SIG_IGN); @@ -202,7 +205,7 @@ int main(int argc, char *argv[]) // Channel display: if (CurrentChannel != LastChannel) { if (!Menu) - Channels.ShowChannel(CurrentChannel, LastChannel > 0); + Menu = new cDisplayChannel(CurrentChannel, LastChannel > 0); PreviousChannel = LastChannel; LastChannel = CurrentChannel; } @@ -262,7 +265,7 @@ int main(int argc, char *argv[]) // Direct Channel Select: case k1 ... k9: if (!Interface->Recording()) - Menu = new cDirectChannelSelect(key); + Menu = new cDisplayChannel(key); break; // Left/Right rotates trough channel groups: case kLeft|k_Repeat: @@ -276,8 +279,7 @@ int main(int argc, char *argv[]) CurrentGroup = Channels.GetPrevGroup(CurrentGroup < 1 ? 1 : CurrentGroup); if (CurrentGroup < 0) CurrentGroup = SaveGroup; - if (Channels.ShowChannel(CurrentGroup, false, true) == kOk) - Channels.SwitchTo(Channels.Get(Channels.GetNextNormal(CurrentGroup))->number); + Menu = new cDisplayChannel(CurrentGroup, false, true); } break; // Up/Down Channel Select: |