From 9fee2b67abb2594c5db2ca9f66525cb09e7b5cc7 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sun, 10 Nov 2002 15:50:21 +0100 Subject: [PATCH] First step towards a 'unique channel ID' --- HISTORY | 45 ++++++++++++++++-- channels.c | 106 +++++++++++++++++++++++++++++-------------- channels.h | 18 +++++--- device.c | 4 +- dvbdevice.c | 6 +-- eit.c | 128 +++++++++++++++++++++++++++++----------------------- eit.h | 31 +++++++------ i18n.c | 18 +++++++- menu.c | 93 +++++++++++++++++++++++--------------- osd.c | 22 +++++---- osd.h | 3 +- remote.h | 4 +- svdrp.c | 43 +++++++++++------- timers.c | 53 ++++++++++------------ timers.h | 5 +- tools.c | 9 +++- tools.h | 4 +- vdr.5 | 30 +++++++++--- 18 files changed, 394 insertions(+), 228 deletions(-) diff --git a/HISTORY b/HISTORY index 2d8c272f..07741159 100644 --- a/HISTORY +++ b/HISTORY @@ -249,7 +249,7 @@ Video Disk Recorder Revision History - 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 + 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. @@ -1722,9 +1722,46 @@ Video Disk Recorder Revision History which can be used to disable simultaneous recording and replaying on the primary DVB device in case there are problems with this. -2002-11-08: Version 1.1.16 +2002-11-10: Version 1.1.16 - Fixed saving the polarization parameter of channels that have a number in the 'source' parameter (thanks to Peter Seyringer for reporting this one). -- Updated channels.conf.terr (thanks to Andy Carter). -- Updated channels.conf.cable (thanks to Achim Lange). +- Updated 'channels.conf.terr' (thanks to Andy Carter). +- Updated 'channels.conf.cable' (thanks to Achim Lange). +- First step towards a "unique channel ID". The channel ID is a human readable + string, made up from several parameters of the channel's definition in the file + 'channels.conf' (see man vdr(5) for details). + In order for the "unique channel ID" to work, all channel definitions now must + be unique with respect to the combination of their Source, Frequency and SID + parameters. You may have to fix your 'channels.conf' manually if there are error + messages in the log file when loading it. BE SURE TO MAKE A BACKUP COPY OF YOUR + 'channels.conf' AND 'timers.conf' FILE BEFORE SWITCHING TO THIS VERSION, AND CHECK + VERY CAREFULLY WHETHER YOUR TIMERS ARE STILL SET TO THE RIGHT CHANNELS! + When reading an existing 'timers.conf', the channels will be identified as before + by their numbers. As soon as this file is written back, the channel numbers will + be replaced by the channel IDs. After that it is possible to manually edit the + 'channels.conf' file and rearrange the channels without breaking the timers. + Note that you can still define new timers manually by using the channel number. + VDR will correctly identify the 'channel' parameter in a timer definition and + use it as a channel number or a channel ID, respectively. Also, the SVDRP commands + that return timer definitions will list them with channel numbers in order to + stay compatible with existing applications. + The channel ID is also used in the 'epg.data' file to allow EPG information from + different sources to be stored, which would previously have been mixed up in case + they were using the same 'service ID'. Note that the contents of an existing + 'epg.data' file from a previous version will be silently ignored, since it doesn't + contain the new channel IDs. When inserting EPG data into VDR via SVDRP you now also + need to use the channel IDs. + Currently the EPG data received from the DVB data stream only uses the 'Source' + and 'Service ID' part of the channel ID. This makes it work for channels with + the same service IDs on different sources (like satellites, cable or terrestrial). + However, it doesn't work yet if the service IDs are not unique within a specific + source. This will be fixed later. +- Added missing SID parameters to 'channels.conf'. Some channels have been removed + since they are apparently no longer broadcasted. +- Removed dropping EPG events from "other" streams that have a duration of 86400 + seconds or more (was introduced in version 1.1.10). This has become obsolete by + the modification in version 1.1.13, which fixed fetching the current/next information + to handle cases where the duration of an event is set wrongly and would last beyond + the start time of the next event. Besides, the change in 1.1.10 broke handling EPG + data for NVOD channels. diff --git a/channels.c b/channels.c index e9fe3de6..cb05e2ca 100644 --- a/channels.c +++ b/channels.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: channels.c 1.7 2002/11/08 13:21:16 kls Exp $ + * $Id: channels.c 1.8 2002/11/10 13:01:55 kls Exp $ */ #include "channels.h" @@ -126,19 +126,18 @@ char *cChannel::buffer = NULL; cChannel::cChannel(void) { - *name = 0; - frequency = 0; + strcpy(name, "Pro7"); + frequency = 12480; source = cSource::FromString("S19.2E"); - srate = 0; - vpid = 0; - apid1 = 0; + srate = 27500; + vpid = 255; + apid1 = 256; apid2 = 0; - dpid1 = 0; + dpid1 = 257; dpid2 = 0; - tpid = 0; + tpid = 32; ca = 0; sid = 0; - number = 0; groupSep = false; polarization = 'v'; inversion = INVERSION_AUTO; @@ -151,30 +150,44 @@ cChannel::cChannel(void) hierarchy = HIERARCHY_AUTO; } -cChannel::cChannel(const cChannel *Channel) +cChannel& cChannel::operator= (const cChannel &Channel) { - strcpy(name, Channel ? Channel->name : "Pro7"); - frequency = Channel ? Channel->frequency : 12480; - source = Channel ? Channel->source : cSource::FromString("S19.2E"); - srate = Channel ? Channel->srate : 27500; - vpid = Channel ? Channel->vpid : 255; - apid1 = Channel ? Channel->apid1 : 256; - apid2 = Channel ? Channel->apid2 : 0; - dpid1 = Channel ? Channel->dpid1 : 257; - dpid2 = Channel ? Channel->dpid2 : 0; - tpid = Channel ? Channel->tpid : 32; - ca = Channel ? Channel->ca : 0; - sid = Channel ? Channel->sid : 0; - groupSep = Channel ? Channel->groupSep : false; - polarization = Channel ? Channel->polarization : 'v'; - inversion = Channel ? Channel->inversion : INVERSION_AUTO; - bandwidth = Channel ? Channel->bandwidth : BANDWIDTH_AUTO; - coderateH = Channel ? Channel->coderateH : FEC_AUTO; - coderateL = Channel ? Channel->coderateL : FEC_AUTO; - modulation = Channel ? Channel->modulation : QAM_AUTO; - transmission = Channel ? Channel->transmission : TRANSMISSION_MODE_AUTO; - guard = Channel ? Channel->guard : GUARD_INTERVAL_AUTO; - hierarchy = Channel ? Channel->hierarchy : HIERARCHY_AUTO; + memcpy(&__BeginData__, &Channel.__BeginData__, (char *)&Channel.__EndData__ - (char *)&Channel.__BeginData__); + return *this; +} + +static int MHz(int frequency) +{ + while (frequency > 20000) { + frequency /= 1000; + } + return frequency; +} + +uint64 cChannel::GetChannelID(void) const +{ + return (uint64(source) << 48) | (uint64(0) << 32) | ((MHz(frequency)) << 16) | sid; +} + +const char *cChannel::GetChannelIDStr(void) const +{ + static char buffer[256]; + snprintf(buffer, sizeof(buffer), "%s-%d-%d-%d", cSource::ToString(source), 0, MHz(frequency), sid); + return buffer; +} + +uint64 cChannel::StringToChannelID(const char *s) +{ + char *sourcebuf = NULL; + int reserved; + int frequency; + int sid; + if (4 == sscanf(s, "%a[^-]-%d-%d-%d", &sourcebuf, &reserved, &frequency, &sid)) { + int source = cSource::FromString(sourcebuf); + if (source >= 0) + return (uint64(source) << 48) | (uint64(reserved) << 32) | (frequency << 16) | sid; + } + return 0; } static int PrintParameter(char *p, char Name, int Value) @@ -278,7 +291,7 @@ const char *cChannel::ToText(void) return ToText(this); } -bool cChannel::Parse(const char *s) +bool cChannel::Parse(const char *s, bool AllowNonUniqueID) { if (*s == ':') { groupSep = true; @@ -324,6 +337,10 @@ bool cChannel::Parse(const char *s) free(sourcebuf); free(apidbuf); free(namebuf); + if (!AllowNonUniqueID && Channels.GetByChannelID(GetChannelID())) { + esyslog("ERROR: channel data not unique!"); + return false; + } return ok; } else @@ -404,15 +421,34 @@ cChannel *cChannels::GetByNumber(int Number, int SkipGap) return NULL; } -cChannel *cChannels::GetByServiceID(unsigned short ServiceId) +cChannel *cChannels::GetByServiceID(int Source, unsigned short ServiceID) { for (cChannel *channel = First(); channel; channel = Next(channel)) { - if (!channel->GroupSep() && channel->Sid() == ServiceId) + if (!channel->GroupSep() && channel->Source() == Source && channel->Sid() == ServiceID) return channel; } return NULL; } +cChannel *cChannels::GetByChannelID(uint64 ChannelID) +{ + for (cChannel *channel = First(); channel; channel = Next(channel)) { + if (!channel->GroupSep() && channel->GetChannelID() == ChannelID) + return channel; + } + return NULL; +} + +bool cChannels::HasUniqueChannelID(cChannel *NewChannel, cChannel *OldChannel) +{ + uint64 NewChannelID = NewChannel->GetChannelID(); + for (cChannel *channel = First(); channel; channel = Next(channel)) { + if (!channel->GroupSep() && channel != OldChannel && channel->GetChannelID() == NewChannelID) + return false; + } + return true; +} + bool cChannels::SwitchTo(int Number) { cChannel *channel = GetByNumber(Number); diff --git a/channels.h b/channels.h index 7eb818c3..52e79b4d 100644 --- a/channels.h +++ b/channels.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: channels.h 1.3 2002/10/20 11:50:36 kls Exp $ + * $Id: channels.h 1.4 2002/11/10 13:01:23 kls Exp $ */ #ifndef __CHANNELS_H @@ -41,6 +41,7 @@ private: static char *buffer; static const char *ToText(cChannel *Channel); enum { MaxChannelName = 32 }; // 31 chars + terminating 0! + int __BeginData__; char name[MaxChannelName]; int frequency; // MHz int source; @@ -53,7 +54,6 @@ private: int sid; int number; // Sequence number assigned on load bool groupSep; - //XXX char polarization; int inversion; int bandwidth; @@ -63,13 +63,14 @@ private: int transmission; int guard; int hierarchy; + int __EndData__; const char *ParametersToString(void); bool StringToParameters(const char *s); public: cChannel(void); - cChannel(const cChannel *Channel); + cChannel& operator= (const cChannel &Channel); const char *ToText(void); - bool Parse(const char *s); + bool Parse(const char *s, bool AllowNonUniqueID = false); bool Save(FILE *f); const char *Name(void) const { return name; } int Frequency(void) const { return frequency; } @@ -86,7 +87,6 @@ public: int Number(void) const { return number; } void SetNumber(int Number) { number = Number; } bool GroupSep(void) const { return groupSep; } - //XXX char Polarization(void) const { return polarization; } int Inversion(void) const { return inversion; } int Bandwidth(void) const { return bandwidth; } @@ -96,10 +96,12 @@ public: int Transmission(void) const { return transmission; } int Guard(void) const { return guard; } int Hierarchy(void) const { return hierarchy; } - //XXX bool IsCable(void) { return (source & cSource::st_Mask) == cSource::stCable; } bool IsSat(void) { return (source & cSource::st_Mask) == cSource::stSat; } bool IsTerr(void) { return (source & cSource::st_Mask) == cSource::stTerr; } + uint64 GetChannelID(void) const; + const char *GetChannelIDStr(void) const; + static uint64 StringToChannelID(const char *s); }; class cChannels : public cConfig { @@ -113,7 +115,9 @@ public: int GetNextNormal(int Idx); // Get next normal channel (not group) void ReNumber(void); // Recalculate 'number' based on channel type cChannel *GetByNumber(int Number, int SkipGap = 0); - cChannel *GetByServiceID(unsigned short ServiceId); + cChannel *GetByServiceID(int Source, unsigned short ServiceID); + cChannel *GetByChannelID(uint64 ChannelID); + bool HasUniqueChannelID(cChannel *NewChannel, cChannel *OldChannel = NULL); bool SwitchTo(int Number); int MaxNumber(void) { return maxNumber; } }; diff --git a/device.c b/device.c index 69178a9e..edbf5cae 100644 --- a/device.c +++ b/device.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.c 1.34 2002/11/03 11:51:24 kls Exp $ + * $Id: device.c 1.35 2002/11/10 10:17:57 kls Exp $ */ #include "device.h" @@ -379,7 +379,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) if (Result == scrOk) { if (LiveView && IsPrimaryDevice()) { - cSIProcessor::SetCurrentServiceID(Channel->Sid()); + cSIProcessor::SetCurrentChannelID(Channel->GetChannelID()); currentChannel = Channel->Number(); } cStatus::MsgChannelSwitch(this, Channel->Number()); // only report status if channel switch successfull diff --git a/dvbdevice.c b/dvbdevice.c index 30d56586..119a28e6 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.34 2002/11/03 12:31:11 kls Exp $ + * $Id: dvbdevice.c 1.35 2002/11/10 12:57:17 kls Exp $ */ #include "dvbdevice.h" @@ -412,7 +412,7 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) // Stop setting system time: if (siProcessor) - siProcessor->SetCurrentTransponder(0); + siProcessor->SetCurrentTransponder(0, 0); // Turn off live PIDs if necessary: @@ -585,7 +585,7 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) // Start setting system time: if (siProcessor) - siProcessor->SetCurrentTransponder(Channel->Frequency()); + siProcessor->SetCurrentTransponder(Channel->Source(), Channel->Frequency()); return true; } diff --git a/eit.c b/eit.c index 12045d63..f8b436c0 100644 --- a/eit.c +++ b/eit.c @@ -16,7 +16,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: eit.c 1.59 2002/11/02 12:46:53 kls Exp $ + * $Id: eit.c 1.60 2002/11/10 15:50:21 kls Exp $ ***************************************************************************/ #include "eit.h" @@ -180,7 +180,7 @@ bool cTDT::SetSystemTime() // --- cEventInfo ------------------------------------------------------------ -cEventInfo::cEventInfo(unsigned short serviceid, unsigned short eventid) +cEventInfo::cEventInfo(uint64 channelid, unsigned short eventid) { pTitle = NULL; pSubtitle = NULL; @@ -190,7 +190,7 @@ cEventInfo::cEventInfo(unsigned short serviceid, unsigned short eventid) tTime = 0; uTableID = 0; uEventID = eventid; - uServiceID = serviceid; + uChannelID = channelid; nChannelNumber = 0; } @@ -325,15 +325,15 @@ void cEventInfo::SetEventID(unsigned short evid) uEventID = evid; } /** */ -void cEventInfo::SetServiceID(unsigned short servid) +void cEventInfo::SetChannelID(uint64 channelid) { - uServiceID = servid; + uChannelID = channelid; } /** */ -unsigned short cEventInfo::GetServiceID() const +uint64 cEventInfo::GetChannelID() const { - return uServiceID; + return uChannelID; } /** */ @@ -368,7 +368,7 @@ bool cEventInfo::Read(FILE *f, cSchedule *Schedule) if (n == 3 || n == 4) { pEvent = (cEventInfo *)Schedule->GetEvent(uEventID, tTime); if (!pEvent) - pEvent = Schedule->AddEvent(new cEventInfo(Schedule->GetServiceID(), uEventID)); + pEvent = Schedule->AddEvent(new cEventInfo(Schedule->GetChannelID(), uEventID)); if (pEvent) { pEvent->SetTableID(uTableID); pEvent->SetTime(tTime); @@ -404,24 +404,24 @@ bool cEventInfo::Read(FILE *f, cSchedule *Schedule) struct tEpgBugFixStats { int hits; int n; - unsigned short serviceIDs[MAXEPGBUGFIXCHANS]; + uint64 channelIDs[MAXEPGBUGFIXCHANS]; tEpgBugFixStats(void) { hits = n = 0; } }; tEpgBugFixStats EpgBugFixStats[MAXEPGBUGFIXSTATS]; -static void EpgBugFixStat(int Number, unsigned int ServiceID) +static void EpgBugFixStat(int Number, uint64 ChannelID) { if (0 <= Number && Number < MAXEPGBUGFIXSTATS) { tEpgBugFixStats *p = &EpgBugFixStats[Number]; p->hits++; int i = 0; for (; i < p->n; i++) { - if (p->serviceIDs[i] == ServiceID) + if (p->channelIDs[i] == ChannelID) break; } if (i == p->n && p->n < MAXEPGBUGFIXCHANS) - p->serviceIDs[p->n++] = ServiceID; + p->channelIDs[p->n++] = ChannelID; } } @@ -448,7 +448,7 @@ static void ReportEpgBugFixStats(bool Reset = false) char *q = buffer; q += snprintf(q, sizeof(buffer) - (q - buffer), "%d\t%d", i, p->hits); for (int c = 0; c < p->n; c++) { - cChannel *channel = Channels.GetByServiceID(p->serviceIDs[c]); + cChannel *channel = Channels.GetByChannelID(p->channelIDs[c]); if (channel) { q += snprintf(q, sizeof(buffer) - (q - buffer), "%s%s", delim, channel->Name()); delim = ", "; @@ -499,7 +499,7 @@ void cEventInfo::FixEpgBugs(void) char *s = e ? strdup(e) : NULL; free(pSubtitle); pSubtitle = s; - EpgBugFixStat(0, GetServiceID()); + EpgBugFixStat(0, GetChannelID()); // now the fixes #1 and #2 below will handle the rest } } @@ -524,7 +524,7 @@ void cEventInfo::FixEpgBugs(void) free(pExtendedDescription); pSubtitle = s; pExtendedDescription = d; - EpgBugFixStat(1, GetServiceID()); + EpgBugFixStat(1, GetChannelID()); } } } @@ -541,7 +541,7 @@ void cEventInfo::FixEpgBugs(void) memmove(pSubtitle, pSubtitle + 1, strlen(pSubtitle)); pExtendedDescription = pSubtitle; pSubtitle = NULL; - EpgBugFixStat(2, GetServiceID()); + EpgBugFixStat(2, GetChannelID()); } } @@ -553,7 +553,7 @@ void cEventInfo::FixEpgBugs(void) if (pSubtitle && strcmp(pTitle, pSubtitle) == 0) { free(pSubtitle); pSubtitle = NULL; - EpgBugFixStat(3, GetServiceID()); + EpgBugFixStat(3, GetChannelID()); } // ZDF.info puts the Subtitle between double quotes, which is nothing @@ -569,7 +569,7 @@ void cEventInfo::FixEpgBugs(void) char *p = strrchr(pSubtitle, '"'); if (p) *p = 0; - EpgBugFixStat(4, GetServiceID()); + EpgBugFixStat(4, GetChannelID()); } } @@ -590,7 +590,7 @@ void cEventInfo::FixEpgBugs(void) if (*p == '-' && *(p + 1) == ' ' && *(p + 2) && islower(*(p - 1)) && islower(*(p + 2))) { if (!startswith(p + 2, "und ")) { // special case in German, as in "Lach- und Sachgeschichten" memmove(p, p + 2, strlen(p + 2) + 1); - EpgBugFixStat(5, GetServiceID()); + EpgBugFixStat(5, GetChannelID()); } } p++; @@ -608,10 +608,10 @@ void cEventInfo::FixEpgBugs(void) // --- cSchedule ------------------------------------------------------------- -cSchedule::cSchedule(unsigned short servid) +cSchedule::cSchedule(uint64 channelid) { pPresent = pFollowing = NULL; - uServiceID = servid; + uChannelID = channelid; } @@ -645,14 +645,14 @@ const cEventInfo *cSchedule::GetFollowingEvent(void) const return pe; } -void cSchedule::SetServiceID(unsigned short servid) +void cSchedule::SetChannelID(uint64 channelid) { - uServiceID = servid; + uChannelID = channelid; } /** */ -unsigned short cSchedule::GetServiceID() const +uint64 cSchedule::GetChannelID() const { - return uServiceID; + return uChannelID; } /** */ const cEventInfo * cSchedule::GetEvent(unsigned short uEventID, time_t tTime) const @@ -735,10 +735,10 @@ void cSchedule::Cleanup(time_t tTime) /** */ void cSchedule::Dump(FILE *f, const char *Prefix) const { - cChannel *channel = Channels.GetByServiceID(uServiceID); + cChannel *channel = Channels.GetByChannelID(uChannelID); if (channel) { - fprintf(f, "%sC %u %s\n", Prefix, uServiceID, channel->Name()); + fprintf(f, "%sC %s %s\n", Prefix, channel->GetChannelIDStr(), channel->Name()); for (cEventInfo *p = Events.First(); p; p = Events.Next(p)) p->Dump(f, Prefix); fprintf(f, "%sc\n", Prefix); @@ -751,12 +751,22 @@ bool cSchedule::Read(FILE *f, cSchedules *Schedules) char *s; while ((s = readline(f)) != NULL) { if (*s == 'C') { - unsigned int uServiceID; - if (1 == sscanf(s + 1, "%u", &uServiceID)) { - cSchedule *p = (cSchedule *)Schedules->AddServiceID(uServiceID); - if (p) { - if (!cEventInfo::Read(f, p)) - return false; + s = skipspace(s + 1); + char *p = strchr(s, ' '); + if (p) + *p = 0; // strips optional channel name + if (*s) { + uint64 uChannelID = cChannel::StringToChannelID(s); + if (uChannelID) { + cSchedule *p = (cSchedule *)Schedules->AddChannelID(uChannelID); + if (p) { + if (!cEventInfo::Read(f, p)) + return false; + } + } + else { + esyslog("ERROR: illegal channel ID: %s", s); + return false; } } } @@ -775,28 +785,28 @@ bool cSchedule::Read(FILE *f, cSchedules *Schedules) cSchedules::cSchedules() { pCurrentSchedule = NULL; - uCurrentServiceID = 0; + uCurrentChannelID = 0; } cSchedules::~cSchedules() { } /** */ -const cSchedule *cSchedules::AddServiceID(unsigned short servid) +const cSchedule *cSchedules::AddChannelID(uint64 channelid) { - const cSchedule *p = GetSchedule(servid); + const cSchedule *p = GetSchedule(channelid); if (!p) { - Add(new cSchedule(servid)); - p = GetSchedule(servid); + Add(new cSchedule(channelid)); + p = GetSchedule(channelid); } return p; } /** */ -const cSchedule *cSchedules::SetCurrentServiceID(unsigned short servid) +const cSchedule *cSchedules::SetCurrentChannelID(uint64 channelid) { - pCurrentSchedule = AddServiceID(servid); + pCurrentSchedule = AddChannelID(channelid); if (pCurrentSchedule) - uCurrentServiceID = servid; + uCurrentChannelID = channelid; return pCurrentSchedule; } /** */ @@ -805,14 +815,14 @@ const cSchedule * cSchedules::GetSchedule() const return pCurrentSchedule; } /** */ -const cSchedule * cSchedules::GetSchedule(unsigned short servid) const +const cSchedule * cSchedules::GetSchedule(uint64 channelid) const { cSchedule *p; p = First(); while (p != NULL) { - if (p->GetServiceID() == servid) + if (p->GetChannelID() == channelid) return p; p = Next(p); } @@ -856,7 +866,7 @@ public: cEIT(unsigned char *buf, int length, cSchedules *Schedules); ~cEIT(); /** */ - int ProcessEIT(unsigned char *buffer); + int ProcessEIT(unsigned char *buffer, int CurrentSource); protected: // Protected methods /** returns true if this EIT covers a @@ -879,7 +889,7 @@ cEIT::~cEIT() } /** */ -int cEIT::ProcessEIT(unsigned char *buffer) +int cEIT::ProcessEIT(unsigned char *buffer, int CurrentSource) { cEventInfo *pEvent, *rEvent = NULL; cSchedule *pSchedule, *rSchedule = NULL; @@ -893,18 +903,19 @@ int cEIT::ProcessEIT(unsigned char *buffer) if (VdrProgramInfos) { for (VdrProgramInfo = (struct VdrProgramInfo *) VdrProgramInfos->Head; VdrProgramInfo; VdrProgramInfo = (struct VdrProgramInfo *) xSucc (VdrProgramInfo)) { - // Drop events that belong to an "other TS" and are very long (some stations broadcast bogus data for "other" channels): - if (VdrProgramInfo->Duration >= 86400 && (tid == 0x4F || tid == 0x60 || tid == 0x61)) - continue; - pSchedule = (cSchedule *)schedules->GetSchedule(VdrProgramInfo->ServiceID); + //XXX TODO use complete channel ID + cChannel *channel = Channels.GetByServiceID(CurrentSource, VdrProgramInfo->ServiceID); + uint64 channelID = channel ? channel->GetChannelID() : (uint64(CurrentSource) << 48) | VdrProgramInfo->ServiceID; + //XXX + pSchedule = (cSchedule *)schedules->GetSchedule(channelID); if (!pSchedule) { - schedules->Add(new cSchedule(VdrProgramInfo->ServiceID)); - pSchedule = (cSchedule *)schedules->GetSchedule(VdrProgramInfo->ServiceID); + schedules->Add(new cSchedule(channelID)); + pSchedule = (cSchedule *)schedules->GetSchedule(channelID); if (!pSchedule) break; } if (VdrProgramInfo->ReferenceServiceID) { - rSchedule = (cSchedule *)schedules->GetSchedule(VdrProgramInfo->ReferenceServiceID); + rSchedule = (cSchedule *)schedules->GetSchedule((uint64(CurrentSource) << 48) | VdrProgramInfo->ReferenceServiceID); if (!rSchedule) break; rEvent = (cEventInfo *)rSchedule->GetEvent((unsigned short)VdrProgramInfo->ReferenceEventID); @@ -915,7 +926,7 @@ int cEIT::ProcessEIT(unsigned char *buffer) if (!pEvent) { // If we don't have that event ID yet, we create a new one. // Otherwise we copy the information into the existing event anyway, because the data might have changed. - pEvent = pSchedule->AddEvent(new cEventInfo(VdrProgramInfo->ServiceID, VdrProgramInfo->EventID)); + pEvent = pSchedule->AddEvent(new cEventInfo(channelID, VdrProgramInfo->EventID)); if (!pEvent) break; pEvent->SetTableID(tid); @@ -988,6 +999,8 @@ cSIProcessor::cSIProcessor(const char *FileName) { fileName = strdup(FileName); masterSIProcessor = numSIProcessors == 0; // the first one becomes the 'master' + currentSource = 0; + currentTransponder = 0; filters = NULL; if (!numSIProcessors++) // the first one creates it schedules = new cSchedules; @@ -1167,7 +1180,7 @@ void cSIProcessor::Action() { cMutexLock MutexLock(&schedulesMutex); cEIT ceit(buf, seclen, schedules); - ceit.ProcessEIT(buf); + ceit.ProcessEIT(buf, currentSource); } else dsyslog("Received stuffing section in EIT\n"); @@ -1250,16 +1263,17 @@ bool cSIProcessor::ShutDownFilters(void) } /** */ -void cSIProcessor::SetCurrentTransponder(int CurrentTransponder) +void cSIProcessor::SetCurrentTransponder(int CurrentSource, int CurrentTransponder) { + currentSource = CurrentSource; currentTransponder = CurrentTransponder; } /** */ -bool cSIProcessor::SetCurrentServiceID(unsigned short servid) +bool cSIProcessor::SetCurrentChannelID(uint64 channelid) { cMutexLock MutexLock(&schedulesMutex); - return schedules ? schedules->SetCurrentServiceID(servid) : false; + return schedules ? schedules->SetCurrentChannelID(channelid) : false; } void cSIProcessor::TriggerDump(void) diff --git a/eit.h b/eit.h index 53740032..d906725e 100644 --- a/eit.h +++ b/eit.h @@ -16,7 +16,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: eit.h 1.20 2002/11/02 12:36:36 kls Exp $ + * $Id: eit.h 1.21 2002/11/10 12:58:27 kls Exp $ ***************************************************************************/ #ifndef __EIT_H @@ -32,7 +32,7 @@ class cEventInfo : public cListObject { friend class cEIT; private: unsigned char uTableID; // Table ID this event came from - unsigned short uServiceID; // Service ID of program for that event + uint64 uChannelID; // Channel 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 @@ -47,13 +47,13 @@ protected: void SetFollowing(bool foll); void SetPresent(bool pres); void SetTitle(const char *string); - void SetServiceID(unsigned short servid); + void SetChannelID(uint64 channelid); void SetEventID(unsigned short evid); void SetDuration(long l); void SetTime(time_t t); void SetExtendedDescription(const char *string); void SetSubtitle(const char *string); - cEventInfo(unsigned short serviceid, unsigned short eventid); + cEventInfo(uint64 channelid, unsigned short eventid); public: ~cEventInfo(); const unsigned char GetTableID(void) const; @@ -68,7 +68,7 @@ public: unsigned short GetEventID(void) const; long GetDuration(void) const; time_t GetTime(void) const; - unsigned short GetServiceID(void) const; + uint64 GetChannelID(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' void Dump(FILE *f, const char *Prefix = "") const; @@ -82,21 +82,21 @@ class cSchedule : public cListObject { private: cEventInfo *pPresent; cEventInfo *pFollowing; - unsigned short uServiceID; + uint64 uChannelID; cList Events; protected: - void SetServiceID(unsigned short servid); + void SetChannelID(uint64 channelid); bool SetFollowingEvent(cEventInfo *pEvent); bool SetPresentEvent(cEventInfo *pEvent); void Cleanup(time_t tTime); void Cleanup(void); - cSchedule(unsigned short servid = 0); + cSchedule(uint64 channelid = 0); public: ~cSchedule(); cEventInfo *AddEvent(cEventInfo *EventInfo); const cEventInfo *GetPresentEvent(void) const; const cEventInfo *GetFollowingEvent(void) const; - unsigned short GetServiceID(void) const; + uint64 GetChannelID(void) const; const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const; const cEventInfo *GetEventAround(time_t tTime) const; const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); } @@ -110,15 +110,15 @@ class cSchedules : public cList { friend class cSIProcessor; private: const cSchedule *pCurrentSchedule; - unsigned short uCurrentServiceID; + uint64 uCurrentChannelID; protected: - const cSchedule *AddServiceID(unsigned short servid); - const cSchedule *SetCurrentServiceID(unsigned short servid); + const cSchedule *AddChannelID(uint64 channelid); + const cSchedule *SetCurrentChannelID(uint64 channelid); void Cleanup(); public: cSchedules(void); ~cSchedules(); - const cSchedule *GetSchedule(unsigned short servid) const; + const cSchedule *GetSchedule(uint64 channelid) const; const cSchedule *GetSchedule(void) const; void Dump(FILE *f, const char *Prefix = "") const; static bool Read(FILE *f); @@ -141,6 +141,7 @@ private: static const char *epgDataFileName; static time_t lastDump; bool masterSIProcessor; + int currentSource; int currentTransponder; SIP_FILTER *filters; char *fileName; @@ -160,8 +161,8 @@ public: static bool Read(FILE *f = NULL); static void Clear(void); void SetStatus(bool On); - void SetCurrentTransponder(int CurrentTransponder); - static bool SetCurrentServiceID(unsigned short servid); + void SetCurrentTransponder(int CurrentSource, int CurrentTransponder); + static bool SetCurrentChannelID(uint64 channelid); static void TriggerDump(void); }; diff --git a/i18n.c b/i18n.c index 08b27650..79fa6079 100644 --- a/i18n.c +++ b/i18n.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: i18n.c 1.99 2002/10/27 14:24:00 kls Exp $ + * $Id: i18n.c 1.100 2002/11/10 12:32:30 kls Exp $ * * Translations provided by: * @@ -1468,6 +1468,22 @@ const tI18nPhrase Phrases[] = { "",//TODO "",//TODO }, + { "Channel settings are not unique!", + "Kanaleinstellungen sind nicht eindeutig!", + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + }, { "Channel locked (recording)!", "Kanal blockiert (zeichnet auf)!", "Zaklenjen kanal (snemanje)!", diff --git a/menu.c b/menu.c index 47ad091b..6b773a53 100644 --- a/menu.c +++ b/menu.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 1.222 2002/11/01 12:15:45 kls Exp $ + * $Id: menu.c 1.223 2002/11/10 12:32:42 kls Exp $ */ #include "menu.h" @@ -546,16 +546,18 @@ private: cChannel data; void Setup(void); public: - cMenuEditChannel(int Index); + cMenuEditChannel(cChannel *Channel, bool New = false); virtual eOSState ProcessKey(eKeys Key); }; -cMenuEditChannel::cMenuEditChannel(int Index) +cMenuEditChannel::cMenuEditChannel(cChannel *Channel, bool New) :cOsdMenu(tr("Edit channel"), 14) { - channel = Channels.Get(Index); + channel = Channel; if (channel) { data = *channel; + if (New) + channel = NULL; Setup(); } } @@ -603,10 +605,26 @@ eOSState cMenuEditChannel::ProcessKey(eKeys Key) if (state == osUnknown) { if (Key == kOk) { - if (channel) - *channel = data; - Channels.Save(); - state = osBack; + if (Channels.HasUniqueChannelID(&data, channel)) { + if (channel) { + *channel = data; + isyslog("edited channel %d %s", channel->Number(), data.ToText()); + state = osBack; + } + else { + channel = new cChannel; + *channel = data; + Channels.Add(channel); + Channels.ReNumber(); + isyslog("added channel %d %s", channel->Number(), data.ToText()); + state = osUser1; + } + Channels.Save(); + } + else { + Interface->Error(tr("Channel settings are not unique!")); + state = osContinue; + } } } if (Key != kNone && (data.source & cSource::st_Mask) != (oldSource & cSource::st_Mask)) @@ -696,10 +714,8 @@ eOSState cMenuChannels::Edit(void) if (HasSubMenu() || Count() == 0) return osContinue; cChannel *ch = Channels.Get(Current()); - if (ch) { - isyslog("editing channel %d", ch->Number()); - return AddSubMenu(new cMenuEditChannel(Current())); - } + if (ch) + return AddSubMenu(new cMenuEditChannel(ch)); return osContinue; } @@ -707,13 +723,7 @@ eOSState cMenuChannels::New(void) { if (HasSubMenu()) return osContinue; - cChannel *channel = new cChannel(Channels.Get(Current())); - Channels.Add(channel); - Channels.ReNumber(); - Add(new cMenuChannelItem(channel), true); - Channels.Save(); - isyslog("channel %d added", channel->Number()); - return AddSubMenu(new cMenuEditChannel(Current())); + return AddSubMenu(new cMenuEditChannel(Channels.Get(Current()), true)); } eOSState cMenuChannels::Del(void) @@ -753,16 +763,27 @@ eOSState cMenuChannels::ProcessKey(eKeys Key) { eOSState state = cOsdMenu::ProcessKey(Key); - if (state == osUnknown) { - switch (Key) { - case kOk: return Switch(); - case kRed: return Edit(); - case kGreen: return New(); - case kYellow: return Del(); - case kBlue: Mark(); break; - default: break; - } - } + switch (state) { + case osUser1: { + cChannel *channel = Channels.Last(); + if (channel) { + Add(new cMenuChannelItem(channel), true); + return CloseSubMenu(); + } + } + break; + default: + if (state == osUnknown) { + switch (Key) { + case kOk: return Switch(); + case kRed: return Edit(); + case kGreen: return New(); + case kYellow: return Del(); + case kBlue: Mark(); break; + default: break; + } + } + } return state; } @@ -1068,7 +1089,7 @@ cMenuEvent::cMenuEvent(const cEventInfo *EventInfo, bool CanSwitch) { eventInfo = EventInfo; if (eventInfo) { - cChannel *channel = Channels.GetByServiceID(eventInfo->GetServiceID()); + cChannel *channel = Channels.GetByChannelID(eventInfo->GetChannelID()); if (channel) { char *buffer; asprintf(&buffer, "%-17.*s\t%.*s %s - %s", 17, channel->Name(), 5, eventInfo->GetDate(), eventInfo->GetTimeString(), eventInfo->GetEndTimeString()); @@ -1162,7 +1183,7 @@ cMenuWhatsOn::cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentCha pArray[num] = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent(); if (pArray[num]) { - cChannel *channel = Channels.GetByServiceID(pArray[num]->GetServiceID()); + cChannel *channel = Channels.GetByChannelID(pArray[num]->GetChannelID()); if (channel) { pArray[num]->SetChannelNumber(channel->Number()); num++; @@ -1192,7 +1213,7 @@ eOSState cMenuWhatsOn::Switch(void) { cMenuWhatsOnItem *item = (cMenuWhatsOnItem *)Get(Current()); if (item) { - cChannel *channel = Channels.GetByServiceID(item->eventInfo->GetServiceID()); + cChannel *channel = Channels.GetByChannelID(item->eventInfo->GetChannelID()); if (channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true)) return osEnd; } @@ -1313,7 +1334,7 @@ void cMenuSchedule::PrepareSchedule(cChannel *Channel) SetTitle(buffer); free(buffer); if (schedules) { - const cSchedule *Schedule = Channel->Sid() ? schedules->GetSchedule(Channel->Sid()) : schedules->GetSchedule(); + const cSchedule *Schedule = schedules->GetSchedule(Channel->GetChannelID()); int num = Schedule->NumEvents(); const cEventInfo **pArray = MALLOC(const cEventInfo *, num); if (pArray) { @@ -1376,7 +1397,7 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key) if (!now && !next) { int ChannelNr = 0; if (Count()) { - cChannel *channel = Channels.GetByServiceID(((cMenuScheduleItem *)Get(Current()))->eventInfo->GetServiceID()); + cChannel *channel = Channels.GetByChannelID(((cMenuScheduleItem *)Get(Current()))->eventInfo->GetChannelID()); if (channel) ChannelNr = channel->Number(); } @@ -1403,7 +1424,7 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key) now = next = false; const cEventInfo *ei = cMenuWhatsOn::ScheduleEventInfo(); if (ei) { - cChannel *channel = Channels.GetByServiceID(ei->GetServiceID()); + cChannel *channel = Channels.GetByChannelID(ei->GetChannelID()); if (channel) { PrepareSchedule(channel); if (channel->Number() != cDevice::CurrentChannel()) { @@ -2713,7 +2734,7 @@ bool cRecordControl::GetEventInfo(void) cMutexLock MutexLock; const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock); if (Schedules) { - const cSchedule *Schedule = Schedules->GetSchedule(channel->Sid()); + const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID()); if (Schedule) { eventInfo = Schedule->GetEventAround(Time); if (eventInfo) { diff --git a/osd.c b/osd.c index acd9aec4..8324874c 100644 --- a/osd.c +++ b/osd.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.c 1.36 2002/10/13 10:31:28 kls Exp $ + * $Id: osd.c 1.37 2002/11/10 12:30:09 kls Exp $ */ #include "osd.h" @@ -599,20 +599,24 @@ eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu) delete subMenu; subMenu = SubMenu; subMenu->Display(); - return osContinue; // convenience return value (see cMenuMain) + return osContinue; // convenience return value +} + +eOSState cOsdMenu::CloseSubMenu() +{ + delete subMenu; + subMenu = NULL; + RefreshCurrent(); + Display(); + return osContinue; // convenience return value } eOSState cOsdMenu::ProcessKey(eKeys Key) { if (subMenu) { eOSState state = subMenu->ProcessKey(Key); - if (state == osBack) { - delete subMenu; - subMenu = NULL; - RefreshCurrent(); - Display(); - state = osContinue; - } + if (state == osBack) + return CloseSubMenu(); return state; } diff --git a/osd.h b/osd.h index ada58e40..9fb515b4 100644 --- a/osd.h +++ b/osd.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.h 1.35 2002/11/03 18:03:55 kls Exp $ + * $Id: osd.h 1.36 2002/11/10 12:28:57 kls Exp $ */ #ifndef __OSD_H @@ -147,6 +147,7 @@ protected: void Mark(void); eOSState HotKey(eKeys Key); eOSState AddSubMenu(cOsdMenu *SubMenu); + eOSState CloseSubMenu(); bool HasSubMenu(void) { return subMenu; } void SetStatus(const char *s); void SetTitle(const char *Title, bool ShowDate = true); diff --git a/remote.h b/remote.h index 35cf62e5..b23b9335 100644 --- a/remote.h +++ b/remote.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remote.h 1.19 2002/11/01 10:48:11 kls Exp $ + * $Id: remote.h 1.20 2002/11/09 11:07:33 kls Exp $ */ #ifndef __REMOTE_H @@ -16,8 +16,6 @@ #include "thread.h" #include "tools.h" -typedef unsigned long long int uint64; - class cRemote : public cListObject { private: enum { MaxKeys = MAXKEYSINMACRO }; diff --git a/svdrp.c b/svdrp.c index 2859fb9d..66b420cf 100644 --- a/svdrp.c +++ b/svdrp.c @@ -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.48 2002/10/20 12:45:03 kls Exp $ + * $Id: svdrp.c 1.49 2002/11/10 12:09:56 kls Exp $ */ #include "svdrp.h" @@ -776,15 +776,20 @@ void cSVDRP::CmdMODC(const char *Option) tail = skipspace(tail); cChannel *channel = Channels.GetByNumber(n); if (channel) { - cChannel c = *channel; - if (!c.Parse(tail)) { - Reply(501, "Error in channel settings"); - return; + cChannel ch; + if (ch.Parse(tail, true)) { + if (Channels.HasUniqueChannelID(&ch, channel)) { + *channel = ch; + Channels.ReNumber(); + Channels.Save(); + isyslog("modifed channel %d %s", channel->Number(), channel->ToText()); + Reply(250, "%d %s", channel->Number(), channel->ToText()); + } + else + Reply(501, "Channel settings are not unique"); } - *channel = c; - Channels.Save(); - isyslog("channel %d modified", channel->Number()); - Reply(250, "%d %s", channel->Number(), channel->ToText()); + else + Reply(501, "Error in channel settings"); } else Reply(501, "Channel \"%d\" not defined", n); @@ -844,13 +849,19 @@ void cSVDRP::CmdMOVT(const char *Option) void cSVDRP::CmdNEWC(const char *Option) { if (*Option) { - cChannel *channel = new cChannel; - if (channel->Parse(Option)) { - Channels.Add(channel); - Channels.ReNumber(); - Channels.Save(); - isyslog("channel %d added", channel->Number()); - Reply(250, "%d %s", channel->Number(), channel->ToText()); + cChannel ch; + if (ch.Parse(Option, true)) { + if (Channels.HasUniqueChannelID(&ch)) { + cChannel *channel = new cChannel; + *channel = ch; + Channels.Add(channel); + Channels.ReNumber(); + Channels.Save(); + isyslog("new channel %d %s", channel->Number(), channel->ToText()); + Reply(250, "%d %s", channel->Number(), channel->ToText()); + } + else + Reply(501, "Channel settings are not unique"); } else Reply(501, "Error in channel settings"); diff --git a/timers.c b/timers.c index 76f86a5e..60445222 100644 --- a/timers.c +++ b/timers.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.c 1.1 2002/10/20 12:28:55 kls Exp $ + * $Id: timers.c 1.2 2002/11/10 10:19:12 kls Exp $ */ #include "timers.h" @@ -49,7 +49,7 @@ cTimer::cTimer(const cEventInfo *EventInfo) startTime = stopTime = 0; recording = pending = false; active = true; - channel = Channels.GetByServiceID(EventInfo->GetServiceID()); + channel = Channels.GetByChannelID(EventInfo->GetChannelID()); time_t tstart = EventInfo->GetTime(); time_t tstop = tstart + EventInfo->GetDuration() + Setup.MarginStop * 60; tstart -= Setup.MarginStart * 60; @@ -92,22 +92,17 @@ bool cTimer::operator< (const cListObject &ListObject) return t1 < t2 || (t1 == t2 && priority > ti->priority); } -const char *cTimer::ToText(cTimer *Timer) +const char *cTimer::ToText(bool UseChannelID) { free(buffer); - strreplace(Timer->file, ':', '|'); - strreplace(Timer->summary, '\n', '|'); - asprintf(&buffer, "%d:%d:%s:%04d:%04d:%d:%d:%s:%s\n", Timer->active, Timer->Channel()->Number(), PrintDay(Timer->day, Timer->firstday), Timer->start, Timer->stop, Timer->priority, Timer->lifetime, Timer->file, Timer->summary ? Timer->summary : ""); - strreplace(Timer->summary, '|', '\n'); - strreplace(Timer->file, '|', ':'); + strreplace(file, ':', '|'); + strreplace(summary, '\n', '|'); + asprintf(&buffer, "%d:%s:%s:%04d:%04d:%d:%d:%s:%s\n", active, UseChannelID ? Channel()->GetChannelIDStr() : itoa(Channel()->Number()), PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : ""); + strreplace(summary, '|', '\n'); + strreplace(file, '|', ':'); return buffer; } -const char *cTimer::ToText(void) -{ - return ToText(this); -} - int cTimer::TimeToInt(int t) { return (t / 100 * 60 + t % 100) * 60; @@ -189,8 +184,9 @@ const char *cTimer::PrintFirstDay(void) bool cTimer::Parse(const char *s) { - char *buffer1 = NULL; - char *buffer2 = NULL; + char *channelbuffer = NULL; + char *daybuffer = NULL; + char *filebuffer = NULL; free(summary); summary = NULL; //XXX Apparently sscanf() doesn't work correctly if the last %a argument @@ -208,34 +204,35 @@ bool cTimer::Parse(const char *s) strcat(strn0cpy(s2, s, l2 + 1), " \n"); s = s2; } - int ch = 0; - if (8 <= sscanf(s, "%d :%d :%a[^:]:%d :%d :%d :%d :%a[^:\n]:%a[^\n]", &active, &ch, &buffer1, &start, &stop, &priority, &lifetime, &buffer2, &summary)) { + bool result = false; + if (8 <= sscanf(s, "%d :%a[^:]:%a[^:]:%d :%d :%d :%d :%a[^:\n]:%a[^\n]", &active, &channelbuffer, &daybuffer, &start, &stop, &priority, &lifetime, &filebuffer, &summary)) { if (summary && !*skipspace(summary)) { free(summary); summary = NULL; } //TODO add more plausibility checks - day = ParseDay(buffer1, &firstday); - strn0cpy(file, buffer2, MaxFileName); + day = ParseDay(daybuffer, &firstday); + result = day != 0; + strn0cpy(file, filebuffer, MaxFileName); strreplace(file, '|', ':'); strreplace(summary, '|', '\n'); - free(buffer1); - free(buffer2); - free(s2); - channel = Channels.GetByNumber(ch); + uint64 cid = cChannel::StringToChannelID(channelbuffer); + channel = cid ? Channels.GetByChannelID(cid) : Channels.GetByNumber(atoi(channelbuffer)); if (!channel) { - esyslog("ERROR: channel %d not defined", ch); - return false; + esyslog("ERROR: channel %s not defined", channelbuffer); + result = false; } - return day != 0; } + free(channelbuffer); + free(daybuffer); + free(filebuffer); free(s2); - return false; + return result; } bool cTimer::Save(FILE *f) { - return fprintf(f, ToText()) > 0; + return fprintf(f, ToText(true)) > 0; } bool cTimer::IsSingleEvent(void) diff --git a/timers.h b/timers.h index 8dc762e3..933c88e9 100644 --- a/timers.h +++ b/timers.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.h 1.1 2002/10/20 11:52:23 kls Exp $ + * $Id: timers.h 1.2 2002/11/10 10:17:05 kls Exp $ */ #ifndef __TIMERS_H @@ -36,7 +36,6 @@ private: char file[MaxFileName]; time_t firstday; char *summary; - static const char *ToText(cTimer *Timer); public: cTimer(bool Instant = false); cTimer(const cEventInfo *EventInfo); @@ -55,7 +54,7 @@ public: const char *File(void) { return file; } time_t FirstDay(void) { return firstday; } const char *Summary(void) { return summary; } - const char *ToText(void); + const char *ToText(bool UseChannelID = false); bool Parse(const char *s); bool Save(FILE *f); bool IsSingleEvent(void); diff --git a/tools.c b/tools.c index df2d5705..68bfb7e4 100644 --- a/tools.c +++ b/tools.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.c 1.72 2002/10/19 12:32:53 kls Exp $ + * $Id: tools.c 1.73 2002/11/09 15:33:47 kls Exp $ */ #include "tools.h" @@ -243,6 +243,13 @@ bool isnumber(const char *s) return true; } +const char *itoa(int n) +{ + static char buf[16]; + snprintf(buf, sizeof(buf), "%d", n); + return buf; +} + const char *AddDirectory(const char *DirName, const char *FileName) { static char *buf = NULL; diff --git a/tools.h b/tools.h index 08f2429b..efa2de6a 100644 --- a/tools.h +++ b/tools.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.h 1.51 2002/10/19 12:31:50 kls Exp $ + * $Id: tools.h 1.52 2002/11/09 15:32:36 kls Exp $ */ #ifndef __TOOLS_H @@ -20,6 +20,7 @@ #include typedef unsigned char uchar; +typedef unsigned long long int uint64; extern int SysLogLevel; @@ -68,6 +69,7 @@ int numdigits(int n); int time_ms(void); void delay_ms(int ms); bool isnumber(const char *s); +const char *itoa(int n); // returns a statically allocated string! const char *AddDirectory(const char *DirName, const char *FileName); // returns a statically allocated string! int FreeDiskSpaceMB(const char *Directory, int *UsedMB = NULL); bool DirectoryOk(const char *DirName, bool LogErrors = false); diff --git a/vdr.5 b/vdr.5 index 2db8a7b6..9720a657 100644 --- a/vdr.5 +++ b/vdr.5 @@ -8,9 +8,9 @@ .\" License as specified in the file COPYING that comes with the .\" vdr distribution. .\" -.\" $Id: vdr.5 1.11 2002/10/27 15:36:44 kls Exp $ +.\" $Id: vdr.5 1.12 2002/11/10 10:10:15 kls Exp $ .\" -.TH vdr 5 "7 Oct 2002" "1.2.0" "Video Disk Recorder Files" +.TH vdr 5 "10 Nov 2002" "1.2.0" "Video Disk Recorder Files" .SH NAME vdr file formats - the Video Disk Recorder Files .SH DESCRIPTION @@ -120,6 +120,18 @@ l l. .TP .B SID The service ID of this channel. +.PP +A particular channel can be uniquely identified by its \fBchannel\ ID\fR, +which is a string that looks like this: + +\fBS19.2E-0-12188-12003\fR + +The components of this string are the \fBSource\fR (S19.2E), \fBFrequency\fR +(12188, MHz) and \fBSID\fR (12003) as defined above. The part that is currently +\fB0\fR is reserved for future use. +.br +The \fBchannel\ ID\fR is used in the \fItimers.conf\fR and \fIepg.data\fR +files to properly identify the channels. .SS TIMERS The file \fItimers.conf\fR contains the timer setup. Each line contains one timer definition, with individual fields @@ -143,7 +155,13 @@ Note: in order to allow future extensibility, external programs using the and leave the lower 16 bit untouched. .TP .B Channel -The number of the channel to record. +The channel to record from. This is either the channel number as shown in the +on-screen menus, or a complete channel ID. When reading \fItimers.conf\fR +any channel numbers will be mapped to the respective channel ids and when +the file is written again, there will only be channel ids. Channel numbers +are accepted as input in order to allow easier creation of timers when +manually editing \fItimers.conf\fR. Also, when timers are listed via SVDRP +commands, the channels are given as numbers. .TP .B Day The day when this timer shall record. @@ -472,7 +490,7 @@ The following tag characters are defined: .TS tab (@); l l. -\fBC\fR@ +\fBC\fR@ \fBE\fR@ \fBT\fR@ \fBS\fR@<subtitle> @@ -491,8 +509,8 @@ should at least have a \fBT\fR entry). .TS tab (@); l l. -<service id> @is the "program number" as defined in 'channels.conf' -<channel name> @is the "name" as in 'channels.conf' (for information only) +<channel id> @is the "channel ID", made up from the parameters defined in 'channels.conf' +<channel name> @is the "name" as in 'channels.conf' (for information only, may be left out) <start time> @is the time (as a time_t integer) in UTC when this event starts <duration> @is the time (in seconds) that this event will take <table id> @is a hex number that indicates the table this event is contained\