diff options
-rw-r--r-- | CONTRIBUTORS | 12 | ||||
-rw-r--r-- | HISTORY | 32 | ||||
-rw-r--r-- | README.vps | 6 | ||||
-rw-r--r-- | channels.conf | 26 | ||||
-rw-r--r-- | config.c | 2 | ||||
-rw-r--r-- | config.h | 6 | ||||
-rw-r--r-- | device.h | 4 | ||||
-rw-r--r-- | dvbdevice.c | 3 | ||||
-rw-r--r-- | eit.c | 22 | ||||
-rw-r--r-- | eitscan.c | 2 | ||||
-rw-r--r-- | eitscan.h | 2 | ||||
-rw-r--r-- | epg.c | 52 | ||||
-rw-r--r-- | epg.h | 13 | ||||
-rw-r--r-- | i18n.c | 28 | ||||
-rw-r--r-- | libsi/descriptor.c | 156 | ||||
-rw-r--r-- | libsi/descriptor.h | 27 | ||||
-rw-r--r-- | libsi/si.c | 83 | ||||
-rw-r--r-- | libsi/si.h | 63 | ||||
-rw-r--r-- | menu.c | 32 | ||||
-rw-r--r-- | menu.h | 2 | ||||
-rw-r--r-- | menuitems.c | 2 | ||||
-rw-r--r-- | menuitems.h | 2 | ||||
-rw-r--r-- | osd.c | 36 | ||||
-rw-r--r-- | pat.c | 18 | ||||
-rw-r--r-- | pat.h | 7 | ||||
-rw-r--r-- | recorder.c | 4 | ||||
-rw-r--r-- | ringbuffer.c | 17 | ||||
-rw-r--r-- | ringbuffer.h | 6 | ||||
-rw-r--r-- | sdt.c | 27 | ||||
-rw-r--r-- | svdrp.c | 2 | ||||
-rw-r--r-- | timers.c | 42 | ||||
-rw-r--r-- | timers.h | 2 | ||||
-rw-r--r-- | transfer.c | 4 | ||||
-rw-r--r-- | vdr.5 | 2 | ||||
-rw-r--r-- | vdr.c | 16 |
35 files changed, 544 insertions, 216 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 438d827..1f72231 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -514,6 +514,8 @@ Oliver Endriss <o.endriss@gmx.de> for suggesting to add 'repeat' function keys '7' and '9' for fixing handling rc key learning in case cRemote::Initialize() returns 'false' for suggesting to change the default "Lifetime" to 99 + for pointing out that the LNB power needs to be explicitly turned on at startup, + because newer drivers don't do this any more Reinhard Walter Buchner <rw.buchner@freenet.de> for adding some satellites to 'sources.conf' @@ -943,3 +945,13 @@ Thomas Bergwinkl <Thomas.Bergwinkl@t-online.de> Stéphane Esté-Gracias <sestegra@free.fr> for fixing a typo in libsi/si.h + for fixing some descriptor handling in 'libsi' + for pointing out a problem with "itemized" texts in EPG data + for pointing out a problem with taking the Sid into account when detecting version + changes in processing the PMT + +Marc Hoppe <MarcHoppe@gmx.de> + for fixing handling the current menu item + +Michael Pennewiß <M.Pennewiss@ARD-Digital.de> + for pointing out that an empty EPG event means there is currently no running event @@ -2713,3 +2713,35 @@ Video Disk Recorder Revision History whether an event has a VPS time that's different than its start time, and whether an event is currently running (see MANUAL under "The "Schedule" Menu" for details). + +2004-03-14: Version 1.3.6 + +- Completed the Finnish OSD texts (thanks to Rolf Ahrenberg). +- Fixed some descriptor handling in 'libsi' (thanks to Stéphane Esté-Gracias). +- Fixed handling the current menu item (thanks to Marc Hoppe). +- Fixed assigning events to timers (they no longer get "stuck"). +- Added log entries whenever the running status of an event changes (currently + only logging the first 30 channels). +- Fixed handling timers in VPS margin if the EPG scan is turned on (the EPG scan + switched the device away from the channel, so it wouldn't see the change of + the running status). +- Fixed handling "itemized" texts in EPG data (thanks to Stéphane Esté-Gracias + for pointing out this problem, and Marcel Wiesweg for improving 'libsi'). +- Fixed handling VPS times at year boundaries. +- Avoiding too many consecutive "ring buffer overflow" messages (which only + slowed down performance even more). +- Taking the Sid into account when detecting version changes in processing the + PMT (thanks to Stéphane Esté-Gracias for pointing out this problem). +- Completed the Russian OSD texts (thanks to Vyacheslav Dikonov). +- Any newline characters in the 'description' of EPG events are now preserved + to allow texts to be displayed the way the tv stations have formatted them. + This was also necessary to better display itemized texts. +- Fixed detecting the running status in case an empty EPG event is broadcast (thanks + to Michael Pennewiß for pointing this out). +- Improved performance when paging through very long menu lists. +- Removed cSchedule::GetEventNumber() and cSchedule::NumEvents(). There is now + cSchedule::Events() that returns the list of events directly. +- Avoiding occasional bad responsiveness to user interaction caused by assigning + events to timers. +- Now explicitly turning on the LNB power at startup, because newer drivers don't + do this any more (thanks to Oliver Endriss for pointing this out). @@ -128,3 +128,9 @@ stations: information to control recording would not see the end of that programme. ... more following as it comes up... + +Contact: +-------- + +ARD Digital: http://www.ard-digital.de/home/index.php?id=16&languageid=1 +ZDF vision: http://www.zdf.de/ZDFde/inhalt/1/0,1872,1021601,00.html (select "zdfvision") diff --git a/channels.conf b/channels.conf index f36c5f3..ca0b6fc 100644 --- a/channels.conf +++ b/channels.conf @@ -24,9 +24,10 @@ VOX:12187:hC34:S19.2E:27500:167:136=deu:71:0:12060:1:1089:0 KABEL1:12480:vC34:S19.2E:27500:511:512:33:0:899:133:33:0 NEUN LIVE,NEUN LIVE Television:12480:vC34:S19.2E:27500:767:768:35:0:897:133:33:0 DSF:12480:vC34:S19.2E:27500:1023:1024=deu:0:0:900:133:33:0 -HSEurope,Home Shopping Europe:12480:vC34:S19.2E:27500:1279:1280:37:0:40:133:33:0 +HSE24:12480:vC34:S19.2E:27500:1279:1280:37:0:40:133:33:0 Bloomberg TV Germany:12551:vC56:S19.2E:22000:162:99=deu:0:0:12160:1:1108:0 EURONEWS:11817:vC34:S19.2E:27500:163:92=fra,93=eng,94=ita,95=esl,91=rus,98=por,99=deu:0:0:8004:1:1070:0 +rbb Brandenburg:12109:hC34:S19.2E:27500:501:502=deu:504:0:28205:1:1073:0 Sky News Intl:11597:vC56:S19.2E:22000:305+131:306=eng:0:0:28707:1:1026:0 Veronica/FoxKids:12574:hC56:S19.2E:22000:518+8190:92=dut:38:622,602,100:5020:53:1109:0 BVN:12574:hC56:S19.2E:22000:515+8190:96=dut:36:0:5025:53:1109:0 @@ -38,20 +39,19 @@ Eurosport:11953:hC34:S19.2E:27500:410:420=deu:430:0:28009:1:1079:0 EinsExtra:12109:hC34:S19.2E:27500:101:102=deu:0:0:28201:1:1073:0 EinsFestival:12109:hC34:S19.2E:27500:201:202=deu:0:0:28202:1:1073:0 EinsMuXx:12109:hC34:S19.2E:27500:301:302=deu:0:0:28203:1:1073:0 -ZDFtheaterkanal:11953:hC34:S19.2E:27500:1110:1120:130:0:28016:1:1079:0 +ZDFtheaterkanal:11953:hC34:S19.2E:27500:1110:1120=deu:130:0:28016:1:1079:0 ZDFdokukanal:11953:hC34:S19.2E:27500:660:670=deu:130:0:28014:1:1079:0 MDR FERNSEHEN:12109:hC34:S19.2E:27500:401:402=deu:404:0:28204:1:1073:0 -RBB Brandenburg:12109:hC34:S19.2E:27500:501:502=deu:504:0:28205:1:1073:0 -RBB Berlin:12109:hC34:S19.2E:27500:601:602=deu:604:0:28206:1:1073:0 +rbb Berlin:12109:hC34:S19.2E:27500:601:602=deu:604:0:28206:1:1073:0 :Premiere World -START,PREMIERE START:11797:hC34:S19.2E:27500:255:256=deu:32:1702,1722,1801:8:133:2:0 -PREM 1,PREMIERE 1:11797:hC34:S19.2E:27500:511:512=deu,513=deu;515=deu:0:1702,1722,1801:10:133:2:0 +START,PREMIERE START:11797:hC34:S19.2E:27500:255:256=deu:32:1722,1801,1702:8:133:2:0 +PREM 1,PREMIERE 1:11797:hC34:S19.2E:27500:511:512=deu;515=deu:0:1702,1801,1722:10:133:2:0 PREM 2,PREMIERE 2:11797:hC34:S19.2E:27500:1791:1792=deu,1793=deu;1795=deu:0:1702,1722,1801:11:133:2:0 -PREM 3,PREMIERE 3:11797:hC34:S19.2E:27500:2303:2304=deu:0:1702,1722,1801:43:133:2:0 -PREM 4,PREMIERE 4:11797:hC34:S19.2E:27500:767:768=deu:0:1702,1722,1801:9:133:2:0 -PREM 5,PREMIERE 5:11797:hC34:S19.2E:27500:1279:1280=deu,1281=deu:0:1702,1722,1801:29:133:2:0 -PREM 6,PREMIERE 6:11797:hC34:S19.2E:27500:1535:1536=deu:0:1702,1722,1801:41:133:2:0 -PREM 7,PREMIERE 7:11797:hC34:S19.2E:27500:1023:1024=deu:0:1702,1722,1801:20:133:2:0 +PREM 3,PREMIERE 3:11797:hC34:S19.2E:27500:2303:2304=deu:0:1722,1702,1801:43:133:2:0 +PREM 4,PREMIERE 4:11797:hC34:S19.2E:27500:767:768=deu:0:1801,1722,1702:9:133:2:0 +PREM 5,PREMIERE 5:11797:hC34:S19.2E:27500:1279:1280=deu:0:1801,1702,1722:29:133:2:0 +PREM 6,PREMIERE 6:11797:hC34:S19.2E:27500:1535:1536=deu:0:1722,1801,1702:41:133:2:0 +PREM 7,PREMIERE 7:11797:hC34:S19.2E:27500:1023:1024=deu:0:1722,1702,1801:20:133:2:0 DISNEY,DISNEY CHANNEL:11758:hC34:S19.2E:27500:2559:2560=deu:0:1702,1722,1801:34:133:17:0 :Premiere Direkt DIREKT,PREMIERE DIREKT:12031:hC34:S19.2E:27500:2815:2816=deu,2817=deu;2819=deu:0:0:18:133:4:0 @@ -59,7 +59,7 @@ DIREKT,PREMIERE DIREKT:12031:hC34:S19.2E:27500:2815:2816=deu,2817=deu;2819=deu:0 B-UHSE,BEATE-UHSE.TV:12070:hC34:S19.2E:27500:1023:1024=deu:0:1702,1722,1801:21:133:1:0 EROTIK,PREMIERE EROTIK:12031:hC34:S19.2E:27500:1279:0:0:1702,1722,1801:513:133:4:0 :Sportsworld -Konferenz:11719:hC34:S19.2E:27500:255:256=deu:0:1702,1722,1801:17:133:3:0 +SPORT 1,PREMIERE SPORT 1:11719:hC34:S19.2E:27500:255:256=deu,257=deu:0:1702,1722,1801:17:133:3:0 SPORT 2,PREMIERE SPORT 2:12031:hC34:S19.2E:27500:3839:3840=deu,3841=deu:0:1702,1722,1801:27:133:4:0 :Beta Digital N24:12480:vC34:S19.2E:27500:2047:2048:36:0:47:133:33:0 @@ -114,6 +114,6 @@ Animal Plnt+:12070:hC23:S28.2E:27500:2315+2307:2316=eng:0:960,961:50002:2:2019:0 S1T:12285:vC23:S28.2E:27500:2311+2304:2312=eng,2313=NAR:2307:960,961:4409:2:2030:0 CNN:12051:vC23:S28.2E:27500:2309:2311=eng:2310:0:7140:2:2018:0 BBC PARL'MNT:12129:vC23:S28.2E:27500:2306:2308=eng,2309=eng:2307:0:7300:2:2022:0 -AL HAYAT:11200:vC56:S13.0E:27500:413:414:0:0:4733:318:13400:0 +AL HAYAT:11200:vC56:S13.0E:27500:413:414=eng:0:0:4733:318:13400:0 EURO1080:12168:vC34:S19.2E:27500:308:256:0:FF:21100:1:1088:0 :@1000 New channels @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.c 1.124 2004/02/21 15:05:40 kls Exp kls $ + * $Id: config.c 1.125 2004/02/28 11:12:20 kls Exp $ */ #include "config.h" @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 1.188 2004/02/21 15:04:53 kls Exp kls $ + * $Id: config.h 1.190 2004/03/05 14:35:15 kls Exp $ */ #ifndef __CONFIG_H @@ -20,8 +20,8 @@ #include "i18n.h" #include "tools.h" -#define VDRVERSION "1.3.5" -#define VDRVERSNUM 10305 // Version * 10000 + Major * 100 + Minor +#define VDRVERSION "1.3.6" +#define VDRVERSNUM 10306 // Version * 10000 + Major * 100 + Minor #define MAXPRIORITY 99 #define MAXLIFETIME 99 @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.h 1.40 2004/02/14 11:29:57 kls Exp $ + * $Id: device.h 1.41 2004/03/14 10:47:01 kls Exp $ */ #ifndef __DEVICE_H @@ -382,7 +382,7 @@ public: virtual int PlayVideo(const uchar *Data, int Length); ///< Actually plays the given data block as video. The data must be ///< part of a PES (Packetized Elementary Stream) which can contain - ///< one video and one audio strem. + ///< one video and one audio stream. virtual void PlayAudio(const uchar *Data, int Length); ///< Plays additional audio streams, like Dolby Digital. ///< A derived class must call the base class function to make sure data diff --git a/dvbdevice.c b/dvbdevice.c index 8ed62e1..6e1d7f9 100644 --- a/dvbdevice.c +++ b/dvbdevice.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbdevice.c 1.82 2004/02/24 10:12:13 kls Exp $ + * $Id: dvbdevice.c 1.83 2004/03/14 14:47:46 kls Exp $ */ #include "dvbdevice.h" @@ -101,6 +101,7 @@ cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCi useCa = false; tunerStatus = tsIdle; startTime = time(NULL); + CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power SetDescription("tuner on device %d", cardIndex + 1); Start(); } @@ -8,7 +8,7 @@ * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>. * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>. * - * $Id: eit.c 1.89 2004/02/22 13:17:52 kls Exp kls $ + * $Id: eit.c 1.93 2004/03/13 13:54:20 kls Exp $ */ #include "eit.h" @@ -43,10 +43,12 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) Schedules->Add(pSchedule); } + bool Empty = true; bool Modified = false; SI::EIT::Event SiEitEvent; for (SI::Loop::Iterator it; eventLoop.hasNext(it); ) { + Empty = false; SiEitEvent = eventLoop.getNext(it); cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), SiEitEvent.getStartTime()); @@ -82,10 +84,6 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) pEvent->SetVersion(getVersionNumber()); pEvent->SetStartTime(SiEitEvent.getStartTime()); pEvent->SetDuration(SiEitEvent.getDuration()); - if (isPresentFollowing()) { - if (SiEitEvent.getRunningStatus() > SI::RunningStatusNotRunning) - pSchedule->SetRunningStatus(pEvent, SiEitEvent.getRunningStatus()); - } int LanguagePreferenceShort = -1; int LanguagePreferenceExt = -1; @@ -130,11 +128,16 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) struct tm tm_r; struct tm t = *localtime_r(&now, &tm_r); // this initializes the time zone in 't' t.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting + int month = t.tm_mon; t.tm_mon = pd->getMonth() - 1; t.tm_mday = pd->getDay(); t.tm_hour = pd->getHour(); t.tm_min = pd->getMinute(); t.tm_sec = 0; + if (month == 11 && t.tm_mon == 0) // current month is dec, but event is in jan + t.tm_year++; + else if (month == 0 && t.tm_mon == 11) // current month is jan, but event is in dec + t.tm_year--; time_t vps = mktime(&t); pEvent->SetVps(vps); } @@ -195,7 +198,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) } if (ExtendedEventDescriptors) { char buffer[ExtendedEventDescriptors->getMaximumTextLength()]; - pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer)); + pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer, ": ")); } } delete ExtendedEventDescriptors; @@ -205,8 +208,15 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) if (LinkChannels) channel->SetLinkChannels(LinkChannels); + if (Tid == 0x4E) { // we trust only the present/following info on the actual TS + if (SiEitEvent.getRunningStatus() >= SI::RunningStatusNotRunning) + pSchedule->SetRunningStatus(pEvent, SiEitEvent.getRunningStatus(), channel); + } Modified = true; } + if (Empty && Tid == 0x4E && getSectionNumber() == 0) + // ETR 211: an empty entry in section 0 of table 0x4E means there is currently no event running + pSchedule->ClrRunningStatus(channel); if (Modified) pSchedule->Sort(); } @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: eitscan.c 1.21 2004/02/14 13:44:31 kls Exp kls $ + * $Id: eitscan.c 1.21 2004/02/14 13:44:31 kls Exp $ */ #include "eitscan.h" @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: eitscan.h 1.8 2004/01/17 15:36:24 kls Exp kls $ + * $Id: eitscan.h 1.8 2004/01/17 15:36:24 kls Exp $ */ #ifndef __EITSCAN_H @@ -7,7 +7,7 @@ * Original version (as used in VDR before 1.3.0) written by * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>. * - * $Id: epg.c 1.13 2004/02/22 14:41:37 kls Exp kls $ + * $Id: epg.c 1.18 2004/03/13 15:01:05 kls Exp $ */ #include "epg.h" @@ -61,8 +61,11 @@ void cEvent::SetVersion(uchar Version) version = Version; } -void cEvent::SetRunningStatus(int RunningStatus) +void cEvent::SetRunningStatus(int RunningStatus, cChannel *Channel) { + if (Channel && runningStatus != RunningStatus && (RunningStatus > SI::RunningStatusNotRunning || runningStatus > SI::RunningStatusUndefined)) + if (Channel->Number() <= 30)//XXX maybe log only those that have timers??? + isyslog("channel %d (%s) event %s '%s' status %d", Channel->Number(), Channel->Name(), GetTimeString(), Title(), RunningStatus); runningStatus = RunningStatus; } @@ -105,6 +108,11 @@ bool cEvent::HasTimer(void) const return false; } +bool cEvent::IsRunning(bool OrAboutToStart) const +{ + return runningStatus >= (OrAboutToStart ? SI::RunningStatusStartsInAFewSeconds : SI::RunningStatusPausing); +} + const char *cEvent::GetDateString(void) const { static char buf[25]; @@ -146,8 +154,11 @@ void cEvent::Dump(FILE *f, const char *Prefix) const fprintf(f, "%sT %s\n", Prefix, title); if (!isempty(shortText)) fprintf(f, "%sS %s\n", Prefix, shortText); - if (!isempty(description)) + if (!isempty(description)) { + strreplace(description, '\n', '|'); fprintf(f, "%sD %s\n", Prefix, description); + strreplace(description, '|', '\n'); + } if (vps) fprintf(f, "%sV %ld\n", Prefix, vps); fprintf(f, "%se\n", Prefix); @@ -186,8 +197,10 @@ bool cEvent::Read(FILE *f, cSchedule *Schedule) case 'S': if (Event) Event->SetShortText(t); break; - case 'D': if (Event) + case 'D': if (Event) { + strreplace(t, '|', '\n'); Event->SetDescription(t); + } break; case 'V': if (Event) Event->SetVps(atoi(t)); @@ -282,11 +295,10 @@ void ReportEpgBugFixStats(bool Reset) void cEvent::FixEpgBugs(void) { - // VDR can't usefully handle newline characters in the EPG data, so let's - // always convert them to blanks (independent of the setting of EPGBugfixLevel): + // VDR can't usefully handle newline characters in the title and shortText of EPG + // data, so let's always convert them to blanks (independent of the setting of EPGBugfixLevel): strreplace(title, '\n', ' '); strreplace(shortText, '\n', ' '); - strreplace(description, '\n', ' '); // Same for control characters: strreplace(title, '\x86', ' '); strreplace(title, '\x87', ' '); @@ -462,6 +474,7 @@ void cEvent::FixEpgBugs(void) cSchedule::cSchedule(tChannelID ChannelID) { channelID = ChannelID; + hasRunning = false;; } cEvent *cSchedule::AddEvent(cEvent *Event) @@ -475,7 +488,7 @@ const cEvent *cSchedule::GetPresentEvent(bool CheckRunningStatus) const const cEvent *pe = NULL; time_t now = time(NULL); for (cEvent *p = events.First(); p; p = events.Next(p)) { - if (p->StartTime() <= now && now < p->StartTime() + p->Duration()) { + if (p->StartTime() <= now && now < p->EndTime()) { pe = p; if (!CheckRunningStatus) break; @@ -514,7 +527,7 @@ const cEvent *cSchedule::GetEventAround(time_t Time) const time_t delta = INT_MAX; for (cEvent *p = events.First(); p; p = events.Next(p)) { time_t dt = Time - p->StartTime(); - if (dt >= 0 && dt < delta && p->StartTime() + p->Duration() >= Time) { + if (dt >= 0 && dt < delta && p->EndTime() >= Time) { delta = dt; pe = p; } @@ -522,14 +535,29 @@ const cEvent *cSchedule::GetEventAround(time_t Time) const return pe; } -void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus) +void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, cChannel *Channel) { for (cEvent *p = events.First(); p; p = events.Next(p)) { if (p == Event) - p->SetRunningStatus(RunningStatus); + p->SetRunningStatus(RunningStatus, Channel); else if (RunningStatus >= SI::RunningStatusPausing && p->RunningStatus() > SI::RunningStatusNotRunning) p->SetRunningStatus(SI::RunningStatusNotRunning); } + if (RunningStatus >= SI::RunningStatusPausing) + hasRunning = true; +} + +void cSchedule::ClrRunningStatus(cChannel *Channel) +{ + if (hasRunning) { + for (cEvent *p = events.First(); p; p = events.Next(p)) { + if (p->RunningStatus() >= SI::RunningStatusPausing) { + p->SetRunningStatus(SI::RunningStatusNotRunning, Channel); + hasRunning = false; + break; + } + } + } } void cSchedule::ResetVersions(void) @@ -555,7 +583,7 @@ void cSchedule::Cleanup(time_t Time) Event = events.Get(a); if (!Event) break; - if (!Event->HasTimer() && Event->StartTime() + Event->Duration() + Setup.EPGLinger * 60 + 3600 < Time) { // adding one hour for safety + if (!Event->HasTimer() && Event->EndTime() + Setup.EPGLinger * 60 + 3600 < Time) { // adding one hour for safety events.Del(Event); a--; } @@ -7,7 +7,7 @@ * Original version (as used in VDR before 1.3.0) written by * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>. * - * $Id: epg.h 1.10 2004/02/22 14:34:04 kls Exp kls $ + * $Id: epg.h 1.15 2004/03/14 13:25:39 kls Exp $ */ #ifndef __EPG_H @@ -49,9 +49,11 @@ public: const char *ShortText(void) const { return shortText; } const char *Description(void) const { return description; } time_t StartTime(void) const { return startTime; } + time_t EndTime(void) const { return startTime + duration; } int Duration(void) const { return duration; } time_t Vps(void) const { return vps; } bool HasTimer(void) const; + bool IsRunning(bool OrAboutToStart = false) const; const char *GetDateString(void) const; const char *GetTimeString(void) const; const char *GetEndTimeString(void) const; @@ -59,7 +61,7 @@ public: void SetEventID(u_int16_t EventID); void SetTableID(uchar TableID); void SetVersion(uchar Version); - void SetRunningStatus(int RunningStatus); + void SetRunningStatus(int RunningStatus, cChannel *Channel = NULL); void SetTitle(const char *Title); void SetShortText(const char *ShortText); void SetDescription(const char *Description); @@ -77,21 +79,22 @@ class cSchedule : public cListObject { private: tChannelID channelID; cList<cEvent> events; + bool hasRunning; public: cSchedule(tChannelID ChannelID); tChannelID ChannelID(void) const { return channelID; } - void SetRunningStatus(cEvent *Event, int RunningStatus); + void SetRunningStatus(cEvent *Event, int RunningStatus, cChannel *Channel = NULL); + void ClrRunningStatus(cChannel *Channel = NULL); void ResetVersions(void); void Sort(void); void Cleanup(time_t Time); void Cleanup(void); cEvent *AddEvent(cEvent *Event); + const cList<cEvent> *Events(void) const { return &events; } const cEvent *GetPresentEvent(bool CheckRunningStatus = false) const; const cEvent *GetFollowingEvent(bool CheckRunningStatus = false) const; const cEvent *GetEvent(u_int16_t EventID, time_t StartTime = 0) const; const cEvent *GetEventAround(time_t Time) const; - const cEvent *GetEventNumber(int n) const { return events.Get(n); } - int NumEvents(void) const { return events.Count(); } void Dump(FILE *f, const char *Prefix = "", eDumpMode DumpMode = dmAll, time_t AtTime = 0) const; static bool Read(FILE *f, cSchedules *Schedules); }; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: i18n.c 1.148 2004/02/21 15:14:36 kls Exp kls $ + * $Id: i18n.c 1.152 2004/03/13 10:59:23 kls Exp $ * * Translations provided by: * @@ -1550,6 +1550,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO + "VPS", "",// TODO "",// TODO "",// TODO @@ -1557,8 +1558,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO - "",// TODO + "VPS ßÞßàÐÒÚÐ", }, { "Priority", "Priorität", @@ -1839,9 +1839,9 @@ const tI18nPhrase Phrases[] = { "",//TODO "Pas de marques d'édition définies!", "",//TODO - "",//TODO + "Muokkausmerkinnät puuttuvat!", "Brak znakow montazowych!", - "Muokkausmerkinnät puuttuvat", + "",//TODO "ÄÝí Ý÷ïõí ïñéóôåß óçìåßá åðåîåñãáóßáò", "Det finns inga redigeringsmärken",//TODO "",//TODO @@ -2389,7 +2389,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO + "ÅàÐÝÕÝØÕ ãáâÐàÕÒèØå ÔÐÝÝëå (ÜØÝ)", }, { "Setup.EPG$Set system time", "Systemzeit stellen", @@ -2777,7 +2777,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "Priorité des pauses", "",// TODO - "Keskeytyksen prioriteetti", + "Taukotallenteen prioriteetti", "Priorytet przerwy", "",// TODO "Ðñïôåñáéüôçôá äéáëåßììáôïò", @@ -2795,7 +2795,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "Durée de vie des pauses (j)", "",// TODO - "Keskeytyksen elinikä (d)", + "Taukotallenteen elinikä (d)", "Okres trwania przerwy (d)", "",// TODO "ÄéÜñêåéá äéáëåßìáôïò", @@ -2831,6 +2831,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO + "Käytä VPS-toimintoa", "",// TODO "",// TODO "",// TODO @@ -2838,8 +2839,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO - "",// TODO + "¸áßÞÛì×ÞÒÐâì áØÓÝÐÛë VPS", }, { "Setup.Recording$VPS margin (s)", "Zeitpuffer bei VPS (s)", @@ -2849,6 +2849,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO + "VPS-toiminnon aloitusmarginaali (s)", "",// TODO "",// TODO "",// TODO @@ -2856,8 +2857,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "",// TODO "",// TODO - "",// TODO - "",// TODO + "±ãäÕàÝÞÕ ÒàÕÜï VPS (áÕÚ)", }, { "Setup.Recording$Mark instant recording", "Direktaufzeichnung markieren", @@ -3627,7 +3627,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "Pause", "",// TODO - "Keskeytä", + "Tauko", "Przerwa", "Pausa", "ÄéÜëåéììá", @@ -4186,7 +4186,7 @@ const tI18nPhrase Phrases[] = { "",// TODO "Pause de l'émission en direct...", "",// TODO - "Keskeytetään lähetys...", + "Pysäytetään lähetys...", "Zatrzymany program biezacy...", "Emisión en directo parada...", "ÄéÜëåéììá æùíôáíïý óÞìáôïò", diff --git a/libsi/descriptor.c b/libsi/descriptor.c index 0b0019f..82e2a8f 100644 --- a/libsi/descriptor.c +++ b/libsi/descriptor.c @@ -6,7 +6,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: descriptor.c 1.6 2004/02/22 11:11:36 kls Exp $ + * $Id: descriptor.c 1.10 2004/03/13 15:08:12 kls Exp $ * * ***************************************************************************/ @@ -60,13 +60,13 @@ void ExtendedEventDescriptor::Item::Parse() { item.setData(data+offset, mid->item_length); } -int ExtendedEventDescriptors::getTextLength() { +/*int ExtendedEventDescriptors::getTextLength() { int ret=0; for (int i=0;i<length;i++) { ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[i]; if (!d) continue; - ret+=d->text.getLength()+1; //plus a blank + ret+=d->text.getLength(); ExtendedEventDescriptor::Item item; for (Loop::Iterator it; d->itemLoop.hasNext(it); ) { item=d->itemLoop.getNext(it); @@ -76,28 +76,84 @@ int ExtendedEventDescriptors::getTextLength() { } } return ret; -} +}*/ -//is there a case where this function does not return the same as getTextLength? int ExtendedEventDescriptors::getMaximumTextLength() { + return getMaximumTextPlainLength()+getMaximumTextItemizedLength(); +} + +char *ExtendedEventDescriptors::getText(const char *separation1, const char *separation2) { + char *text=new char[getMaximumTextLength()+strlen(separation1)+strlen(separation2)]; + return getText(text, separation1, separation2); +} + +char *ExtendedEventDescriptors::getText(char *buffer, const char *separation1, const char *separation2) { + int index=0, len; + char tempbuf[256]; + for (int i=0;i<length;i++) { + ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[i]; + if (!d) + continue; + d->text.getText(tempbuf); + len=strlen(tempbuf); + if (len) { + memcpy(buffer+index, tempbuf, len); + index+=len; + } + } + + for (int i=0;i<length;i++) { + ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[i]; + if (!d) + continue; + + strcpy(buffer+index, separation2); // let's have a separator between the long text and the items + index += strlen(separation2); + ExtendedEventDescriptor::Item item; + for (Loop::Iterator it; d->itemLoop.hasNext(it); ) { + item=d->itemLoop.getNext(it); + + item.itemDescription.getText(tempbuf); + len=strlen(tempbuf); + if (len) { + memcpy(buffer+index, tempbuf, len); + index+=len; + } + strcpy(buffer+index, separation1); + index += strlen(separation1); + + item.item.getText(tempbuf); + len=strlen(tempbuf); + if (len) { + memcpy(buffer+index, tempbuf, len); + index+=len; + } + strcpy(buffer+index, separation2); + index += strlen(separation2); + } + } + + buffer[index]='\0'; + return buffer; +} + +int ExtendedEventDescriptors::getMaximumTextPlainLength() { int ret=0; for (int i=0;i<length;i++) { ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[i]; if (!d) continue; - ret+=d->text.getLength()+1; //plus a blank - ret+=d->itemLoop.getLength(); + ret+=d->text.getLength(); } return ret; } -char *ExtendedEventDescriptors::getText() { - char *text=new char[getMaximumTextLength()]; - return getText(text); +char *ExtendedEventDescriptors::getTextPlain() { + char *text=new char[getMaximumTextPlainLength()]; + return getTextPlain(text); } -//appends the Strings of every Descriptor in the group -char *ExtendedEventDescriptors::getText(char *buffer) { +char *ExtendedEventDescriptors::getTextPlain(char *buffer) { int index=0, len; char tempbuf[256]; for (int i=0;i<length;i++) { @@ -110,30 +166,98 @@ char *ExtendedEventDescriptors::getText(char *buffer) { memcpy(buffer+index, tempbuf, len); index+=len; } + } + buffer[index]='\0'; + return buffer; +} + +int ExtendedEventDescriptors::getMaximumTextItemizedLength() { + int ret=0; + for (int i=0;i<length;i++) { + ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[i]; + if (!d) + continue; + //the size for the two separating characters is included ;-) + ret+=d->itemLoop.getLength(); + } + return ret; +} + +char *ExtendedEventDescriptors::getTextItemized(const char *separation1, const char *separation2) { + char *text=new char[getMaximumTextItemizedLength()+strlen(separation1)+strlen(separation2)]; + return getTextItemized(text, separation1, separation2); +} + +char *ExtendedEventDescriptors::getTextItemized(char *buffer, const char *separation1, const char *separation2) { + int index=0, len; + char tempbuf[256]; + for (int i=0;i<length;i++) { + ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[i]; + if (!d) + continue; ExtendedEventDescriptor::Item item; for (Loop::Iterator it; d->itemLoop.hasNext(it); ) { item=d->itemLoop.getNext(it); - item.item.getText(tempbuf); + item.itemDescription.getText(tempbuf); len=strlen(tempbuf); if (len) { memcpy(buffer+index, tempbuf, len); index+=len; } + strcpy(buffer+index, separation1); + index += strlen(separation1); - item.itemDescription.getText(tempbuf); + item.item.getText(tempbuf); len=strlen(tempbuf); if (len) { memcpy(buffer+index, tempbuf, len); index+=len; } + strcpy(buffer+index, separation2); + index += strlen(separation2); } } buffer[index]='\0'; return buffer; } +//returns the itemized text pair by pair. Maximum length for buffers is 256. +//Return value is false if and only if the end of the list is reached. +bool ExtendedEventDescriptors::getTextItemized(Loop::Iterator &it, bool &valid, char *itemDescription, char *itemText) { + //The iterator has to store two values: The descriptor index (4bit) + //and the item loop index (max overall length 256, min item length 16 => max number 128 => 7bit) + valid=false; + + int index=(it.i & 0x780) >> 7; // 0x780 == 1111 000 0000 + it.i &= 0x7F; //0x7F == 111 1111 + + for (;index<length;index++) { + ExtendedEventDescriptor *d=(ExtendedEventDescriptor *)array[index]; + if (!d) + continue; + + ExtendedEventDescriptor::Item item; + if (d->itemLoop.hasNext(it)) { + item=d->itemLoop.getNext(it); + + item.item.getText(itemDescription); + item.itemDescription.getText(itemText); + valid=true; + break; + } else { + it.reset(); + continue; + } + } + + it.i &= 0x7F; + it.i |= (index & 0xF) << 7; //0xF == 1111 + + return index<length; +} + int TimeShiftedEventDescriptor::getReferenceServiceId() const { return HILO(s->reference_service_id); } @@ -148,7 +272,7 @@ void TimeShiftedEventDescriptor::Parse() { void ContentDescriptor::Parse() { //this descriptor is only a header and a loop - nibbleLoop.setData(data+sizeof(SectionHeader), getLength()-sizeof(SectionHeader)); + nibbleLoop.setData(data+sizeof(descr_content), getLength()-sizeof(descr_content)); } int ContentDescriptor::Nibble::getContentNibbleLevel1() const { @@ -173,7 +297,7 @@ void ContentDescriptor::Nibble::Parse() { void ParentalRatingDescriptor::Parse() { //this descriptor is only a header and a loop - ratingLoop.setData(data+sizeof(SectionHeader), getLength()-sizeof(SectionHeader)); + ratingLoop.setData(data+sizeof(descr_parental_rating), getLength()-sizeof(descr_parental_rating)); } int ParentalRatingDescriptor::Rating::getRating() const { diff --git a/libsi/descriptor.h b/libsi/descriptor.h index 17c81eb..3368b0a 100644 --- a/libsi/descriptor.h +++ b/libsi/descriptor.h @@ -6,7 +6,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: descriptor.h 1.6 2004/02/22 10:16:47 kls Exp $ + * $Id: descriptor.h 1.7 2004/03/07 11:13:54 kls Exp $ * * ***************************************************************************/ @@ -50,14 +50,29 @@ private: class ExtendedEventDescriptors : public DescriptorGroup { public: - //don't use - int getTextLength(); - //really fast int getMaximumTextLength(); + //Returns a concatenated version of first the non-itemized and then the itemized text //same semantics as with SI::String - char *getText(); + char *getText(const char *separation1="\t", const char *separation2="\n"); //buffer must at least be getTextLength(), getMaximumTextLength() is a good choice - char *getText(char *buffer); + char *getText(char *buffer, const char *separation1="\t", const char *separation2="\n"); + + //these only return the non-itemized text fields in concatenated form + int getMaximumTextPlainLength(); + char *getTextPlain(); + char *getTextPlain(char *buffer); + + //these only return the itemized text fields in concatenated form. + //Between the description and the text the separation1 character is used, + //separation2 used between two pairs. Example: + //Director\tSteven Spielberg\nActor\tMichael Mendl\n + int getMaximumTextItemizedLength(); + char *getTextItemized(const char *separation1="\t", const char *separation2="\n"); + char *getTextItemized(char *buffer, const char *separation1="\t", const char *separation2="\n"); + //returns the itemized text pair by pair. Maximum length for buffers is 256. + //Return value is false if and only if the end of the list is reached. + //The argument valid indicates whether the buffers contain valid content. + bool getTextItemized(Loop::Iterator &it, bool &valid, char *itemDescription, char *itemText); }; class TimeShiftedEventDescriptor : public Descriptor { @@ -6,7 +6,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: si.c 1.8 2004/02/22 10:14:12 kls Exp $ + * $Id: si.c 1.9 2004/03/07 10:50:09 kls Exp $ * * ***************************************************************************/ @@ -103,7 +103,7 @@ DescriptorTag Descriptor::getDescriptorTag(const unsigned char *d) { Descriptor *DescriptorLoop::getNext(Iterator &it) { if (it.i<getLength()) { - return createDescriptor(it.i); + return createDescriptor(it.i, true); } return 0; } @@ -115,19 +115,18 @@ Descriptor *DescriptorLoop::getNext(Iterator &it, DescriptorTag tag, bool return const unsigned char *end=p+getLength(); while (p < end) { if (Descriptor::getDescriptorTag(p) == tag) { - d=createDescriptor(it.i); - break; + d=createDescriptor(it.i, returnUnimplemetedDescriptor); + if (d) + break; } it.i+=Descriptor::getLength(p); p+=Descriptor::getLength(p); } } - if (d && d->getDescriptorTag()==UnimplementedDescriptorTag) - return returnUnimplemetedDescriptor ? d : 0; return d; } -Descriptor *DescriptorLoop::getNext(Iterator &it, DescriptorTag *tags, int arrayLength, bool returnUnimplemetedDescriptor) { +Descriptor *DescriptorLoop::getNext(Iterator &it, DescriptorTag *tags, int arrayLength, bool returnUnimplementedDescriptor) { Descriptor *d=0; if (it.i<getLength()) { const unsigned char *p=data.getData(it.i); @@ -135,27 +134,38 @@ Descriptor *DescriptorLoop::getNext(Iterator &it, DescriptorTag *tags, int array while (p < end) { for (int u=0; u<arrayLength;u++) if (Descriptor::getDescriptorTag(p) == tags[u]) { - d=createDescriptor(it.i); + d=createDescriptor(it.i, returnUnimplementedDescriptor); break; } if (d) - break; + break; //length is added to it.i by createDescriptor, break here it.i+=Descriptor::getLength(p); p+=Descriptor::getLength(p); } } - if (d && d->getDescriptorTag()==UnimplementedDescriptorTag) - return returnUnimplemetedDescriptor ? d : 0; return d; } -Descriptor *DescriptorLoop::createDescriptor(int &i) { - Descriptor *d=Descriptor::getDescriptor(data+i, domain); +Descriptor *DescriptorLoop::createDescriptor(int &i, bool returnUnimplemetedDescriptor) { + Descriptor *d=Descriptor::getDescriptor(data+i, domain, returnUnimplemetedDescriptor); + if (!d) + return 0; i+=d->getLength(); d->CheckParse(); return d; } +int DescriptorLoop::getNumberOfDescriptors() { + const unsigned char *p=data.getData(); + const unsigned char *end=p+getLength(); + int count=0; + while (p < end) { + count++; + p+=Descriptor::getLength(p); + } + return count; +} + DescriptorGroup::DescriptorGroup(bool del) { array=0; length=0; @@ -211,6 +221,16 @@ char *String::getText(char *buffer) { return buffer; } +//taken from VDR, Copyright Klaus Schmidinger <kls@cadsoft.de> +char *String::getText(char *buffer, char *shortVersion) { + if (getLength() < 0 || getLength() >4095) { + strncpy(buffer, "text error", getLength()+1); + return buffer; + } + decodeText(buffer, shortVersion); + return buffer; +} + //taken from libdtv, Copyright Rolf Hakenes <hakenes@hippomi.de> void String::decodeText(char *buffer) { const unsigned char *from=data.getData(0); @@ -228,18 +248,47 @@ void String::decodeText(char *buffer) { if ( ((' ' <= *from) && (*from <= '~')) || (*from == '\n') || (0xA0 <= *from) + || (*from == 0x86 || *from == 0x87) ) *to++ = *from; else if (*from == 0x8A) *to++ = '\n'; - else if (*from == 0x86 || *from == 0x87) //&& !(GDT_NAME_DESCRIPTOR & type)) + from++; + } + *to = '\0'; +} + +void String::decodeText(char *buffer, char *shortVersion) { + const unsigned char *from=data.getData(0); + char *to=buffer; + char *toShort=shortVersion; + int IsShortName=0; + + for (int i = 0; i < getLength(); i++) { + if (*from == 0) + break; + if ( ((' ' <= *from) && (*from <= '~')) + || (*from == '\n') + || (0xA0 <= *from) + ) + { *to++ = *from; + if (IsShortName) + *toShort++ = *from; + } + else if (*from == 0x8A) + *to++ = '\n'; + else if (*from == 0x86) + IsShortName++; + else if (*from == 0x87) + IsShortName--; from++; } *to = '\0'; + *toShort = '\0'; } -Descriptor *Descriptor::getDescriptor(CharArray da, DescriptorTagDomain domain) { +Descriptor *Descriptor::getDescriptor(CharArray da, DescriptorTagDomain domain, bool returnUnimplemetedDescriptor) { Descriptor *d=0; switch (domain) { case SI: @@ -383,6 +432,8 @@ Descriptor *Descriptor::getDescriptor(CharArray da, DescriptorTagDomain domain) case AdaptationFieldDataDescriptorTag: case TransportStreamDescriptorTag: default: + if (!returnUnimplemetedDescriptor) + return 0; d=new UnimplementedDescriptor(); break; } @@ -417,6 +468,8 @@ Descriptor *Descriptor::getDescriptor(CharArray da, DescriptorTagDomain domain) case MHP_DelegatedApplicationDescriptorTag: case MHP_ApplicationStorageDescriptorTag: default: + if (!returnUnimplemetedDescriptor) + return 0; d=new UnimplementedDescriptor(); break; } @@ -6,7 +6,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: si.h 1.8 2004/02/23 17:02:33 kls Exp $ + * $Id: si.h 1.9 2004/03/07 10:09:49 kls Exp $ * * ***************************************************************************/ @@ -252,8 +252,11 @@ protected: //returns a subclass of descriptor according to the data given. //The object is allocated with new and must be delete'd. //setData() will have been called, CheckParse() not. - //Never returns null - maybe the UnimplementedDescriptor. - static Descriptor *getDescriptor(CharArray d, DescriptorTagDomain domain); + //if returnUnimplemetedDescriptor==true: + // Never returns null - maybe the UnimplementedDescriptor. + //if returnUnimplemetedDescriptor==false: + // Never returns the UnimplementedDescriptor - maybe null + static Descriptor *getDescriptor(CharArray d, DescriptorTagDomain domain, bool returnUnimplemetedDescriptor); }; class Loop : public VariableLengthPart { @@ -266,6 +269,7 @@ public: template <class T> friend class StructureLoop; friend class DescriptorLoop; template <class T> friend class TypeLoop; + friend class ExtendedEventDescriptors; int i; }; protected: @@ -311,14 +315,46 @@ public: //returns null if no more descriptors available Descriptor *getNext(Iterator &it); //return the next descriptor with given tag, or 0 if not available. - //if the descriptor found is not implemented, - // an UnimplementedDescriptor will be returned if returnUnimplemetedDescriptor==true, - // 0 will be returned if returnUnimplemetedDescriptor==false + //if returnUnimplemetedDescriptor==true: + // an UnimplementedDescriptor may be returned if the next matching descriptor is unimplemented, + // 0 will be returned if and only if no matching descriptor is found. + //if returnUnimplemetedDescriptor==false: + // if 0 is returned, either no descriptor with the given tag was found, + // or descriptors were found, but the descriptor type is not implemented + //In either case, a return value of 0 indicates that no further calls to this method + //with the iterator shall be made. Descriptor *getNext(Iterator &it, DescriptorTag tag, bool returnUnimplemetedDescriptor=false); //return the next descriptor with one of the given tags, or 0 if not available. + //if returnUnimplemetedDescriptor==true: + // returns 0 if and only if no descriptor with one of the given tags was found. + // The UnimplementedDescriptor may be returned. + //if returnUnimplemetedDescriptor==false: + // if 0 is returned, either no descriptor with one of the given tags was found, + // or descriptors were found, but none of them are implemented. + // The UnimplementedDescriptor will never be returned. + //In either case, a return value of 0 indicates that no further calls to this method + //with the iterator shall be made. Descriptor *getNext(Iterator &it, DescriptorTag *tags, int arrayLength, bool returnUnimplemetedDescriptor=false); + //returns the number of descriptors in this loop + int getNumberOfDescriptors(); + //writes the tags of the descriptors in this loop in the array, + // which must at least have the size getNumberOfDescriptors(). + //The number of descriptors, i.e. getNumberOfDescriptors(), is returned. + // You can specify the array type (Descriptor tags are 8 Bit, + // you might e.g. choose a char, short, int or DescriptorTag array) + template <typename T> int getDescriptorTags(T *tags) + { + const unsigned char *p=data.getData(); + const unsigned char *end=p+getLength(); + int count=0; + while (p < end) { + tags[count++]=(T)Descriptor::getDescriptorTag(p); + p+=Descriptor::getLength(p); + } + return count; + } protected: - Descriptor *createDescriptor(int &i); + Descriptor *createDescriptor(int &i, bool returnUnimplemetedDescriptor); DescriptorTagDomain domain; }; @@ -385,7 +421,7 @@ class String : public VariableLengthPart { public: //A note to the length: getLength() returns the length of the raw data. //The text may be shorter. Its length can be obtained with one of the - //above functions and strlen. + //getText functions and strlen. //returns text. Data is allocated with new and must be delete'd by the user. char *getText(); @@ -394,10 +430,19 @@ public: //In most descriptors the string length is an 8-bit field, //so the maximum there is 256. //returns the given buffer for convenience. - char * getText(char *buffer); + //The emphasis marks 0x86 and 0x87 are still available. + char *getText(char *buffer); + //The same semantics as for getText(char*) apply. + //The short version of the text according to ETSI TR 101 211 (chapter 4.6) + //will be written into the shortVersion buffer (which should, therefore, have the same + //length as buffer). If no shortVersion is available, shortVersion will contain + //an empty string. + //The emphasis marks 0x86 and 0x87 are still available in buffer, but not in shortVersion. + char *getText(char *buffer, char *shortVersion); protected: virtual void Parse() {} void decodeText(char *buffer); + void decodeText(char *buffer, char *shortVersion); }; } //end of namespace @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 1.292 2004/02/22 14:14:55 kls Exp kls $ + * $Id: menu.c 1.297 2004/03/14 13:24:02 kls Exp $ */ #include "menu.h" @@ -18,7 +18,6 @@ #include "cutter.h" #include "eitscan.h" #include "i18n.h" -#include "libsi/si.h" #include "menuitems.h" #include "plugin.h" #include "recording.h" @@ -1009,13 +1008,8 @@ public: cMenuTimers::cMenuTimers(void) :cOsdMenu(tr("Timers"), 2, CHNUMWIDTH, 10, 6, 6) { - int i = 0; - cTimer *timer; - - while ((timer = Timers.Get(i)) != NULL) { - Add(new cMenuTimerItem(timer)); - i++; - } + for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) + Add(new cMenuTimerItem(timer)); if (Setup.SortTimers) Sort(); SetHelp(tr("Edit"), tr("New"), tr("Delete"), Setup.SortTimers ? tr("On/Off") : tr("Mark")); @@ -1216,7 +1210,7 @@ cMenuWhatsOnItem::cMenuWhatsOnItem(const cEvent *Event, cChannel *Channel) int TimerMatch; char t = Timers.GetMatch(Event, &TimerMatch) ? (TimerMatch == tmFull) ? 'T' : 't' : ' '; char v = event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' '; - char r = event->RunningStatus() > SI::RunningStatusNotRunning ? '*' : ' '; + char r = event->IsRunning() ? '*' : ' '; asprintf(&buffer, "%d\t%.*s\t%.*s\t%c%c%c\t%s", channel->Number(), 6, channel->Name(), 5, event->GetTimeString(), t, v, r, event->Title()); SetText(buffer, false); } @@ -1334,7 +1328,7 @@ cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event) int TimerMatch; char t = Timers.GetMatch(Event, &TimerMatch) ? (TimerMatch == tmFull) ? 'T' : 't' : ' '; char v = event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' '; - char r = event->RunningStatus() > SI::RunningStatusNotRunning ? '*' : ' '; + char r = event->IsRunning() ? '*' : ' '; asprintf(&buffer, "%.*s\t%.*s\t%c%c%c\t%s", 5, event->GetDateString(), 5, event->GetTimeString(), t, v, r, event->Title()); SetText(buffer, false); } @@ -1386,11 +1380,9 @@ void cMenuSchedule::PrepareSchedule(cChannel *Channel) const cSchedule *Schedule = schedules->GetSchedule(Channel->GetChannelID()); if (Schedule) { const cEvent *PresentEvent = Schedule->GetPresentEvent(Channel->Number() == cDevice::CurrentChannel()); - int num = Schedule->NumEvents(); time_t now = time(NULL) - Setup.EPGLinger * 60; - for (int a = 0; a < num; a++) { - const cEvent *Event = Schedule->GetEventNumber(a); - if (Event->StartTime() + Event->Duration() > now || Event == PresentEvent) + for (const cEvent *Event = Schedule->Events()->First(); Event; Event = Schedule->Events()->Next(Event)) { + if (Event->EndTime() > now || Event == PresentEvent) Add(new cMenuScheduleItem(Event), Event == PresentEvent); } } @@ -1494,13 +1486,8 @@ cMenuCommands::cMenuCommands(const char *Title, cCommands *Commands, const char SetHasHotkeys(); commands = Commands; parameters = Parameters ? strdup(Parameters) : NULL; - int i = 0; - cCommand *command; - - while ((command = commands->Get(i)) != NULL) { - Add(new cOsdItem(hk(command->Title()))); - i++; - } + for (cCommand *command = commands->First(); command; command = commands->Next(command)) + Add(new cOsdItem(hk(command->Title()))); } cMenuCommands::~cMenuCommands() @@ -3196,6 +3183,7 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause) if (device == cTransferControl::ReceiverDevice()) cControl::Shutdown(); // in case this device was used for Transfer Mode } + dsyslog("switching device %d to channel %d", device->DeviceNumber() + 1, channel->Number()); if (!device->SwitchChannel(channel, false)) { cThread::EmergencyExit(true); return false; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.h 1.60 2004/02/15 14:11:28 kls Exp kls $ + * $Id: menu.h 1.60 2004/02/15 14:11:28 kls Exp $ */ #ifndef __MENU_H diff --git a/menuitems.c b/menuitems.c index e3c99a5..49874f1 100644 --- a/menuitems.c +++ b/menuitems.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menuitems.c 1.14 2004/01/25 15:40:55 kls Exp kls $ + * $Id: menuitems.c 1.15 2004/02/24 12:38:43 kls Exp $ */ #include "menuitems.h" diff --git a/menuitems.h b/menuitems.h index 72a2418..7091740 100644 --- a/menuitems.h +++ b/menuitems.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menuitems.h 1.5 2003/01/12 15:06:23 kls Exp kls $ + * $Id: menuitems.h 1.6 2004/02/24 11:55:14 kls Exp $ */ #ifndef __MENUITEMS_H @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.c 1.43 2003/06/04 16:13:00 kls Exp $ + * $Id: osd.c 1.45 2004/03/14 10:33:20 kls Exp $ */ #include "osd.h" @@ -429,30 +429,27 @@ void cOsdMenu::Display(void) Interface->Help(helpRed, helpGreen, helpYellow, helpBlue); int count = Count(); if (count > 0) { - for (int i = 0; i < count; i++) { - cOsdItem *item = Get(i); - if (item) - cStatus::MsgOsdItem(item->Text(), i); - } + int ni = 0; + for (cOsdItem *item = First(); item; item = Next(item)) + cStatus::MsgOsdItem(item->Text(), ni++); if (current < 0) current = 0; // just for safety - there HAS to be a current item! - int n = 0; - if (current - first >= MAXOSDITEMS) { + if (current - first >= MAXOSDITEMS || current < first) { first = current - MAXOSDITEMS / 2; if (first + MAXOSDITEMS > count) first = count - MAXOSDITEMS; if (first < 0) first = 0; } - for (int i = first; i < count; i++) { - cOsdItem *item = Get(i); - if (item) { - item->Display(i - first, i == current ? clrBlack : clrWhite, i == current ? clrCyan : clrBackground); - if (i == current) - cStatus::MsgOsdCurrentItem(item->Text()); - } + int i = first; + int n = 0; + for (cOsdItem *item = Get(first); item; item = Next(item)) { + item->Display(i - first, i == current ? clrBlack : clrWhite, i == current ? clrCyan : clrBackground); + if (i == current) + cStatus::MsgOsdCurrentItem(item->Text()); if (++n == MAXOSDITEMS) //TODO get this from Interface!!! break; + i++; } } if (!isempty(status)) @@ -562,12 +559,13 @@ void cOsdMenu::PageDown(void) { current += MAXOSDITEMS; first += MAXOSDITEMS; - if (current > Count() - 1) { - current = Count() - 1; - first = max(0, Count() - MAXOSDITEMS); + int count = Count(); + if (current > count - 1) { + current = count - 1; + first = max(0, count - MAXOSDITEMS); } if (SpecialItem(current)) { - current += (current < Count() - 1) ? 1 : -1; + current += (current < count - 1) ? 1 : -1; first = max(first, current - MAXOSDITEMS); } Display(); @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: pat.c 1.7 2004/01/25 15:12:53 kls Exp $ + * $Id: pat.c 1.8 2004/03/07 16:59:00 kls Exp $ */ #include "pat.h" @@ -250,19 +250,21 @@ void cPatFilter::Trigger(void) numPmtEntries = 0; } -bool cPatFilter::PmtVersionChanged(int PmtPid, int Version) +bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version) { - Version <<= 16; + uint64_t v = Version; + v <<= 32; + uint64_t id = (PmtPid | (Sid << 16)) & 0x00000000FFFFFFFFLL; for (int i = 0; i < numPmtEntries; i++) { - if ((pmtVersion[i] & 0x0000FFFF) == PmtPid) { - bool Changed = (pmtVersion[i] & 0x00FF0000) != Version; + if ((pmtVersion[i] & 0x00000000FFFFFFFFLL) == id) { + bool Changed = (pmtVersion[i] & 0x000000FF00000000LL) != v; if (Changed) - pmtVersion[i] = PmtPid | Version; + pmtVersion[i] = id | v; return Changed; } } if (numPmtEntries < MAXPMTENTRIES) - pmtVersion[numPmtEntries++] = PmtPid | Version; + pmtVersion[numPmtEntries++] = id | v; return true; } @@ -301,7 +303,7 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length SI::PMT pmt(Data, false); if (!pmt.CheckCRCAndParse()) return; - if (!PmtVersionChanged(pmtPid, pmt.getVersionNumber())) { + if (!PmtVersionChanged(pmtPid, pmt.getTableIdExtension(), pmt.getVersionNumber())) { lastPmtScan = 0; // this triggers the next scan return; } @@ -4,13 +4,14 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: pat.h 1.3 2004/01/03 13:47:54 kls Exp $ + * $Id: pat.h 1.4 2004/03/07 16:22:01 kls Exp $ */ #ifndef __PAT_H #define __PAT_H #include "filter.h" +#include <stdint.h> #define MAXPMTENTRIES 64 @@ -19,9 +20,9 @@ private: time_t lastPmtScan; int pmtIndex; int pmtPid; - int pmtVersion[MAXPMTENTRIES]; + uint64_t pmtVersion[MAXPMTENTRIES]; int numPmtEntries; - bool PmtVersionChanged(int PmtPid, int Version); + bool PmtVersionChanged(int PmtPid, int Sid, int Version); protected: virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length); public: @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recorder.c 1.8 2003/10/18 11:35:02 kls Exp $ + * $Id: recorder.c 1.9 2004/03/07 14:39:25 kls Exp $ */ #include <stdarg.h> @@ -102,7 +102,7 @@ void cRecorder::Receive(uchar *Data, int Length) { int p = ringBuffer->Put(Data, Length); if (p != Length && active) - esyslog("ERROR: ring buffer overflow (%d bytes dropped)", Length - p); + ringBuffer->ReportOverflow(Length - p); } void cRecorder::Action(void) diff --git a/ringbuffer.c b/ringbuffer.c index c92b75f..6f26748 100644 --- a/ringbuffer.c +++ b/ringbuffer.c @@ -7,7 +7,7 @@ * Parts of this file were inspired by the 'ringbuffy.c' from the * LinuxDVB driver (see linuxtv.org). * - * $Id: ringbuffer.c 1.18 2003/10/12 14:25:09 kls Exp $ + * $Id: ringbuffer.c 1.19 2004/03/07 13:46:51 kls Exp $ */ #include "ringbuffer.h" @@ -17,6 +17,8 @@ // --- cRingBuffer ----------------------------------------------------------- +#define OVERFLOWREPORTDELTA 5 // seconds between reports + cRingBuffer::cRingBuffer(int Size, bool Statistics) { size = Size; @@ -24,6 +26,8 @@ cRingBuffer::cRingBuffer(int Size, bool Statistics) maxFill = 0; lastPercent = 0; putTimeout = getTimeout = 0; + lastOverflowReport = 0; + overflowCount = overflowBytes = 0; } cRingBuffer::~cRingBuffer() @@ -68,6 +72,17 @@ void cRingBuffer::SetTimeouts(int PutTimeout, int GetTimeout) getTimeout = GetTimeout; } +void cRingBuffer::ReportOverflow(int Bytes) +{ + overflowCount++; + overflowBytes += Bytes; + if (time(NULL) - lastOverflowReport > OVERFLOWREPORTDELTA) { + esyslog("ERROR: %d ring buffer overflow%s (%d bytes dropped)", overflowCount, overflowCount > 1 ? "s" : "", overflowBytes); + overflowCount = overflowBytes = 0; + lastOverflowReport = time(NULL); + } +} + // --- cRingBufferLinear ----------------------------------------------------- cRingBufferLinear::cRingBufferLinear(int Size, int Margin, bool Statistics) diff --git a/ringbuffer.h b/ringbuffer.h index 0680df4..349096f 100644 --- a/ringbuffer.h +++ b/ringbuffer.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ringbuffer.h 1.13 2003/10/12 13:57:56 kls Exp $ + * $Id: ringbuffer.h 1.14 2004/03/07 13:40:45 kls Exp $ */ #ifndef __RINGBUFFER_H @@ -21,6 +21,9 @@ private: int putTimeout; int getTimeout; int size; + time_t lastOverflowReport; + int overflowCount; + int overflowBytes; protected: int maxFill;//XXX int lastPercent; @@ -39,6 +42,7 @@ public: cRingBuffer(int Size, bool Statistics = false); virtual ~cRingBuffer(); void SetTimeouts(int PutTimeout, int GetTimeout); + void ReportOverflow(int Bytes); }; class cRingBufferLinear : public cRingBuffer { @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: sdt.c 1.7 2004/01/17 17:27:49 kls Exp $ + * $Id: sdt.c 1.8 2004/03/07 10:46:08 kls Exp $ */ #include "sdt.h" @@ -57,34 +57,17 @@ void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length //XXX TODO case 0x04: // NVOD reference service //XXX TODO case 0x05: // NVOD time-shifted service { - char buffer[1024]; - char *p = sd->serviceName.getText(buffer); char NameBuf[1024]; char ShortNameBuf[1024]; - char *pn = NameBuf; - char *ps = ShortNameBuf; - int IsShortName = 0; - while (*p) { - if ((uchar)*p == 0x86) - IsShortName++; - else if ((uchar)*p == 0x87) - IsShortName--; - else { - *pn++ = *p; - if (IsShortName) - *ps++ = *p; - } - p++; - } - *pn = *ps = 0; - pn = NameBuf; + sd->serviceName.getText(NameBuf, ShortNameBuf); + char *pn = compactspace(NameBuf); + char *ps = compactspace(ShortNameBuf); if (*NameBuf && *ShortNameBuf && strcmp(NameBuf, ShortNameBuf) != 0) { + ps = ShortNameBuf + strlen(ShortNameBuf); *ps++ = ','; strcpy(ps, NameBuf); pn = ShortNameBuf; } - pn = compactspace(pn); - ps = compactspace(ps); if (channel) { channel->SetId(sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId()); if (Setup.UpdateChannels >= 1) @@ -10,7 +10,7 @@ * and interact with the Video Disk Recorder - or write a full featured * graphical interface that sits on top of an SVDRP connection. * - * $Id: svdrp.c 1.60 2004/02/22 15:31:23 kls Exp kls $ + * $Id: svdrp.c 1.61 2004/02/24 12:24:43 kls Exp $ */ #include "svdrp.h" @@ -4,14 +4,13 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.c 1.9 2004/02/13 15:37:49 kls Exp kls $ + * $Id: timers.c 1.12 2004/03/14 13:27:57 kls Exp $ */ #include "timers.h" #include <ctype.h> #include "channels.h" #include "i18n.h" -#include "libsi/si.h" // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d' // format characters in order to allow any number of blanks after a numeric @@ -333,8 +332,8 @@ bool cTimer::Matches(time_t t, bool Directly) if (HasFlags(tfActive)) { if (HasFlags(tfVps) && !Directly && event && event->Vps()) { startTime = event->StartTime(); - stopTime = startTime + event->Duration(); - return event->RunningStatus() > SI::RunningStatusNotRunning; + stopTime = event->EndTime(); + return event->IsRunning(true); } return startTime <= t && t < stopTime; // must stop *before* stopTime to allow adjacent timers } @@ -350,9 +349,14 @@ int cTimer::Matches(const cEvent *Event) bool m1 = Matches(t1, true); bool m2 = UseVps ? m1 : Matches(t2, true); startTime = stopTime = 0; - if (m1 && m2) + if (m1 && m2) { + if (UseVps && Event->IsRunning(true)) + return tmFull; + if (time(NULL) > Event->EndTime()) + return tmNone; return tmFull; - if (m1 || m2) + } + if ((m1 || m2) && time(NULL) <= Event->EndTime()) return tmPartial; } return tmNone; @@ -381,6 +385,8 @@ void cTimer::SetEvent(const cEvent *Event) sprintf(vpsbuf, "(VPS: %s) ", Event->GetVpsString()); isyslog("timer %d (%d %04d-%04d '%s') set to event %s %s-%s %s'%s'", Index() + 1, Channel()->Number(), start, stop, file, Event->GetDateString(), Event->GetTimeString(), Event->GetEndTimeString(), vpsbuf, Event->Title()); } + else + isyslog("timer %d (%d %04d-%04d '%s') set to no event", Index() + 1, Channel()->Number(), start, stop, file); event = Event; } } @@ -500,7 +506,7 @@ cTimer *cTimers::GetNextActiveTimer(void) void cTimers::SetEvents(void) { - cSchedulesLock SchedulesLock; + cSchedulesLock SchedulesLock(false, 100); const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); if (Schedules) { for (cTimer *ti = First(); ti; ti = Next(ti)) { @@ -508,19 +514,17 @@ void cTimers::SetEvents(void) const cEvent *Event = NULL; if (Schedule) { //XXX what if the Schedule doesn't have any VPS??? - const cEvent *e; int Match = tmNone; - int i = 0; - while ((e = Schedule->GetEventNumber(i++)) != NULL) { - int m = ti->Matches(e); - if (m > Match) { - Match = m; - Event = e; - if (Match == tmFull) - break; - //XXX what if there's another event with the same VPS time??? - } - } + for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) { + int m = ti->Matches(e); + if (m > Match) { + Match = m; + Event = e; + if (Match == tmFull) + break; + //XXX what if there's another event with the same VPS time??? + } + } } ti->SetEvent(Event); } @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.h 1.6 2003/12/13 13:04:21 kls Exp kls $ + * $Id: timers.h 1.7 2004/02/29 14:18:17 kls Exp $ */ #ifndef __TIMERS_H @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: transfer.c 1.15 2003/10/18 11:36:03 kls Exp $ + * $Id: transfer.c 1.16 2004/03/07 14:40:15 kls Exp $ */ #include "transfer.h" @@ -55,7 +55,7 @@ void cTransfer::Receive(uchar *Data, int Length) int i = 0; while (active && Length > 0) { if (i++ > 10) { - esyslog("ERROR: ring buffer overflow (%d bytes dropped)", Length); + ringBuffer->ReportOverflow(Length); break; } int p = ringBuffer->Put(Data, Length); @@ -8,7 +8,7 @@ .\" License as specified in the file COPYING that comes with the .\" vdr distribution. .\" -.\" $Id: vdr.5 1.25 2004/02/22 13:18:48 kls Exp kls $ +.\" $Id: vdr.5 1.26 2004/02/24 12:36:35 kls Exp $ .\" .TH vdr 5 "1 Jun 2003" "1.2.0" "Video Disk Recorder Files" .SH NAME @@ -22,7 +22,7 @@ * * The project's page is at http://www.cadsoft.de/vdr * - * $Id: vdr.c 1.177 2004/02/15 14:29:30 kls Exp kls $ + * $Id: vdr.c 1.180 2004/03/14 14:25:02 kls Exp $ */ #include <getopt.h> @@ -549,10 +549,13 @@ int main(int argc, char *argv[]) PreviousChannel[PreviousChannelIndex ^= 1] = LastChannel; // Timers and Recordings: if (!Timers.BeingEdited()) { - static time_t LastSetEvents = 0;//XXX trigger by actual EPG data modification??? - if (!Menu && time(NULL) - LastSetEvents > 5) { - Timers.SetEvents(); - LastSetEvents = time(NULL); + // Assign events to timers: + if (time(NULL) - LastActivity > 10) { + static time_t LastSetEvents = 0;//XXX trigger by actual EPG data modification??? + if (time(NULL) - LastSetEvents > 5) { + Timers.SetEvents(); + LastSetEvents = time(NULL); + } } time_t Now = time(NULL); // must do all following calls with the exact same time! // Process ongoing recordings: @@ -571,7 +574,6 @@ int main(int argc, char *argv[]) if (Timer->HasFlags(tfActive | tfVps) && !Timer->Recording() && !Timer->Pending() && Timer->Matches(Now + Setup.VpsMargin, true)) { if (!Timer->InVpsMargin()) { Timer->SetInVpsMargin(true); - TimerInVpsMargin = true; //XXX if not primary device has TP??? LastTimerChannel = Timer->Channel()->Number(); cRecordControls::Start(Timer); // will only switch the device @@ -579,6 +581,8 @@ int main(int argc, char *argv[]) } else Timer->SetInVpsMargin(false); + if (Timer->InVpsMargin()) + TimerInVpsMargin = true; } } // CAM control: |