diff options
| author | Klaus Schmidinger <vdr@tvdr.de> | 2004-01-04 12:30:00 +0100 | 
|---|---|---|
| committer | Klaus Schmidinger <vdr@tvdr.de> | 2004-01-04 12:30:00 +0100 | 
| commit | 8976ebcec5ca1ac03c54209b7cc12e9d14915c6b (patch) | |
| tree | 8562202f489ee585c1252b2cb4a9e61b3e200efe | |
| parent | 3a1058fe1fca6d10cea42786aa54abf3d0bd0b94 (diff) | |
| download | vdr-8976ebcec5ca1ac03c54209b7cc12e9d14915c6b.tar.gz vdr-8976ebcec5ca1ac03c54209b7cc12e9d14915c6b.tar.bz2 | |
Implemented automatic PID switching and channel detection
| -rw-r--r-- | CONTRIBUTORS | 2 | ||||
| -rw-r--r-- | HISTORY | 25 | ||||
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | PLUGINS/src/sky/HISTORY | 4 | ||||
| -rw-r--r-- | PLUGINS/src/sky/sky.c | 12 | ||||
| -rw-r--r-- | channels.c | 223 | ||||
| -rw-r--r-- | channels.h | 52 | ||||
| -rw-r--r-- | ci.c | 23 | ||||
| -rw-r--r-- | ci.h | 3 | ||||
| -rw-r--r-- | config.h | 4 | ||||
| -rw-r--r-- | device.c | 42 | ||||
| -rw-r--r-- | device.h | 13 | ||||
| -rw-r--r-- | dvbdevice.c | 77 | ||||
| -rw-r--r-- | dvbdevice.h | 6 | ||||
| -rw-r--r-- | eit.c | 10 | ||||
| -rw-r--r-- | eitscan.c | 144 | ||||
| -rw-r--r-- | eitscan.h | 7 | ||||
| -rw-r--r-- | epg.h | 4 | ||||
| -rw-r--r-- | menu.c | 65 | ||||
| -rw-r--r-- | menu.h | 3 | ||||
| -rw-r--r-- | pat.c | 332 | ||||
| -rw-r--r-- | pat.h | 9 | ||||
| -rw-r--r-- | sdt.c | 146 | ||||
| -rw-r--r-- | sdt.h | 27 | ||||
| -rw-r--r-- | sections.c | 13 | ||||
| -rw-r--r-- | svdrp.c | 9 | ||||
| -rw-r--r-- | thread.c | 12 | ||||
| -rw-r--r-- | thread.h | 8 | ||||
| -rw-r--r-- | timers.c | 8 | ||||
| -rw-r--r-- | vdr.5 | 43 | ||||
| -rw-r--r-- | vdr.c | 21 | 
31 files changed, 1060 insertions, 291 deletions
| diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 1ae39e7d..d2f715dd 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -216,6 +216,8 @@ Andreas Schultz <aschultz@warp10.net>   for implementing the TerrestrialDeliverySystemDescriptor in libdtv   for fixing setting the locking pid after a timed wait   for changing thread handling to make it work with NPTL ("Native Posix Thread Library") + for his 'autopid' patch which was helpful when implementing automatic + channel data gathering  Aaron Holtzman   for writing 'ac3dec' @@ -2470,7 +2470,7 @@ Video Disk Recorder Revision History  - Final release of version 1.2.6. -2003-12-23: Version 1.3.0 +2004-01-04: Version 1.3.0  - Changed thread handling to make it work with NPTL ("Native Posix Thread Library").    Thanks to Jon Burgess, Andreas Schultz, Werner Fink and Stefan Huelswitt. @@ -2482,6 +2482,7 @@ Video Disk Recorder Revision History    instead of explicit 'dsyslog()' calls inside their Action() function in order    to support logging the thread ids.  - Added "Slovak Link" and "Czech Link" to 'ca.conf' (thanks to Emil Petersky). +  However, 'ca.conf' is now pretty much obsolete due to the automatic CA handling.  - Mutexes are now created with PTHREAD_MUTEX_ERRORCHECK_NP, which makes the    'lockingTid' stuff obsolete (thanks to Stefan Huelswitt).  - Changed font handling to allow language specific character sets. @@ -2499,7 +2500,7 @@ Video Disk Recorder Revision History    shortened to 'description'.  - Replaced 'libdtv' with 'libsi' (thanks to Marcel Wiesweg), which is thread    safe and can be used by multiple section filters simultaneously. -- Added 'cRWlock' to 'thread.[hc]'. Note that all plugin Makefiles need to +- Added 'cRwLock' to 'thread.[hc]'. Note that all plugin Makefiles need to    define _GNU_SOURCE for this to work (see the example plugin Makefiles and    'newplugin').  - Fixed a problem with crc32 in SI handling on 64bit systems (thanks to Pedro @@ -2512,3 +2513,23 @@ Video Disk Recorder Revision History    sections, depending on where they are found in the PMT (thanks to Hans-Peter    Raschke for reporting this one). This should make SkyCrypt CAMs work.  - Now using the 'version number' of EPG events to avoid unnecessary work. +- Channel data is now automatically derived from the DVB data stream (inspired +  by the 'autopid' patch from Andreas Schultz). +- The current channel is now automatically re-tuned if the PIDs or other settings +  change. If a recording is going on on a channel that has a change in its +  settings, the recording will be stopped and immediately restarted to use the +  new channel settings. +- EPG events now use the complete channel ID with NID, TID and SID. +- Channel names in 'channels.conf' can now have a short form, as provided +  by some tv stations (see man vdr(5)). Currently channels that provide short +  names in addition to long ones are listed in the OSD as "short,long name", +  as in "RTL,RTL Television". The short names will be used explicitly later. +- The Ca parameter in 'channels.conf' has been extended and now contains all the +  CA system ids for the given channel. When switching to a channel VDR now tests +  for a device that provides one of these CA system ids. The devices automatically +  get their supported ids from the CI handler. +- The values in 'ca.conf' are currently without any real meaning. Whether or not +  a channel with conditional access can be received is now determined automatically +  by evaluating its CA descriptors and comparing them to the CA system ids +  provided by the installed CAM. Only the special values 1-16 are used to assign +  a channel to a particular device. @@ -4,7 +4,7 @@  # See the main source file 'vdr.c' for copyright information and  # how to reach the author.  # -# $Id: Makefile 1.61 2003/12/21 14:45:27 kls Exp $ +# $Id: Makefile 1.62 2003/12/25 13:38:56 kls Exp $  .DELETE_ON_ERROR: @@ -36,7 +36,7 @@ SILIB    = $(LSIDIR)/libsi.a  OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbosd.o\         dvbplayer.o dvbspu.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\         lirc.o menu.o menuitems.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\ -       receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sections.o sources.o\ +       receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o sources.o\         spu.o status.o svdrp.o thread.o timers.o tools.o transfer.o vdr.o videodir.o  FIXFONT_ISO8859_1 = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1 diff --git a/PLUGINS/src/sky/HISTORY b/PLUGINS/src/sky/HISTORY index d41904e6..d3da71ec 100644 --- a/PLUGINS/src/sky/HISTORY +++ b/PLUGINS/src/sky/HISTORY @@ -12,3 +12,7 @@ VDR Plugin 'sky' Revision History  2003-05-09: Version 0.1.1  - Changed Start() to Initialize(). + +2004-01-04: Version 0.2.0 + +- Implemented automatic PID switching and channel detection diff --git a/PLUGINS/src/sky/sky.c b/PLUGINS/src/sky/sky.c index ae591378..0f2da6e9 100644 --- a/PLUGINS/src/sky/sky.c +++ b/PLUGINS/src/sky/sky.c @@ -3,7 +3,7 @@   *   * See the README file for copyright information and how to reach the author.   * - * $Id: sky.c 1.3 2003/05/09 15:27:16 kls Exp $ + * $Id: sky.c 1.4 2004/01/04 12:30:00 kls Exp $   */  #include <sys/socket.h> @@ -14,7 +14,7 @@  #include <vdr/plugin.h>  #include <vdr/sources.h> -static const char *VERSION        = "0.1.1"; +static const char *VERSION        = "0.2.0";  static const char *DESCRIPTION    = "Sky Digibox interface";  // --- cDigiboxDevice -------------------------------------------------------- @@ -37,6 +37,7 @@ public:    cDigiboxDevice(void);    virtual ~cDigiboxDevice();    virtual bool ProvidesSource(int Source) const; +  virtual bool ProvidesTransponder(const cChannel *Channel) const;    virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsSetChannel = NULL) const;    virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);    }; @@ -137,13 +138,18 @@ bool cDigiboxDevice::ProvidesSource(int Source) const    return source == Source;  } +bool cDigiboxDevice::ProvidesTransponder(const cChannel *Channel) const +{ +  return false; // can't provide any actual transponder +} +  bool cDigiboxDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const  {    bool result = false;    bool hasPriority = Priority < 0 || Priority > this->Priority();    bool needsDetachReceivers = true; -  if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel->Ca())) { +  if (ProvidesSource(Channel->Source()) && Channel->Ca() == 0x30) {//XXX       if (Receiving()) {          if (digiboxChannelNumber == Channel->Frequency()) {             needsDetachReceivers = false; @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: channels.c 1.16 2003/10/17 15:42:40 kls Exp $ + * $Id: channels.c 1.17 2004/01/04 12:28:49 kls Exp $   */  #include "channels.h" @@ -159,6 +159,7 @@ char *cChannel::buffer = NULL;  cChannel::cChannel(void)  { +  memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__);    strcpy(name,   "Pro7");    frequency    = 12480;    source       = cSource::FromString("S19.2E"); @@ -170,7 +171,7 @@ cChannel::cChannel(void)    dpid1        = 257;    dpid2        = 0;    tpid         = 32; -  ca           = 0; +  caids[0]     = 0;    nid          = 0;    tid          = 0;    sid          = 888; @@ -186,6 +187,28 @@ cChannel::cChannel(void)    transmission = TRANSMISSION_MODE_AUTO;    guard        = GUARD_INTERVAL_AUTO;    hierarchy    = HIERARCHY_AUTO; +  modification = CHANNELMOD_NONE; +} + +cChannel::cChannel(const cChannel *Channel) +{ +  *this = *Channel; +  *name = 0; +  vpid         = 0; +  ppid         = 0; +  apid1        = 0; +  apid2        = 0; +  dpid1        = 0; +  dpid2        = 0; +  tpid         = 0; +  caids[0]     = 0; +  nid          = 0; +  tid          = 0; +  sid          = 0; +  rid          = 0; +  number       = 0; +  groupSep     = false; +  modification = CHANNELMOD_NONE;  }  cChannel& cChannel::operator= (const cChannel &Channel) @@ -194,16 +217,117 @@ cChannel& cChannel::operator= (const cChannel &Channel)    return *this;  } -static int MHz(int frequency) +int cChannel::Transponder(void) const  { -  while (frequency > 20000) -        frequency /= 1000; -  return frequency; +  int tf = frequency; +  while (tf > 20000) +        tf /= 1000; +  return tf;  }  tChannelID cChannel::GetChannelID(void) const  { -  return tChannelID(source, nid, nid ? tid : MHz(frequency), sid, rid); +  return tChannelID(source, nid, nid ? tid : Transponder(), sid, rid); +} + +int cChannel::Modification(int Mask) +{ +  int Result = modification & Mask; +  modification = CHANNELMOD_NONE; +  return Result; +} + +void cChannel::SetId(int Nid, int Tid, int Sid, int Rid, bool Log) +{ +  if (nid != Nid || tid != Tid || sid != Sid || rid != Rid) { +     if (Log) +        dsyslog("changing id of channel %d from %d-%d-%d-%d to %d-%d-%d-%d", Number(), nid, tid, sid, rid, Nid, Tid, Sid, Rid); +     nid = Nid; +     tid = Tid; +     sid = Sid; +     rid = Rid; +     modification |= CHANNELMOD_ID; +     Channels.SetModified(); +     } +} + +void cChannel::SetName(const char *Name, bool Log) +{ +  if (!isempty(Name) && strcmp(name, Name) != 0) { +     if (Log) +        dsyslog("changing name of channel %d from '%s' to '%s'", Number(), name, Name); +     strn0cpy(name, Name, MaxChannelName); +     modification |= CHANNELMOD_NAME; +     Channels.SetModified(); +     } +} + +void cChannel::SetPids(int Vpid, int Ppid, int Apid1, int Apid2, int Dpid1, int Dpid2, int Tpid) +{ +  //XXX if (vpid != Vpid || ppid != Ppid || apid1 != Apid1 || apid2 != Apid2 || dpid1 != Dpid1 || dpid2 != Dpid2 || tpid != Tpid) { +  if (vpid != Vpid || ppid != Ppid || apid1 != Apid1 || (Apid2 && apid2 != Apid2) || dpid1 != Dpid1 || dpid2 != Dpid2 || tpid != Tpid) { +     dsyslog("changing pids of channel %d from %d+%d:%d,%d;%d,%d:%d to %d+%d:%d,%d;%d,%d:%d", Number(), vpid, ppid, apid1, apid2, dpid1, dpid2, tpid, Vpid, Ppid, Apid1, Apid2, Dpid1, Dpid2, Tpid); +     vpid = Vpid; +     ppid = Ppid; +     apid1 = Apid1; +     if (Apid2)//XXX should we actually react here? +     apid2 = Apid2; +     dpid1 = Dpid1; +     dpid2 = Dpid2; +     tpid = Tpid; +     modification |= CHANNELMOD_PIDS; +     Channels.SetModified(); +     } +} + +void cChannel::SetCaIds(const int *CaIds) +{ +  if (caids[0] && caids[0] <= 0x00FF) +     return; // special values will not be overwritten +  bool modified = false; +  for (int i = 0; i < MAXCAIDS; i++) { +      if (caids[i] != CaIds[i]) { +         modified = true; +         break; +         } +      if (!caids[i] || !CaIds[i]) +         break; +      } +  if (modified) { +     char OldCaIdsBuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia +     char NewCaIdsBuf[MAXCAIDS * 5 + 10]; +     char *qo = OldCaIdsBuf; +     char *qn = NewCaIdsBuf; +     int i; +     for (i = 0; i < MAXCAIDS; i++) { +         if (i == 0 || caids[i]) +            qo += snprintf(qo, sizeof(OldCaIdsBuf), "%s%X", i > 0 ? "," : "", caids[i]); +         if (!caids[i]) +            break; +         } +     for (i = 0; i < MAXCAIDS; i++) { +         if (i == 0 || CaIds[i]) +            qn += snprintf(qn, sizeof(NewCaIdsBuf), "%s%X", i > 0 ? "," : "", CaIds[i]); +         caids[i] = CaIds[i]; +         if (!CaIds[i]) +            break; +         } +     caids[i] = 0; +     *qo = *qn = 0; +     dsyslog("changing caids of channel %d from %s to %s", Number(), OldCaIdsBuf, NewCaIdsBuf); +     modification |= CHANNELMOD_CA; +     Channels.SetModified(); +     } +} + +void cChannel::SetCaDescriptors(int Level) +{ +  if (Level > 0) { +     modification |= CHANNELMOD_CA; +     Channels.SetModified(); +     if (Level > 1) +        dsyslog("changing ca descriptors of channel %d", Number()); +     }  }  static int PrintParameter(char *p, char Name, int Value) @@ -290,10 +414,10 @@ const char *cChannel::ToText(cChannel *Channel)       char vpidbuf[32];       char *q = vpidbuf;       q += snprintf(q, sizeof(vpidbuf), "%d", Channel->vpid); -     if (Channel->ppid) +     if (Channel->ppid && Channel->ppid != Channel->vpid)          q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "+%d", Channel->ppid);       *q = 0; -     char apidbuf[32]; +     char apidbuf[MAXAPIDS * 2 * 6 + 10]; // 2: Apids and Dpids, 6: 5 digits plus delimiting ',' or ';', 10: paranoia       q = apidbuf;       q += snprintf(q, sizeof(apidbuf), "%d", Channel->apid1);       if (Channel->apid2) @@ -303,7 +427,16 @@ const char *cChannel::ToText(cChannel *Channel)       if (Channel->dpid2)          q += snprintf(q, sizeof(apidbuf) - (q - apidbuf), ",%d", Channel->dpid2);       *q = 0; -     asprintf(&buffer, "%s:%d:%s:%s:%d:%s:%s:%d:%d:%d:%d:%d:%d\n", s, Channel->frequency, Channel->ParametersToString(), cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, Channel->tpid, Channel->ca, Channel->sid, Channel->nid, Channel->tid, Channel->rid); +     char caidbuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia +     q = caidbuf; +     for (int i = 0; i < MAXCAIDS; i++) { +         if (i == 0 || Channel->caids[i]) +            q += snprintf(q, sizeof(caidbuf), "%s%X", i > 0 ? "," : "", Channel->caids[i]); +         if (!Channel->caids[i]) +            break; +         } +     *q = 0; +     asprintf(&buffer, "%s:%d:%s:%s:%d:%s:%s:%d:%s:%d:%d:%d:%d\n", s, Channel->frequency, Channel->ParametersToString(), cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, Channel->tpid, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid);       }    return buffer;  } @@ -336,12 +469,16 @@ bool cChannel::Parse(const char *s, bool AllowNonUniqueID)       char *parambuf = NULL;       char *vpidbuf = NULL;       char *apidbuf = NULL; -     int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%d :%d :%d :%d :%d :%d ", &namebuf, &frequency, ¶mbuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpid, &ca, &sid, &nid, &tid, &rid); +     char *caidbuf = NULL; +     int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%d :%a[^:]:%d :%d :%d :%d ", &namebuf, &frequency, ¶mbuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpid, &caidbuf, &sid, &nid, &tid, &rid);       if (fields >= 9) {          if (fields == 9) {             // allow reading of old format -           sid = ca; -           ca = tpid; +           sid = atoi(caidbuf); +           delete caidbuf; +           caidbuf = NULL; +           caids[0] = tpid; +           caids[1] = 0;             tpid = 0;             }          vpid  = ppid  = 0; @@ -350,24 +487,46 @@ bool cChannel::Parse(const char *s, bool AllowNonUniqueID)          ok = false;          if (parambuf && sourcebuf && vpidbuf && apidbuf) {             ok = StringToParameters(parambuf) && (source = cSource::FromString(sourcebuf)) >= 0; +             char *p = strchr(vpidbuf, '+');             if (p)                *p++ = 0;             sscanf(vpidbuf, "%d", &vpid);             if (p)                sscanf(p, "%d", &ppid); +           else +              ppid = vpid; +             p = strchr(apidbuf, ';');             if (p)                *p++ = 0;             sscanf(apidbuf, "%d ,%d ", &apid1, &apid2);             if (p)                sscanf(p, "%d ,%d ", &dpid1, &dpid2); + +           if (caidbuf) { +              char *p = caidbuf; +              char *q; +              int NumCaIds = 0; +              while ((q = strtok(p, ",")) != NULL) { +                    if (NumCaIds < MAXCAIDS) { +                       caids[NumCaIds++] = strtol(q, NULL, 16) & 0xFFFF; +                       if (NumCaIds == 1 && caids[0] <= 0x00FF) +                          break; +                       } +                    else +                       esyslog("ERROR: too many CA ids!"); // no need to set ok to 'false' +                    p = NULL; +                    } +              caids[NumCaIds] = 0; +              }             }          strn0cpy(name, namebuf, MaxChannelName);          free(parambuf);          free(sourcebuf);          free(vpidbuf);          free(apidbuf); +        free(caidbuf);          free(namebuf);          if (!GetChannelID().Valid()) {             esyslog("ERROR: channel data results in invalid ID!"); @@ -394,6 +553,12 @@ bool cChannel::Save(FILE *f)  cChannels Channels; +cChannels::cChannels(void) +{ +  maxNumber = 0; +  modified = false; +} +  bool cChannels::Load(const char *FileName, bool AllowComments, bool MustExist)  {    if (cConfig<cChannel>::Load(FileName, AllowComments, MustExist)) { @@ -457,10 +622,10 @@ cChannel *cChannels::GetByNumber(int Number, int SkipGap)    return NULL;  } -cChannel *cChannels::GetByServiceID(int Source, unsigned short ServiceID) +cChannel *cChannels::GetByServiceID(int Source, int Transponder, unsigned short ServiceID)  {    for (cChannel *channel = First(); channel; channel = Next(channel)) { -      if (!channel->GroupSep() && channel->Source() == Source && channel->Sid() == ServiceID) +      if (!channel->GroupSep() && channel->Source() == Source && ISTRANSPONDER(channel->Transponder(), Transponder) && channel->Sid() == ServiceID)           return channel;        }    return NULL; @@ -497,3 +662,31 @@ bool cChannels::SwitchTo(int Number)    cChannel *channel = GetByNumber(Number);    return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true);  } + +void cChannels::SetModified(void) +{ +  modified = true; +} + +bool cChannels::Modified(void) +{ +  bool Result = modified; +  modified = false; +  return Result; +} + +cChannel *cChannels::NewChannel(int Source, int Transponder, const char *Name, int Nid, int Tid, int Sid, int Rid) +{ +  dsyslog("creating new channel '%s' on %s transponder %d with id %d-%d-%d-%d", Name, cSource::ToString(Source), Transponder, Nid, Tid, Sid, Rid); +  for (cChannel *channel = First(); channel; channel = Next(channel)) { +      if (!channel->GroupSep() && channel->Source() == Source && ISTRANSPONDER(channel->Transponder(), Transponder)) { +         cChannel *NewChannel = new cChannel(channel); +         Add(NewChannel); +         ReNumber(); +         NewChannel->SetId(Nid, Tid, Sid, Rid, false); +         NewChannel->SetName(Name, false); +         return NewChannel; +         } +      } +  return NULL; +} @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: channels.h 1.9 2003/10/26 13:32:00 kls Exp $ + * $Id: channels.h 1.10 2004/01/04 12:26:37 kls Exp $   */  #ifndef __CHANNELS_H @@ -12,10 +12,22 @@  #include "config.h"  #include "sources.h" +#include "thread.h"  #include "tools.h"  #define ISTRANSPONDER(f1, f2)  (abs((f1) - (f2)) < 4) //XXX +#define CHANNELMOD_NONE     0x00 +#define CHANNELMOD_ALL      0xFF +#define CHANNELMOD_NAME     0x01 +#define CHANNELMOD_PIDS     0x02 +#define CHANNELMOD_ID       0x04 +#define CHANNELMOD_CA       0x10 +#define CHANNELMOD_RETUNE   (CHANNELMOD_PIDS | CHANNELMOD_CA) + +#define MAXAPIDS  2 +#define MAXCAIDS  8 +  struct tChannelParameterMap {    int userValue;    int driverValue; @@ -46,7 +58,7 @@ public:    tChannelID(void) { source = nid = tid = sid = rid = 0; }    tChannelID(int Source, int Nid, int Tid, int Sid, int Rid = 0) { source = Source; nid = Nid; tid = Tid; sid = Sid; rid = Rid; }    bool operator== (const tChannelID &arg) const; -  bool Valid(void) { return tid && sid; } // nid and rid are optional and source may be 0 +  bool Valid(void) { return tid && sid; } // nid and rid are optional and source may be 0//XXX source may not be 0???    tChannelID &ClrRid(void) { rid = 0; return *this; }    static tChannelID FromString(const char *s);    const char *ToString(void); @@ -58,7 +70,7 @@ class cChannel : public cListObject {  private:    static char *buffer;    static const char *ToText(cChannel *Channel); -  enum { MaxChannelName = 32 }; // 31 chars + terminating 0! +  enum { MaxChannelName = 64 }; // 63 chars + terminating 0!    int __BeginData__;    char name[MaxChannelName];    int frequency; // MHz @@ -69,7 +81,7 @@ private:    int apid1, apid2;    int dpid1, dpid2;    int tpid; -  int ca; +  int caids[MAXCAIDS + 1]; // list is zero-terminated    int nid;    int tid;    int sid; @@ -86,16 +98,19 @@ private:    int guard;    int hierarchy;    int __EndData__; +  int modification;    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 AllowNonUniqueID = false);    bool Save(FILE *f);    const char *Name(void) const { return name; } -  int Frequency(void) const { return frequency; } +  int Frequency(void) const { return frequency; } ///< Returns the actual frequency, as given in 'channels.conf' +  int Transponder(void) const;                    ///< Returns the transponder frequency in MHz    int Source(void) const { return source; }    int Srate(void) const { return srate; }    int Vpid(void) const { return vpid; } @@ -105,8 +120,11 @@ public:    int Dpid1(void) const { return dpid1; }    int Dpid2(void) const { return dpid2; }    int Tpid(void) const { return tpid; } -  int Ca(void) const { return ca; } +  int Ca(int Index = 0) const { return Index < MAXCAIDS ? caids[Index] : 0; } +  int Nid(void) const { return nid; } +  int Tid(void) const { return tid; }    int Sid(void) const { return sid; } +  int Rid(void) const { return rid; }    int Number(void) const { return number; }    void SetNumber(int Number) { number = Number; }    bool GroupSep(void) const { return groupSep; } @@ -123,24 +141,38 @@ public:    bool IsSat(void) const { return (source & cSource::st_Mask) == cSource::stSat; }    bool IsTerr(void) const { return (source & cSource::st_Mask) == cSource::stTerr; }    tChannelID GetChannelID(void) const; +  int Modification(int Mask = CHANNELMOD_ALL); +  void SetId(int Nid, int Tid, int Sid, int Rid = 0, bool Log = true); +  void SetName(const char *Name, bool Log = true); +  void SetPids(int Vpid, int Ppid, int Apid1, int Apid2, int Dpid1, int Dpid2, int Tpid); +  void SetCaIds(const int *CaIds); // list must be zero-terminated +  void SetCaDescriptors(int Level);    }; -class cChannels : public cConfig<cChannel> { -protected: +class cChannels : public cRwLock, public cConfig<cChannel> { +private:    int maxNumber; +  bool modified; +  int beingEdited;  public: -  cChannels(void) { maxNumber = 0; } +  cChannels(void);    virtual bool Load(const char *FileName, bool AllowComments = false, bool MustExist = false);    int GetNextGroup(int Idx);   // Get next channel group    int GetPrevGroup(int Idx);   // Get previous channel group    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(int Source, unsigned short ServiceID); +  cChannel *GetByServiceID(int Source, int Transponder, unsigned short ServiceID);    cChannel *GetByChannelID(tChannelID ChannelID, bool TryWithoutRid = false); +  int BeingEdited(void) { return beingEdited; } +  void IncBeingEdited(void) { beingEdited++; } +  void DecBeingEdited(void) { beingEdited--; }    bool HasUniqueChannelID(cChannel *NewChannel, cChannel *OldChannel = NULL);    bool SwitchTo(int Number);    int MaxNumber(void) { return maxNumber; } +  void SetModified(void); +  bool Modified(void); +  cChannel *NewChannel(int Source, int Transponder, const char *Name, int Nid, int Tid, int Sid, int Rid = 0);    };  extern cChannels Channels; @@ -4,13 +4,9 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: ci.c 1.20 2003/12/24 10:23:24 kls Exp $ + * $Id: ci.c 1.21 2004/01/02 15:07:36 kls Exp $   */ -/* XXX TODO -- update CA descriptors in case they change -XXX*/ -  #include "ci.h"  #include <asm/unaligned.h>  #include <ctype.h> @@ -1570,6 +1566,23 @@ const unsigned short *cCiHandler::GetCaSystemIds(int Slot)    return cas ? cas->GetCaSystemIds() : NULL;  } +bool cCiHandler::ProvidesCa(const unsigned short *CaSystemIds) +{ +  cMutexLock MutexLock(&mutex); +  for (int Slot = 0; Slot < numSlots; Slot++) { +      cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT, Slot); +      if (cas) { +         for (const unsigned short *ids = cas->GetCaSystemIds(); ids && *ids; ids++) { +             for (const unsigned short *id = CaSystemIds; *id; id++) { +                 if (*id == *ids) +                    return true; +                 } +             } +         } +      } +  return false; +} +  bool cCiHandler::SetCaPmt(cCiCaPmt &CaPmt, int Slot)  {    cMutexLock MutexLock(&mutex); @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: ci.h 1.11 2003/12/24 10:05:46 kls Exp $ + * $Id: ci.h 1.12 2003/12/31 13:49:49 kls Exp $   */  #ifndef __CI_H @@ -111,6 +111,7 @@ public:    cCiMenu *GetMenu(void);    cCiEnquiry *GetEnquiry(void);    const unsigned short *GetCaSystemIds(int Slot); +  bool ProvidesCa(const unsigned short *CaSystemIds); //XXX Slot???    bool SetCaPmt(cCiCaPmt &CaPmt, int Slot);    bool Reset(int Slot);    }; @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: config.h 1.177 2003/10/18 11:14:33 kls Exp $ + * $Id: config.h 1.178 2003/12/27 13:57:56 kls Exp $   */  #ifndef __CONFIG_H @@ -87,7 +87,7 @@ public:    cConfig(void) { fileName = NULL; }    virtual ~cConfig() { free(fileName); }    const char *FileName(void) { return fileName; } -  bool Load(const char *FileName = NULL, bool AllowComments = false, bool MustExist = false) +  virtual bool Load(const char *FileName = NULL, bool AllowComments = false, bool MustExist = false)    {      Clear();      if (FileName) { @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: device.c 1.50 2003/12/22 10:53:45 kls Exp $ + * $Id: device.c 1.51 2004/01/04 11:30:05 kls Exp $   */  #include "device.h" @@ -47,6 +47,7 @@ cDevice::cDevice(void)    sectionHandler = NULL;    eitFilter = NULL;    patFilter = NULL; +  sdtFilter = NULL;    ciHandler = NULL;    player = NULL; @@ -68,8 +69,9 @@ cDevice::~cDevice()    for (int i = 0; i < MAXRECEIVERS; i++)        Detach(receiver[i]);    delete ciHandler; -  delete eitFilter; +  delete sdtFilter;    delete patFilter; +  delete eitFilter;    delete sectionHandler;  } @@ -157,7 +159,7 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDe        if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job           if (device[i]->Receiving() && !ndr)              pri = 0; // receiving and allows additional receivers -         else if (d && !device[i]->Receiving() && device[i]->ProvidesCa(Channel->Ca()) < d->ProvidesCa(Channel->Ca())) +         else if (d && !device[i]->Receiving() && device[i]->ProvidesCa(Channel) < d->ProvidesCa(Channel))              pri = 1; // free and fewer Ca's           else if (!device[i]->Receiving() && !device[i]->IsPrimaryDevice())              pri = 2; // free and not the primary device @@ -165,7 +167,7 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDe              pri = 3; // free           else if (d && device[i]->Priority() < d->Priority())              pri = 4; // receiving but priority is lower -         else if (d && device[i]->Priority() == d->Priority() && device[i]->ProvidesCa(Channel->Ca()) < d->ProvidesCa(Channel->Ca())) +         else if (d && device[i]->Priority() == d->Priority() && device[i]->ProvidesCa(Channel) < d->ProvidesCa(Channel))              pri = 5; // receiving with same priority but fewer Ca's           else              pri = 6; // all others @@ -325,6 +327,7 @@ void cDevice::StartSectionHandler(void)       sectionHandler = new cSectionHandler(this);       AttachFilter(eitFilter = new cEitFilter);       AttachFilter(patFilter = new cPatFilter); +     AttachFilter(sdtFilter = new cSdtFilter(patFilter));       sectionHandler->SetStatus(true);       }  } @@ -349,6 +352,11 @@ bool cDevice::ProvidesSource(int Source) const    return false;  } +bool cDevice::ProvidesTransponder(const cChannel *Channel) const +{ +  return false; +} +  bool cDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const  {    return false; @@ -431,6 +439,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)          Result = scrNotAvailable;       }    else { +     Channels.Lock(false);       cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel       // Stop section handling:       if (sectionHandler) { @@ -440,12 +449,13 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)       if (SetChannelDevice(Channel, LiveView)) {          // Start section handling:          if (sectionHandler) { -           sectionHandler->SetSource(Channel->Source(), Channel->Frequency()); +           sectionHandler->SetSource(Channel->Source(), Channel->Transponder());             sectionHandler->SetStatus(true);             }          }       else          Result = scrFailed; +     Channels.Unlock();       }    if (Result == scrOk) { @@ -462,6 +472,11 @@ bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)    return false;  } +bool cDevice::HasLock(void) +{ +  return true; +} +  bool cDevice::HasProgramme(void)  {    return Replaying() || pidHandles[ptAudio].pid || pidHandles[ptVideo].pid; @@ -651,6 +666,7 @@ int cDevice::Priority(void) const  int cDevice::CanShift(int Ca, int Priority, int UsedCards) const  {    return -1;//XXX+ too complex with multiple recordings per device +  /*XXX    // Test whether a receiver on this device can be shifted to another one    // in order to perform a new receiving with the given Ca and Priority on this device:    int ShiftLevel = -1; // default means this device can't be shifted @@ -681,25 +697,17 @@ int cDevice::CanShift(int Ca, int Priority, int UsedCards) const    else if (Priority > this->Priority())       ShiftLevel = 0; // no shifting necessary, this device can do the job    return ShiftLevel; +  XXX*/  } -int cDevice::ProvidesCa(int Ca) const +int cDevice::ProvidesCa(const cChannel *Channel) const  { +  int Ca = Channel->Ca();    if (Ca == CardIndex() + 1)       return 1; // exactly _this_ card was requested    if (Ca && Ca <= MAXDEVICES)       return 0; // a specific card was requested, but not _this_ one -  int result = Ca ? 0 : 1; // by default every card can provide FTA -  int others = Ca ? 1 : 0; -  for (int i = 0; i < MAXCACAPS; i++) { -      if (caCaps[i]) { -         if (caCaps[i] == Ca) -            result = 1; -         else -            others++; -         } -      } -  return result ? result + others : 0; +  return !Ca; // by default every card can provide FTA  }  bool cDevice::Receiving(bool CheckAny) const @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: device.h 1.36 2003/12/22 10:52:39 kls Exp $ + * $Id: device.h 1.37 2004/01/04 11:52:00 kls Exp $   */  #ifndef __DEVICE_H @@ -14,6 +14,7 @@  #include "eit.h"  #include "filter.h"  #include "pat.h" +#include "sdt.h"  #include "sections.h"  #include "thread.h"  #include "tools.h" @@ -131,7 +132,8 @@ public:           ///< Returns the card index of this device (0 ... MAXDEVICES - 1).    int DeviceNumber(void) const;           ///< Returns the number of this device (0 ... MAXDEVICES - 1). -  int ProvidesCa(int Ca) const; +  virtual int ProvidesCa(const cChannel *Channel) const;//XXX PLUGINS.html!!! +         //XXX describe changed functionality!!!           ///< Checks whether this device provides the given value in its           ///< caCaps. Returns 0 if the value is not provided, 1 if only this           ///< value is provided, and > 1 if this and other values are provided. @@ -161,6 +163,8 @@ protected:  public:    virtual bool ProvidesSource(int Source) const;           ///< Returns true if this device can provide the given source. +  virtual bool ProvidesTransponder(const cChannel *Channel) const; +         ///< XXX -> PLUGINS.html!    virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;           ///< Returns true if this device can provide the given channel.           ///< In case the device has cReceivers attached to it or it is the primary @@ -192,6 +196,10 @@ protected:  public:    static int CurrentChannel(void) { return primaryDevice ? currentChannel : 0; }           ///< Returns the number of the current channel on the primary device. +  virtual bool HasLock(void);//XXX PLUGINS.html +         ///< Returns true if the device has a lock on the requested transponder. +         ///< Default is true, a specific device implementation may return false +         ///< to indicate that it is not ready yet.    virtual bool HasProgramme(void);           ///< Returns true if the device is currently showing any programme to           ///< the user, either through replaying or live. @@ -232,6 +240,7 @@ private:    cSectionHandler *sectionHandler;    cEitFilter *eitFilter;    cPatFilter *patFilter; +  cSdtFilter *sdtFilter;  protected:    void StartSectionHandler(void);         ///< A derived device that provides section data must call diff --git a/dvbdevice.c b/dvbdevice.c index 76c3340b..15c6786d 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.75 2003/12/24 09:57:29 kls Exp $ + * $Id: dvbdevice.c 1.76 2004/01/04 12:28:00 kls Exp $   */  #include "dvbdevice.h" @@ -86,7 +86,7 @@ public:    virtual ~cDvbTuner();    bool IsTunedTo(const cChannel *Channel) const;    void Set(const cChannel *Channel, bool Tune, bool UseCa); -  bool Locked(void) { return tunerStatus == tsLocked; } +  bool Locked(void) { return tunerStatus >= tsLocked; }    };  cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCiHandler *CiHandler) @@ -114,19 +114,18 @@ cDvbTuner::~cDvbTuner()  bool cDvbTuner::IsTunedTo(const cChannel *Channel) const  { -  return tunerStatus != tsIdle && channel.Source() == Channel->Source() && channel.Frequency() == Channel->Frequency(); +  return tunerStatus != tsIdle && channel.Source() == Channel->Source() && channel.Transponder() == Channel->Transponder();  }  void cDvbTuner::Set(const cChannel *Channel, bool Tune, bool UseCa)  {    cMutexLock MutexLock(&mutex); -  bool CaChange = !(Channel->GetChannelID() == channel.GetChannelID());    if (Tune)       tunerStatus = tsSet; -  else if (tunerStatus == tsCam && CaChange) +  else if (tunerStatus == tsCam)       tunerStatus = tsTuned;    useCa = UseCa; -  if (Channel->Ca() && CaChange) +  if (Channel->Ca() && tunerStatus != tsCam)       startTime = time(NULL);    channel = *Channel;    newSet.Broadcast(); @@ -268,29 +267,27 @@ void cDvbTuner::Action(void)                   continue;                   }                } -           if (tunerStatus >= tsLocked) { -              if (ciHandler) { -                 if (ciHandler->Process() && useCa) { -                    if (tunerStatus != tsCam) {//XXX TODO update in case the CA descriptors have changed -                       for (int Slot = 0; Slot < ciHandler->NumSlots(); Slot++) { -                           cCiCaPmt CaPmt(channel.Source(), channel.Frequency(), channel.Sid(), ciHandler->GetCaSystemIds(Slot)); -                           if (CaPmt.Valid()) { -                              CaPmt.AddPid(channel.Vpid(), 2); -                              CaPmt.AddPid(channel.Apid1(), 4); -                              CaPmt.AddPid(channel.Apid2(), 4); -                              CaPmt.AddPid(channel.Dpid1(), 0); -                              if (ciHandler->SetCaPmt(CaPmt, Slot)) { -                                 tunerStatus = tsCam; -                                 startTime = 0; -                                 } -                              } +           } +        if (ciHandler) { +           if (ciHandler->Process() && useCa) { +              if (tunerStatus == tsLocked) { +                 for (int Slot = 0; Slot < ciHandler->NumSlots(); Slot++) { +                     cCiCaPmt CaPmt(channel.Source(), channel.Frequency(), channel.Sid(), ciHandler->GetCaSystemIds(Slot)); +                     if (CaPmt.Valid()) { +                        CaPmt.AddPid(channel.Vpid(), 2); +                        CaPmt.AddPid(channel.Apid1(), 4); +                        CaPmt.AddPid(channel.Apid2(), 4); +                        CaPmt.AddPid(channel.Dpid1(), 0); +                        if (ciHandler->SetCaPmt(CaPmt, Slot)) { +                           tunerStatus = tsCam; +                           startTime = 0;                             } -                       } -                    } -                 else -                    tunerStatus = tsLocked; +                        } +                     }                   }                } +           else if (tunerStatus > tsLocked) +              tunerStatus = tsLocked;             }          // in the beginning we loop more often to let the CAM connection start up fast          newSet.TimedWait(mutex, (ciHandler && (time(NULL) - startTime < 20)) ? 100 : 1000); @@ -436,6 +433,17 @@ bool cDvbDevice::HasDecoder(void) const    return fd_video >= 0 && fd_audio >= 0;  } +int cDvbDevice::ProvidesCa(const cChannel *Channel) const +{ +  if (Channel->Ca() >= 0x0100 && ciHandler) { +     unsigned short ids[MAXCAIDS + 1]; +     for (int i = 0; i <= MAXCAIDS; i++) // '<=' copies the terminating 0! +         ids[i] = Channel->Ca(i); +     return ciHandler->ProvidesCa(ids); +     } +  return cDevice::ProvidesCa(Channel); +} +  cOsdBase *cDvbDevice::NewOsd(int x, int y)  {    return new cDvbOsd(x, y); @@ -661,13 +669,18 @@ bool cDvbDevice::ProvidesSource(int Source) const    return true;  } +bool cDvbDevice::ProvidesTransponder(const cChannel *Channel) const +{ +  return ProvidesSource(Channel->Source()) && ((Channel->Source() & cSource::st_Mask) != cSource::stSat || Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization())); +} +  bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const  {    bool result = false;    bool hasPriority = Priority < 0 || Priority > this->Priority();    bool needsDetachReceivers = false; -  if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel->Ca())) { +  if ((Channel->Vpid() || Channel->Apid1()) && ProvidesSource(Channel->Source()) && ProvidesCa(Channel)) {       result = hasPriority;       if (Priority >= 0 && Receiving()) {          if (dvbTuner->IsTunedTo(Channel)) { @@ -736,15 +749,14 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)    if (TurnOffLivePIDs)       TurnOffLiveMode(); -  dvbTuner->Set(Channel, DoTune, !EITScanner.UsesDevice(this)); //XXX 1.3: this is an ugly hack - find a cleaner solution +  dvbTuner->Set(Channel, DoTune, !EITScanner.UsesDevice(this)); //XXX 1.3: this is an ugly hack - find a cleaner solution//XXX    // PID settings:    if (TurnOnLivePIDs) {       aPid1 = Channel->Apid1();       aPid2 = Channel->Apid2(); -     int pPid = Channel->Ppid() ? Channel->Ppid() : Channel->Vpid(); -     if (!(AddPid(pPid, ptPcr) && AddPid(Channel->Apid1(), ptAudio) && AddPid(Channel->Vpid(), ptVideo))) {//XXX+ dolby dpid1!!! (if audio plugins are attached) +     if (!(AddPid(Channel->Ppid(), ptPcr) && AddPid(Channel->Apid1(), ptAudio) && AddPid(Channel->Vpid(), ptVideo))) {//XXX+ dolby dpid1!!! (if audio plugins are attached)          esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1);          return false;          } @@ -758,6 +770,11 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)    return true;  } +bool cDvbDevice::HasLock(void) +{ +  return dvbTuner->Locked(); +} +  void cDvbDevice::SetVolumeDevice(int Volume)  {    if (HasDecoder()) { diff --git a/dvbdevice.h b/dvbdevice.h index f6f6f484..f64e6dda 100644 --- a/dvbdevice.h +++ b/dvbdevice.h @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: dvbdevice.h 1.25 2003/12/21 14:04:00 kls Exp $ + * $Id: dvbdevice.h 1.26 2004/01/03 10:21:50 kls Exp $   */  #ifndef __DVBDEVICE_H @@ -44,6 +44,7 @@ protected:  public:    cDvbDevice(int n);    virtual ~cDvbDevice(); +  virtual int ProvidesCa(const cChannel *Channel) const;    virtual bool HasDecoder(void) const;  // OSD facilities @@ -61,9 +62,12 @@ private:    void TurnOffLiveMode(void);  public:    virtual bool ProvidesSource(int Source) const; +  virtual bool ProvidesTransponder(const cChannel *Channel) const;    virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;  protected:    virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView); +public: +  virtual bool HasLock(void);  // PID handle facilities @@ -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.83 2003/12/25 12:48:47 kls Exp $ + * $Id: eit.c 1.84 2004/01/02 22:27:29 kls Exp $   */  #include "eit.h" @@ -29,12 +29,10 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)    if (!CheckCRCAndParse())       return; -  //XXX TODO use complete channel ID -  cChannel *channel = Channels.GetByServiceID(Source, getServiceId()); +  tChannelID channelID(Source, getOriginalNetworkId(), getTransportStreamId(), getServiceId()); +  cChannel *channel = Channels.GetByChannelID(channelID, true);    if (!channel)       return; // only collect data for known channels -  tChannelID channelID = channel->GetChannelID(); -  channelID.ClrRid();    cEvent *rEvent = NULL; @@ -82,7 +80,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)           // Unfortunately some stations (like, e.g. "Premiere") broadcast their EPG data on several transponders (like           // the actual Premiere transponder and the Sat.1/Pro7 transponder), but use different version numbers on           // each of them :-( So if one DVB card is tuned to the Premiere transponder, while an other one is tuned -         // to the Sat.1/Pro7 transponder, events will keep toggling because ot the bogus version numbers. +         // to the Sat.1/Pro7 transponder, events will keep toggling because of the bogus version numbers.           if (Tid == pEvent->TableID() && pEvent->Version() == getVersionNumber())              continue;           } @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: eitscan.c 1.14 2003/09/06 13:06:13 kls Exp $ + * $Id: eitscan.c 1.15 2004/01/04 12:28:00 kls Exp $   */  #include "eitscan.h" @@ -12,6 +12,68 @@  #include "channels.h"  #include "dvbdevice.h" +// --- cScanData ------------------------------------------------------------- + +class cScanData : public cListObject { +private: +  int source; +  int transponder; +public: +  cScanData(int Source, int Transponder); +  virtual bool operator< (const cListObject &ListObject); +  int Source(void) { return source; } +  int Transponder(void) { return transponder; } +  cChannel *GetChannel(void); +  }; + +cScanData::cScanData(int Source, int Transponder) +{ +  source = Source; +  transponder = Transponder; +} + +bool cScanData::operator< (const cListObject &ListObject) +{ +  cScanData *sd = (cScanData *)&ListObject; +  return source < sd->source || source == sd->source && transponder < sd->transponder; +} + +//XXX this might be done differently later... +cChannel *cScanData::GetChannel(void) +{ +  for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { +      if (!Channel->GroupSep() && Channel->Source() == source && ISTRANSPONDER(Channel->Transponder(), transponder)) +         return Channel; +      } +  return NULL; +} + +// --- cScanList ------------------------------------------------------------- + +class cScanList : public cList<cScanData> { +public: +  cScanList(void); +  void AddTransponder(const cChannel *Channel); +  }; + +cScanList::cScanList(void) +{ +  for (cChannel *ch = Channels.First(); ch; ch = Channels.Next(ch)) +      AddTransponder(ch); +  Sort(); +} + +void cScanList::AddTransponder(const cChannel *Channel) +{ +  for (cScanData *sd = First(); sd; sd = Next(sd)) { +      if (sd->Source() == Channel->Source() && sd->Transponder() == Channel->Transponder()) +         return; +      } +  Add(new cScanData(Channel->Source(), Channel->Transponder())); +} + +// --- cEITScanner ----------------------------------------------------------- +  cEITScanner EITScanner;  cEITScanner::cEITScanner(void) @@ -20,24 +82,12 @@ cEITScanner::cEITScanner(void)    currentDevice = NULL;    currentChannel = 0;    memset(lastChannel, 0, sizeof(lastChannel)); -  numTransponders = 0; -  transponders = NULL; +  scanList = NULL;  }  cEITScanner::~cEITScanner()  { -  free(transponders); -} - -bool cEITScanner::TransponderScanned(cChannel *Channel) -{ -  for (int i = 0; i < numTransponders; i++) { -      if (transponders[i] == Channel->Frequency()) -         return true; -      } -  transponders = (int *)realloc(transponders, ++numTransponders * sizeof(int)); -  transponders[numTransponders - 1] = Channel->Frequency(); -  return false; +  delete scanList;  }  void cEITScanner::Activity(void) @@ -54,37 +104,51 @@ void cEITScanner::Process(void)    if (Setup.EPGScanTimeout && Channels.MaxNumber() > 1) {       time_t now = time(NULL);       if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) { -        for (int i = 0; i < cDevice::NumDevices(); i++) { -            cDevice *Device = cDevice::GetDevice(i); -            if (Device && Device->CardIndex() < MAXDVBDEVICES) { -               if (Device != cDevice::PrimaryDevice() || (cDevice::NumDevices() == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) { -                  if (!(Device->Receiving(true) || Device->Replaying())) { -                     for (;;) { -                         cChannel *Channel = Channels.GetByNumber(lastChannel[Device->DeviceNumber()] + 1, 1); -                         if (Channel) { -                            lastChannel[Device->DeviceNumber()] = Channel->Number(); -                            if (Channel->Sid() && Device->ProvidesChannel(Channel) && !TransponderScanned(Channel)) { -                               if (Device == cDevice::PrimaryDevice() && !currentChannel) { -                                  currentChannel = Device->CurrentChannel(); +        if (Channels.Lock(false, 10)) { +           if (!scanList) +              scanList = new cScanList(); +           for (bool AnyDeviceSwitched = false; !AnyDeviceSwitched; ) { +               cScanData *ScanData = NULL; +               for (int i = 0; i < cDevice::NumDevices(); i++) { +                   cDevice *Device = cDevice::GetDevice(i); +                   if (Device) { +                      if (Device != cDevice::PrimaryDevice() || (cDevice::NumDevices() == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) { +                         if (!(Device->Receiving(true) || Device->Replaying())) { +                            if (!ScanData) +                               ScanData = scanList->First(); +                            if (ScanData) { +                               cChannel *Channel = ScanData->GetChannel(); +                               //XXX if (Device->ProvidesTransponder(Channel)) { +                               if ((!Channel->Ca() || Channel->Ca() == Device->DeviceNumber() + 1 || Channel->Ca() >= 0x0100) && Device->ProvidesTransponder(Channel)) { //XXX temporary for the 'sky' plugin +                                  if (Device == cDevice::PrimaryDevice() && !currentChannel) +                                     currentChannel = Device->CurrentChannel(); +                                  currentDevice = Device;//XXX see also dvbdevice.c!!! +                                  Device->SwitchChannel(Channel, false); +                                  currentDevice = NULL; +                                  scanList->Del(ScanData); +                                  ScanData = NULL; +                                  AnyDeviceSwitched = true;                                    } -                               currentDevice = Device; -                               Device->SwitchChannel(Channel, false); -                               currentDevice = NULL; -                               break;                                 } -                            } -                         else { -                            if (lastChannel[Device->DeviceNumber()]) -                               numTransponders = 0; -                            lastChannel[Device->DeviceNumber()] = 0; -                            break; +                            else +                               break;                              }                           } -                     } +                      } +                   } +               if (ScanData && !AnyDeviceSwitched) { +                  scanList->Del(ScanData); +                  ScanData = NULL; +                  } +               if (!scanList->Count()) { +                  delete scanList; +                  scanList = NULL; +                  break;                    }                 } -            } -        lastScan = time(NULL); +           Channels.Unlock(); +           lastScan = time(NULL); +           }          }       }  } @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: eitscan.h 1.4 2003/09/06 13:05:51 kls Exp $ + * $Id: eitscan.h 1.5 2004/01/03 13:08:39 kls Exp $   */  #ifndef __EITSCAN_H @@ -13,6 +13,8 @@  #include <time.h>  #include "config.h" +class cScanList; +  class cEITScanner {  private:    enum { ActivityTimeout = 60, @@ -22,8 +24,7 @@ private:    cDevice *currentDevice;    int currentChannel;    int lastChannel[MAXDEVICES]; -  int numTransponders, *transponders; -  bool TransponderScanned(cChannel *Channel); +  cScanList *scanList;  public:    cEITScanner(void);    ~cEITScanner(); @@ -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.2 2003/12/24 13:20:35 kls Exp $ + * $Id: epg.h 1.3 2004/01/03 17:00:25 kls Exp $   */  #ifndef __EPG_H @@ -110,7 +110,7 @@ class cSchedules : public cList<cSchedule> {    friend class cSchedule;    friend class cSchedulesLock;  private: -  cRWlock rwlock; +  cRwLock rwlock;    static cSchedules schedules;    static const char *epgDataFileName;    static time_t lastCleanup; @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: menu.c 1.275 2003/12/22 10:05:14 kls Exp $ + * $Id: menu.c 1.276 2004/01/04 11:12:43 kls Exp $   */  #include "menu.h" @@ -581,7 +581,7 @@ void cMenuEditChannel::Setup(void)    Add(new cMenuEditIntItem( tr("Dpid1"),        &data.dpid1, 0, 0x1FFF));    Add(new cMenuEditIntItem( tr("Dpid2"),        &data.dpid2, 0, 0x1FFF));    Add(new cMenuEditIntItem( tr("Tpid"),         &data.tpid,  0, 0x1FFF)); -  Add(new cMenuEditCaItem(  tr("CA"),           &data.ca, true)); +  Add(new cMenuEditCaItem(  tr("CA"),           &data.caids[0], true));//XXX    Add(new cMenuEditIntItem( tr("Sid"),          &data.sid, 0));    /* XXX not yet used    Add(new cMenuEditIntItem( tr("Nid"),          &data.nid, 0)); @@ -615,7 +615,6 @@ eOSState cMenuEditChannel::ProcessKey(eKeys Key)             if (channel) {                *channel = data;                isyslog("edited channel %d %s", channel->Number(), data.ToText()); -              Timers.Save();                state = osBack;                }             else { @@ -626,7 +625,7 @@ eOSState cMenuEditChannel::ProcessKey(eKeys Key)                isyslog("added channel %d %s", channel->Number(), data.ToText());                state = osUser1;                } -           Channels.Save(); +           Channels.SetModified();             }          else {             Interface->Error(tr("Channel settings are not unique!")); @@ -682,6 +681,7 @@ protected:    virtual void Move(int From, int To);  public:    cMenuChannels(void); +  ~cMenuChannels();    virtual eOSState ProcessKey(eKeys Key);    }; @@ -693,6 +693,12 @@ cMenuChannels::cMenuChannels(void)           Add(new cMenuChannelItem(channel), channel->Number() == cDevice::CurrentChannel());        }    SetHelp(tr("Edit"), tr("New"), tr("Delete"), tr("Mark")); +  Channels.IncBeingEdited(); +} + +cMenuChannels::~cMenuChannels() +{ +  Channels.DecBeingEdited();  }  cChannel *cMenuChannels::GetChannel(int Index) @@ -704,11 +710,10 @@ cChannel *cMenuChannels::GetChannel(int Index)  void cMenuChannels::Propagate(void)  {    Channels.ReNumber(); -  Channels.Save();    for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())        ci->Set(); -  Timers.Save(); // channel numbering has changed!    Display(); +  Channels.SetModified();  }  eOSState cMenuChannels::Switch(void) @@ -1380,22 +1385,24 @@ void cMenuSchedule::PrepareSchedule(cChannel *Channel)    free(buffer);    if (schedules) {       const cSchedule *Schedule = schedules->GetSchedule(Channel->GetChannelID()); -     int num = Schedule->NumEvents(); -     const cEvent **pArray = MALLOC(const cEvent *, num); -     if (pArray) { -        time_t now = time(NULL); -        int numreal = 0; -        for (int a = 0; a < num; a++) { -            const cEvent *Event = Schedule->GetEventNumber(a); -            if (Event->StartTime() + Event->Duration() > now) -               pArray[numreal++] = Event; -            } - -        qsort(pArray, numreal, sizeof(cEvent *), CompareEventTime); - -        for (int a = 0; a < numreal; a++) -            Add(new cMenuScheduleItem(pArray[a])); -        free(pArray); +     if (Schedule) { +        int num = Schedule->NumEvents(); +        const cEvent **pArray = MALLOC(const cEvent *, num); +        if (pArray) { +           time_t now = time(NULL); +           int numreal = 0; +           for (int a = 0; a < num; a++) { +               const cEvent *Event = Schedule->GetEventNumber(a); +               if (Event->StartTime() + Event->Duration() > now) +                  pArray[numreal++] = Event; +               } +    +           qsort(pArray, numreal, sizeof(cEvent *), CompareEventTime); +    +           for (int a = 0; a < numreal; a++) +               Add(new cMenuScheduleItem(pArray[a])); +           free(pArray); +           }          }       }  } @@ -3219,6 +3226,20 @@ void cRecordControls::Process(time_t t)        }  } +void cRecordControls::ChannelDataModified(cChannel *Channel) +{ +  for (int i = 0; i < MAXRECORDCONTROLS; i++) { +      if (RecordControls[i]) { +         if (RecordControls[i]->Timer() && RecordControls[i]->Timer()->Channel() == Channel) { +            isyslog("stopping recording due to modification of channel %d", Channel->Number()); +            RecordControls[i]->Stop(true); +            // This will restart the recording, maybe even from a different +            // device in case conditional access has changed. +            } +         } +      } +} +  bool cRecordControls::Active(void)  {    for (int i = 0; i < MAXRECORDCONTROLS; i++) { @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: menu.h 1.58 2003/12/21 15:27:07 kls Exp $ + * $Id: menu.h 1.59 2004/01/04 11:01:13 kls Exp $   */  #ifndef __MENU_H @@ -143,6 +143,7 @@ public:    static const char *GetInstantId(const char *LastInstantId);    static cRecordControl *GetRecordControl(const char *FileName);    static void Process(time_t t); +  static void ChannelDataModified(cChannel *Channel);    static bool Active(void);    static void Shutdown(void);    }; @@ -4,45 +4,39 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: pat.c 1.2 2003/12/24 10:23:33 kls Exp $ + * $Id: pat.c 1.3 2004/01/04 12:27:06 kls Exp $   */  #include "pat.h"  #include <malloc.h> +#include "channels.h"  #include "libsi/section.h"  #include "libsi/descriptor.h" +#include "thread.h"  #define PMT_SCAN_TIMEOUT  10 // seconds  // --- cCaDescriptor ---------------------------------------------------------  class cCaDescriptor : public cListObject { -  friend class cCaDescriptors;  private: -  int source; -  int transponder; -  int serviceId;    int caSystem; -  int providerId; -  int caPid;    bool stream;    int length;    uchar *data;  public: -  cCaDescriptor(int Source, int Transponder, int ServiceId, int CaSystem, int ProviderId, int CaPid, bool Stream, int Length, const uchar *Data); +  cCaDescriptor(int CaSystem, int CaPid, bool Stream, int Length, const uchar *Data);    virtual ~cCaDescriptor(); +  bool operator== (const cCaDescriptor &arg) const; +  int CaSystem(void) { return caSystem; } +  int Stream(void) { return stream; }    int Length(void) const { return length; }    const uchar *Data(void) const { return data; }    }; -cCaDescriptor::cCaDescriptor(int Source, int Transponder, int ServiceId, int CaSystem, int ProviderId, int CaPid, bool Stream, int Length, const uchar *Data) +cCaDescriptor::cCaDescriptor(int CaSystem, int CaPid, bool Stream, int Length, const uchar *Data)  { -  source = Source; -  transponder = Transponder; -  serviceId = ServiceId;    caSystem = CaSystem; -  providerId = ProviderId; -  caPid = CaPid;    stream = Stream;    length = Length + 6;    data = MALLOC(uchar, length); @@ -54,15 +48,6 @@ cCaDescriptor::cCaDescriptor(int Source, int Transponder, int ServiceId, int CaS    data[5] =   CaPid         & 0xFF;    if (Length)       memcpy(&data[6], Data, Length); -//#define DEBUG_CA_DESCRIPTORS 1 -#ifdef DEBUG_CA_DESCRIPTORS -  char buffer[1024]; -  char *q = buffer; -  q += sprintf(q, "CAM: %04X %5d %5d %04X %6X %04X %d -", source, transponder, serviceId, caSystem, providerId, caPid, stream); -  for (int i = 0; i < length; i++) -      q += sprintf(q, " %02X", data[i]); -  dsyslog(buffer); -#endif  }  cCaDescriptor::~cCaDescriptor() @@ -70,75 +55,121 @@ cCaDescriptor::~cCaDescriptor()    free(data);  } +bool cCaDescriptor::operator== (const cCaDescriptor &arg) const +{ +  return length == arg.length && memcmp(data, arg.data, length) == 0; +} +  // --- cCaDescriptors -------------------------------------------------------- -class cCaDescriptors : public cList<cCaDescriptor> { +class cCaDescriptors : public cListObject {  private: -  cMutex mutex; +  int source; +  int transponder; +  int serviceId; +  int numCaIds; +  int caIds[MAXCAIDS + 1]; +  cList<cCaDescriptor> caDescriptors; +  void AddCaId(int CaId);  public: -  void NewCaDescriptor(int Source, int Transponder, int ServiceId, SI::CaDescriptor *d, bool Stream); -  int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag); +  cCaDescriptors(int Source, int Transponder, int ServiceId); +  bool operator== (const cCaDescriptors &arg) const; +  bool Is(int Source, int Transponder, int ServiceId); +  bool Is(cCaDescriptors * CaDescriptors); +  bool Empty(void) { return caDescriptors.Count() == 0; } +  void AddCaDescriptor(SI::CaDescriptor *d, bool Stream); +  int GetCaDescriptors(const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag); +  const int *CaIds(void) { return caIds; }    }; -void cCaDescriptors::NewCaDescriptor(int Source, int Transponder, int ServiceId, SI::CaDescriptor *d, bool Stream) -{ -  // The code for determining the ProviderID was taken from 'libdtv' -  // written by Rolf Hakenes <hakenes@hippomi.de>. - -  const uchar *Data = d->privateData.getData(); -  int Length = d->privateData.getLength(); -  int ProviderID = 0; - -  switch (d->getCaType() >> 8) { -    case 0x01: // SECA -         ProviderID = (Data[0] << 8) | Data[1]; -         break; -    case 0x05: // Viaccess ? (France Telecom) -         for (int i = 0; i < Length; i++) { -             if (Data[i] == 0x14 && Data[i + 1] == 0x03) { -                ProviderID = (Data[i + 2] << 16) | -                             (Data[i + 3] << 8) | -                             (Data[i + 4] & 0xf0); -                break; -                } -             } -         break; +cCaDescriptors::cCaDescriptors(int Source, int Transponder, int ServiceId) +{ +  source = Source; +  transponder = Transponder; +  serviceId = ServiceId; +  numCaIds = 0; +  caIds[0] = 0; +} + +bool cCaDescriptors::operator== (const cCaDescriptors &arg) const +{ +  cCaDescriptor *ca1 = caDescriptors.First(); +  cCaDescriptor *ca2 = arg.caDescriptors.First(); +  while (ca1 && ca2) { +        if (!(*ca1 == *ca2)) +           return false; +        ca1 = caDescriptors.Next(ca1); +        ca2 = arg.caDescriptors.Next(ca2); +        } +  return !ca1 && !ca2; +} + +bool cCaDescriptors::Is(int Source, int Transponder, int ServiceId) +{ +  return source == Source && transponder == Transponder && serviceId == ServiceId; +} + +bool cCaDescriptors::Is(cCaDescriptors * CaDescriptors) +{ +  return Is(CaDescriptors->source, CaDescriptors->transponder, CaDescriptors->serviceId); +} + +void cCaDescriptors::AddCaId(int CaId) +{ +  if (numCaIds < MAXCAIDS) { +     for (int i = 0; i < numCaIds; i++) { +         if (caIds[i] == CaId) +            return; +         } +     caIds[numCaIds++] = CaId; +     caIds[numCaIds] = 0;       } +} -  cMutexLock MutexLock(&mutex); -  for (cCaDescriptor *ca = First(); ca; ca = Next(ca)) { -      if (ca->source == Source && ca->transponder == Transponder && ca->serviceId == ServiceId && ca->caSystem == d->getCaType() && ca->providerId == ProviderID && ca->caPid == d->getCaPid()) +void cCaDescriptors::AddCaDescriptor(SI::CaDescriptor *d, bool Stream) +{ +  cCaDescriptor *nca = new cCaDescriptor(d->getCaType(), d->getCaPid(), Stream, d->privateData.getLength(), d->privateData.getData()); +  for (cCaDescriptor *ca = caDescriptors.First(); ca; ca = caDescriptors.Next(ca)) { +      if (*ca == *nca) { +         delete nca;           return; +         }        } -  Add(new cCaDescriptor(Source, Transponder, ServiceId, d->getCaType(), ProviderID, d->getCaPid(), Stream, Length, Data)); -  //XXX update??? +  AddCaId(nca->CaSystem()); +  caDescriptors.Add(nca); +//#define DEBUG_CA_DESCRIPTORS 1 +#ifdef DEBUG_CA_DESCRIPTORS +  char buffer[1024]; +  char *q = buffer; +  q += sprintf(q, "CAM: %04X %5d %5d %04X %d -", source, transponder, serviceId, d->getCaType(), Stream); +  for (int i = 0; i < nca->Length(); i++) +      q += sprintf(q, " %02X", nca->Data()[i]); +  dsyslog(buffer); +#endif  } -int cCaDescriptors::GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag) +int cCaDescriptors::GetCaDescriptors(const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)  {    if (!CaSystemIds || !*CaSystemIds)       return 0;    if (BufSize > 0 && Data) { -     cMutexLock MutexLock(&mutex);       int length = 0;       int IsStream = -1; -     for (cCaDescriptor *d = First(); d; d = Next(d)) { -         if (d->source == Source && d->transponder == Transponder && d->serviceId == ServiceId) { -            const unsigned short *caids = CaSystemIds; -            do { -               if (d->caSystem == *caids) { -                  if (length + d->Length() <= BufSize) { -                     if (IsStream >= 0 && IsStream != d->stream) -                        dsyslog("CAM: different stream flag in CA descriptors"); -                     IsStream = d->stream; -                     memcpy(Data + length, d->Data(), d->Length()); -                     length += d->Length(); -                     } -                  else -                     return -1; +     for (cCaDescriptor *d = caDescriptors.First(); d; d = caDescriptors.Next(d)) { +         const unsigned short *caids = CaSystemIds; +         do { +            if (d->CaSystem() == *caids) { +               if (length + d->Length() <= BufSize) { +                  if (IsStream >= 0 && IsStream != d->Stream()) +                     dsyslog("CAM: different stream flag in CA descriptors"); +                  IsStream = d->Stream(); +                  memcpy(Data + length, d->Data(), d->Length()); +                  length += d->Length();                    } -               } while (*++caids); -            } +               else +                  return -1; +               } +            } while (*++caids);           }       StreamFlag = IsStream == 1;       return length; @@ -146,11 +177,52 @@ int cCaDescriptors::GetCaDescriptors(int Source, int Transponder, int ServiceId,    return -1;  } -cCaDescriptors CaDescriptors; +// --- cCaDescriptorHandler -------------------------------------------------- + +class cCaDescriptorHandler : public cList<cCaDescriptors> { +private: +  cMutex mutex; +public: +  int AddCaDescriptors(cCaDescriptors *CaDescriptors); +      // Returns 0 if this is an already known descriptor, +      // 1 if it is an all new descriptor with actual contents, +      // and 2 if an existing descriptor was changed. +  int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag); +  }; + +int cCaDescriptorHandler::AddCaDescriptors(cCaDescriptors *CaDescriptors) +{ +  cMutexLock MutexLock(&mutex); +  for (cCaDescriptors *ca = First(); ca; ca = Next(ca)) { +      if (ca->Is(CaDescriptors)) { +         if (*ca == *CaDescriptors) { +            delete CaDescriptors; +            return 0; +            } +         Del(ca); +         Add(CaDescriptors); +         return 2; +         } +      } +  Add(CaDescriptors); +  return CaDescriptors->Empty() ? 0 : 1; +} + +int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag) +{ +  cMutexLock MutexLock(&mutex); +  for (cCaDescriptors *ca = First(); ca; ca = Next(ca)) { +      if (ca->Is(Source, Transponder, ServiceId)) +         return ca->GetCaDescriptors(CaSystemIds, BufSize, Data, StreamFlag); +      } +  return 0; +} + +cCaDescriptorHandler CaDescriptorHandler;  int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)  { -  return CaDescriptors.GetCaDescriptors(Source, Transponder, ServiceId, CaSystemIds, BufSize, Data, StreamFlag); +  return CaDescriptorHandler.GetCaDescriptors(Source, Transponder, ServiceId, CaSystemIds, BufSize, Data, StreamFlag);  }  // --- cPatFilter ------------------------------------------------------------ @@ -160,6 +232,7 @@ cPatFilter::cPatFilter(void)    pmtIndex = 0;    pmtPid = 0;    lastPmtScan = 0; +  numPmtEntries = 0;    Set(0x00, 0x00);  // PAT  } @@ -169,6 +242,28 @@ void cPatFilter::SetStatus(bool On)    pmtIndex = 0;    pmtPid = 0;    lastPmtScan = 0; +  numPmtEntries = 0; +} + +void cPatFilter::Trigger(void) +{ +  numPmtEntries = 0; +} + +bool cPatFilter::PmtVersionChanged(int PmtPid, int Version) +{ +  Version <<= 16; +  for (int i = 0; i < numPmtEntries; i++) { +      if ((pmtVersion[i] & 0x0000FFFF) == PmtPid) { +         bool Changed = (pmtVersion[i] & 0x00FF0000) != Version; +         if (Changed) +            pmtVersion[i] = PmtPid | Version; +         return Changed; +         } +      } +  if (numPmtEntries < MAXPMTENTRIES) +     pmtVersion[numPmtEntries++] = PmtPid | Version; +  return true;  }  void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) @@ -206,21 +301,78 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length       SI::PMT pmt(Data, false);       if (!pmt.CheckCRCAndParse())          return; -     SI::CaDescriptor *d; -     // Scan the common loop: -     for (SI::Loop::Iterator it; (d = (SI::CaDescriptor*)pmt.commonDescriptors.getNext(it, SI::CaDescriptorTag)); ) { -         CaDescriptors.NewCaDescriptor(Source(), Transponder(), pmt.getServiceId(), d, false); -         delete d; -         } -     // Scan the stream-specific loop: -     SI::PMT::Stream stream; -     for (SI::Loop::Iterator it; pmt.streamLoop.hasNext(it); ) { -         stream = pmt.streamLoop.getNext(it); -         for (SI::Loop::Iterator it; (d = (SI::CaDescriptor*)stream.streamDescriptors.getNext(it, SI::CaDescriptorTag)); ) { -             CaDescriptors.NewCaDescriptor(Source(), Transponder(), pmt.getServiceId(), d, true); -             delete d; -             } -         } +     if (!PmtVersionChanged(pmtPid, pmt.getVersionNumber())) { +        lastPmtScan = 0; // this triggers the next scan +        return; +        } +     if (!Channels.Lock(true, 10)) { +        numPmtEntries = 0; // to make sure we try again +        return; +        } +     cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), pmt.getServiceId()); +     if (Channel) { +        SI::CaDescriptor *d; +        cCaDescriptors *CaDescriptors = new cCaDescriptors(Channel->Source(), Channel->Transponder(), Channel->Sid()); +        // Scan the common loop: +        for (SI::Loop::Iterator it; (d = (SI::CaDescriptor*)pmt.commonDescriptors.getNext(it, SI::CaDescriptorTag)); ) { +            CaDescriptors->AddCaDescriptor(d, false); +            delete d; +            } +        // Scan the stream-specific loop: +        SI::PMT::Stream stream; +        int Vpid = 0; +        int Ppid = pmt.getPCRPid(); +        int Apids[MAXAPIDS] = { 0 }; +        int Dpids[MAXAPIDS] = { 0 }; +        int Tpid = 0; +        int NumApids = 0; +        int NumDpids = 0; +        for (SI::Loop::Iterator it; pmt.streamLoop.hasNext(it); ) { +            stream = pmt.streamLoop.getNext(it); +            switch (stream.getStreamType()) { +              case 1: // STREAMTYPE_11172_VIDEO +              case 2: // STREAMTYPE_13818_VIDEO +                      Vpid = stream.getPid(); +                      break; +              case 3: // STREAMTYPE_11172_AUDIO +              case 4: // STREAMTYPE_13818_AUDIO +                      { +                      if (NumApids < MAXAPIDS) +                         Apids[NumApids++] = stream.getPid(); +                      } +                      break; +              case 5: // STREAMTYPE_13818_PRIVATE +              case 6: // STREAMTYPE_13818_PES_PRIVATE +              //XXX case 8: // STREAMTYPE_13818_DSMCC +                      { +                      SI::Descriptor *d; +                      for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { +                          switch (d->getDescriptorTag()) { +                            case SI::AC3DescriptorTag: +                                 if (NumDpids < MAXAPIDS) +                                    Dpids[NumDpids++] = stream.getPid(); +                                 break; +                            case SI::TeletextDescriptorTag: +                                 Tpid = stream.getPid(); +                                 break; +                            default: ; +                            } +                          delete d; +                          } +                      } +                      break; +              //default: printf("PID: %5d %5d %2d %3d %3d\n", pmt.getServiceId(), stream.getPid(), stream.getStreamType(), pmt.getVersionNumber(), Channel->Number());//XXX +              } +            for (SI::Loop::Iterator it; (d = (SI::CaDescriptor*)stream.streamDescriptors.getNext(it, SI::CaDescriptorTag)); ) { +                CaDescriptors->AddCaDescriptor(d, true); +                delete d; +                } +            } +        Channel->SetPids(Vpid, Ppid, Apids[0], Apids[1], Dpids[0], Dpids[1], Tpid); +        Channel->SetCaIds(CaDescriptors->CaIds()); +        Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors)); +        }       lastPmtScan = 0; // this triggers the next scan +     Channels.Unlock();       }  } @@ -4,25 +4,30 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: pat.h 1.2 2003/12/24 10:08:22 kls Exp $ + * $Id: pat.h 1.3 2004/01/03 13:47:54 kls Exp $   */  #ifndef __PAT_H  #define __PAT_H  #include "filter.h" -#include "thread.h" + +#define MAXPMTENTRIES 64  class cPatFilter : public cFilter {  private:    time_t lastPmtScan;    int pmtIndex;    int pmtPid; +  int pmtVersion[MAXPMTENTRIES]; +  int numPmtEntries; +  bool PmtVersionChanged(int PmtPid, int Version);  protected:    virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);  public:    cPatFilter(void);    virtual void SetStatus(bool On); +  void Trigger(void);    };  int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag); @@ -0,0 +1,146 @@ +/* + * sdt.c: SDT section filter + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: sdt.c 1.1 2004/01/04 11:54:42 kls Exp $ + */ + +#include "sdt.h" +#include "channels.h" +#include "libsi/section.h" +#include "libsi/descriptor.h" + +// --- cSDT ------------------------------------------------------------------ + +class cSDT : public SI::SDT { +public: +  cSDT(int Source, int Transponder, uchar &lastSdtVersion, cPatFilter *PatFilter, const u_char *Data); +  }; + +cSDT::cSDT(int Source, int Transponder, uchar &lastSdtVersion, cPatFilter *PatFilter, const u_char *Data) +:SI::SDT(Data, false) +{ +  if (!CheckCRCAndParse()) +     return; + +  if (getVersionNumber() == lastSdtVersion) +     return; + +  if (!Channels.Lock(true, 10)) +     return; + +  lastSdtVersion = getVersionNumber(); + +  SI::SDT::Service SiSdtService; +  for (SI::Loop::Iterator it; serviceLoop.hasNext(it); ) { +      SiSdtService = serviceLoop.getNext(it); + +      cChannel *Channel = Channels.GetByChannelID(tChannelID(Source, getOriginalNetworkId(), getTransportStreamId(), SiSdtService.getServiceId())); +      if (!Channel) +         Channel = Channels.GetByChannelID(tChannelID(Source, 0, Transponder, SiSdtService.getServiceId())); + +      SI::Descriptor *d; +      for (SI::Loop::Iterator it2; (d = SiSdtService.serviceDescriptors.getNext(it2)); ) { +          switch (d->getDescriptorTag()) { +            case SI::ServiceDescriptorTag: { +                 SI::ServiceDescriptor *sd = (SI::ServiceDescriptor *)d; +                 switch (sd->getServiceType()) { +                   case 0x01: // digital television service +                   //XXX TODO case 0x02: // digital radio sound service +                   //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; +                        if (*NameBuf && *ShortNameBuf) { +                           *ps++ = ','; +                           strcpy(ps, NameBuf); +                           pn = ShortNameBuf; +                           } +                        if (Channel) { +                           Channel->SetId(getOriginalNetworkId(), getTransportStreamId(), SiSdtService.getServiceId()); +                           Channel->SetName(pn); +                           // Using SiSdtService.getFreeCaMode() is no good, because some +                           // tv stations set this flag even for non-encrypted channels :-( +                           // The special value 0xFFFF was supposed to mean "unknown encryption" +                           // and would have been overwritten with real CA values later: +                           // Channel->SetCa(SiSdtService.getFreeCaMode() ? 0xFFFF : 0); +                           } +                        else if (*pn) { +                           Channel = Channels.NewChannel(Source, Transponder, pn, getOriginalNetworkId(), getTransportStreamId(), SiSdtService.getServiceId()); +                           PatFilter->Trigger(); +                           } +                        } +                   } +                 } +                 break; +            // Using the CaIdentifierDescriptor is no good, because some tv stations +            // just don't use it. The actual CA values are collected in pat.c: +            /* +            case SI::CaIdentifierDescriptorTag: { +                 SI::CaIdentifierDescriptor *cid = (SI::CaIdentifierDescriptor *)d; +                 if (Channel) { +                    for (SI::Loop::Iterator it; cid->identifiers.hasNext(it); ) +                        Channel->SetCa(cid->identifiers.getNext(it)); +                    } +                 } +                 break; +            */ +            case SI::NVODReferenceDescriptorTag: { +                 SI::NVODReferenceDescriptor *nrd = (SI::NVODReferenceDescriptor *)d; +                 for (SI::Loop::Iterator it; nrd->serviceLoop.hasNext(it); ) { +                     SI::NVODReferenceDescriptor::Service Service = nrd->serviceLoop.getNext(it); +                     //printf(" %04X-%04X-%04X\n", Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId());//XXX TODO +                     } +                 } +                 break; +            default: ; +            } +          delete d; +          } +      } +  Channels.Unlock(); +} + + +// --- cSdtFilter ------------------------------------------------------------ + +cSdtFilter::cSdtFilter(cPatFilter *PatFilter) +{ +  lastSdtVersion = 0xFF; +  patFilter = PatFilter; +  Set(0x11, 0x42);  // SDT +} + +void cSdtFilter::SetStatus(bool On) +{ +  cFilter::SetStatus(On); +  lastSdtVersion = 0xFF; +} + +void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) +{ +  if (Source() && Transponder()) +     cSDT SDT(Source(), Transponder(), lastSdtVersion, patFilter, Data); +} @@ -0,0 +1,27 @@ +/* + * sdt.h: SDT section filter + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: sdt.h 1.1 2004/01/03 13:49:55 kls Exp $ + */ + +#ifndef __SDT_H +#define __SDT_H + +#include "filter.h" +#include "pat.h" + +class cSdtFilter : public cFilter { +private: +  uchar lastSdtVersion; +  cPatFilter *patFilter; +protected: +  virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length); +public: +  cSdtFilter(cPatFilter *PatFilter); +  virtual void SetStatus(bool On); +  }; + +#endif //__SDT_H @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: sections.c 1.1 2003/12/22 11:17:38 kls Exp $ + * $Id: sections.c 1.2 2004/01/03 12:54:01 kls Exp $   */  #include "sections.h" @@ -108,23 +108,25 @@ void cSectionHandler::Detach(cFilter *Filter)  void cSectionHandler::SetSource(int Source, int Transponder)  { +  Lock();    source = Source;    transponder = Transponder; +  Unlock();  }  void cSectionHandler::SetStatus(bool On)  { +  Lock();    if (on != On) { -     Lock();       statusCount++;       for (cFilter *fi = filters.First(); fi; fi = filters.Next(fi)) {           fi->SetStatus(false);           if (On)              fi->SetStatus(true);           } -     Unlock();       on = On;       } +  Unlock();  }  void cSectionHandler::Action(void) @@ -144,6 +146,9 @@ void cSectionHandler::Action(void)          Unlock();          if (poll(pfd, NumFilters, 1000) != 0) { +           bool DeviceHasLock = device->HasLock(); +           if (!DeviceHasLock) +              usleep(100000);             for (int i = 0; i < NumFilters; i++) {                 if (pfd[i].revents & POLLIN) {                    cFilterHandle *fh = NULL; @@ -158,6 +163,8 @@ void cSectionHandler::Action(void)                       // Read section data:                       unsigned char buf[4096]; // max. allowed size for any EIT section                       int r = safe_read(fh->handle, buf, sizeof(buf)); +                     if (!DeviceHasLock) +                        continue; // we do the read anyway, to flush any data that might have come from a different transponder                       if (r > 3) { // minimum number of bytes necessary to get section length                          int len = (((buf[1] & 0x0F) << 8) | (buf[2] & 0xFF)) + 3;                          if (len == r) { @@ -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.56 2003/12/21 13:37:10 kls Exp $ + * $Id: svdrp.c 1.57 2003/12/28 10:09:30 kls Exp $   */  #include "svdrp.h" @@ -476,7 +476,7 @@ void cSVDRP::CmdDELC(const char *Option)                 }             Channels.Del(channel);             Channels.ReNumber(); -           Channels.Save(); +           Channels.SetModified();             isyslog("channel %s deleted", Option);             Reply(250, "Channel \"%s\" deleted", Option);             } @@ -810,9 +810,8 @@ void cSVDRP::CmdMODC(const char *Option)                if (Channels.HasUniqueChannelID(&ch, channel)) {                   *channel = ch;                   Channels.ReNumber(); -                 Channels.Save(); +                 Channels.SetModified();                   isyslog("modifed channel %d %s", channel->Number(), channel->ToText()); -                 Timers.Save();                   Reply(250, "%d %s", channel->Number(), channel->ToText());                   }                else @@ -886,7 +885,7 @@ void cSVDRP::CmdNEWC(const char *Option)             *channel = ch;             Channels.Add(channel);             Channels.ReNumber(); -           Channels.Save(); +           Channels.SetModified();             isyslog("new channel %d %s", channel->Number(), channel->ToText());             Reply(250, "%d %s", channel->Number(), channel->ToText());             } @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: thread.c 1.29 2003/12/21 15:17:24 kls Exp $ + * $Id: thread.c 1.30 2004/01/03 16:59:33 kls Exp $   */  #include "thread.h" @@ -80,20 +80,20 @@ void cCondVar::Signal(void)  }  */ -// --- cRWlock --------------------------------------------------------------- +// --- cRwLock --------------------------------------------------------------- -cRWlock::cRWlock(bool PreferWriter) +cRwLock::cRwLock(bool PreferWriter)  {    pthread_rwlockattr_t attr = { PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP };    pthread_rwlock_init(&rwlock, &attr);  } -cRWlock::~cRWlock() +cRwLock::~cRwLock()  {    pthread_rwlock_destroy(&rwlock);  } -bool cRWlock::Lock(bool Write, int TimeoutMs) +bool cRwLock::Lock(bool Write, int TimeoutMs)  {    int Result = 0;    struct timespec abstime; @@ -108,7 +108,7 @@ bool cRWlock::Lock(bool Write, int TimeoutMs)    return Result == 0;  } -void cRWlock::Unlock(void) +void cRwLock::Unlock(void)  {    pthread_rwlock_unlock(&rwlock);  } @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: thread.h 1.19 2003/12/21 15:44:31 kls Exp $ + * $Id: thread.h 1.20 2004/01/03 16:58:50 kls Exp $   */  #ifndef __THREAD_H @@ -28,12 +28,12 @@ public:    //void Signal(void);    }; -class cRWlock { +class cRwLock {  private:    pthread_rwlock_t rwlock;  public: -  cRWlock(bool PreferWriter = false); -  ~cRWlock(); +  cRwLock(bool PreferWriter = false); +  ~cRwLock();    bool Lock(bool Write, int TimeoutMs = 0);    void Unlock(void);    }; @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: timers.c 1.7 2003/12/13 13:06:29 kls Exp $ + * $Id: timers.c 1.8 2003/12/27 13:10:04 kls Exp $   */  #include "timers.h" @@ -216,8 +216,10 @@ bool cTimer::Parse(const char *s)       strn0cpy(file, filebuffer, MaxFileName);       strreplace(file, '|', ':');       strreplace(summary, '|', '\n'); -     tChannelID cid = tChannelID::FromString(channelbuffer); -     channel = cid.Valid() ? Channels.GetByChannelID(cid, true) : Channels.GetByNumber(atoi(channelbuffer)); +     if (isnumber(channelbuffer)) +        channel = Channels.GetByNumber(atoi(channelbuffer)); +     else +        channel = Channels.GetByChannelID(tChannelID::FromString(channelbuffer), true);       if (!channel) {          esyslog("ERROR: channel %s not defined", channelbuffer);          result = false; @@ -8,7 +8,7 @@  .\" License as specified in the file COPYING that comes with the  .\" vdr distribution.  .\" -.\" $Id: vdr.5 1.20 2003/05/29 11:58:57 kls Exp $ +.\" $Id: vdr.5 1.21 2004/01/02 15:24:21 kls Exp $  .\"  .TH vdr 5 "1 Jun 2003" "1.2.0" "Video Disk Recorder Files"  .SH NAME @@ -45,7 +45,7 @@ Such a delimiter will not appear in the Channels menu.  A \fBchannel definition\fR is a line with channel data, where the fields  are separated by ':' characters. Example: -\fBRTL:12188:h:S19.2E:27500:163:104:105:0:12003:0:0:0\fR +\fBRTL,RTL Television:12188:h:S19.2E:27500:163:104:105:0:12003:1:1089:0\fR  The line number of a channel definition (not counting group separators,  and based on a possible previous '@...' parameter) @@ -57,6 +57,13 @@ to right):  .B Name  The channel's name (if the name originally contains a ':' character  it has to be replaced by '|'). +Some tv stations provide a way of deriving a "short name" from the +channel name, which can be used in situations where there is not +much space for displaying a long name. If a short name is available +for this channel, it preceeds the full name and is delimited by a comma, +as in + +\fBRTL,RTL Television:...\fR  .TP  .B Frequency  The transponder frequency (as an integer). For DVB-S this value is in MHz. For DVB-C @@ -119,23 +126,33 @@ the audio PIDs, separated by a semicolon, as in  The teletext PID.  .TP  .B Conditional access -An integer defining how this channel can be accessed: +A hexadecimal integer defining how this channel can be accessed:  .TS  tab (@);  l l. -\fB0\fR@Free To Air -\fB1...4\fR@explicitly requires the DVB card with the given number -\fB>=100\fR@requires a specific decryption method defined in \fIca.conf\fR +\fB0000\fR@Free To Air +\fB0001...000F\fR@explicitly requires the device with the given number +\fB0010...00FF\fR@reserved for user defined assignments defined in \fIca.conf\fR +\fB0100...FFFF\fR@specific decryption methods as broadcast in the data stream\fR  .TE +Values in the range 0001...00FF will not be overwritten, all other values +will be automatically replaced by the actual CA system identifiers received +from the data stream. If there is more than one CA system id broadcast, they +will be separated by commas, as in + +.B ...:1702,1722,1801:... + +The values are in hex because that's the way they are defined in the "ETR 162" +document. Leading zeros may be omitted.  .TP  .B SID  The Service ID of this channel.  .TP  .B NID -The Network ID of this channel (for future use, currently always 0). +The Network ID of this channel.  .TP  .B TID -The Transport stream ID of this channel (for future use, currently always 0). +The Transport stream ID of this channel.  .TP  .B RID  The Radio ID of this channel (typically 0, may be used to distinguish channels where @@ -144,12 +161,12 @@ NID, TID and SID are all equal).  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-0\fR +\fBS19.2E-1-1089-12003-0\fR -The components of this string are the \fBSource\fR (S19.2E), \fBFrequency\fR -(12188, MHz) and \fBSID\fR (12003) as defined above. The parts that are currently -\fB0\fR are reserved for future use (the last part can be omitted if it is \fB0\fR, -so the above example could also be written as \fBS19.2E-0-12188-12003\fR). +The components of this string are the \fBSource\fR (S19.2E), \fBNID\fR +(1), \fBTID\fR (1089), \fBSID\fR (12003) and \fBRID\fR (0) as defined above. +The last part can be omitted if it is \fB0\fR, +so the above example could also be written as S19.2E-1-1089-12003).  .br  The \fBchannel\ ID\fR is used in the \fItimers.conf\fR and \fIepg.data\fR  files to properly identify the channels. @@ -22,7 +22,7 @@   *   * The project's page is at http://www.cadsoft.de/vdr   * - * $Id: vdr.c 1.171 2003/12/22 13:29:24 kls Exp $ + * $Id: vdr.c 1.172 2004/01/04 11:12:05 kls Exp $   */  #include <getopt.h> @@ -511,6 +511,25 @@ int main(int argc, char *argv[])                dsyslog("max. latency time %d seconds", MaxLatencyTime);                }             } +        // Handle channel modifications: +        if (!Channels.BeingEdited() && Channels.Modified()) { +           if (Channels.Lock(false, 100)) { +              Channels.Save(); //XXX only after user changes??? +              Timers.Save(); +              for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { +                  if (Channel && Channel->Modification(CHANNELMOD_RETUNE)) { +                     cRecordControls::ChannelDataModified(Channel); +                     if (Channel->Number() == cDevice::CurrentChannel()) { +                        if (!cDevice::PrimaryDevice()->Replaying()) { +                           isyslog("retuning due to modification of channel %d", Channel->Number()); +                           Channels.SwitchTo(Channel->Number()); +                           } +                        } +                     } +                  } +              Channels.Unlock(); +              } +           }          // Channel display:          if (!EITScanner.Active() && cDevice::CurrentChannel() != LastChannel) {             if (!Menu) | 
