Alternative-Channel patch

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi folks,

this patch is relevant for the following people:

People receiving the same tv program on different channels (DVB-C and
analog via pvrinput, or HD and SD cards, or DVB-C and DVB-T, and ...)

Here is my use-case:
I have a dvb-c and some pvrinput devices.
If the dvb-c card is occupied by a recording, i have to switch to a
pvrinput channel.

An example:

My channels are configured as follows:

1 - RTL (DVB-C)
2 - Sat1 (DVB-C)
3 - RTL2 (DVB-C)
...
101 RTL (pvrinput)
102 Sat1 (pvrinput)
103 RTL2 (pvrinput)

The channel 1 and 101, 2 and 102, 3 and 103 are receiving the same programme.

In case the DVB-C device is occupied, i have to switch to the channels
101, 102, 103...

The DVB-C device can be occupied by a timer, streamdev and so on.

With the patch you can configure:
101 is an alternative for 1
102 is an alternative for 2
...

If you have configured this, vdr behaves as follow:
when watching channel 1 and the dvb-c card also receives channel 1:

when pressing CH+, then the vdr switches to channel 2. Channel 2
cannot be received by the dvb-c device, because 1 and 2 are not in the
same "stream":
vdr displays the epg data of channel 2, but showing the pictures and
sound of channel 102. The ChannelControl-OSD shows: 2 (102) Sat1. The
first number is the channel selected, the second number (in
parenthese) the channel receiving.

When selecting the channel 2 by pressing the "2"-Key, the same happends.

When a timer is starting on channel 2, but the dvb-c device is
occupied, then channel 102 gets recorded.

How does this patch works ?:

Add your alternatives under menu -> Settings -> Alternative

For live view:
a device gets searched for the given channel. It doesn't matter
whether receivers have to be detached or not.
if no device is found, then a device for the alternative channels gets
searched. It also doesn't matter whether receivers have to be detached
or not.

if still no device could be found, the channel is not available

For non-live-view:

a device gets searched for the given channel. Only devices where no
receiver has to be detached are taken into account.
if no device is found, then a device for the alternative channels gets
searched. Only devices where no receiver has to be detached are taken
into account.
if no device is found, then a device gets searched for the given
channel. It doesn't matter whether receivers have to be detached or
not.
if no device is found, then a device for the alternative channels gets
searched. It also doesn't matter whether receivers have to be detached
or not.

Why the difference for live tv and non-live tv ?: With live tv there
are receivers which can be detached at any time (like osdteletext).
Live-tv has normally the least priority, so live tv can on push live
tv aside. If in the first step live view isn't allowed to detach other
receivers, an alternative will be taken every second channel switch.

What about streamdev ?:

I have tested streamdev only a little bit. The patch has to be applied
on the client. In my test (only with "never suspend") it works.

A note about pvrinput:
If you use pvrinput with this patch, you also have to patch pvrinput.
Otherwise pvrinput devices always returns the need for detaching other
receivers.

Because of lack of a ff-card and a cam module, i could test it only in
transfer mode and without any encryption => testers are welcome.

I havn't add any translations yet. If the patch gets merged into the
development branch, i will provide the translations at least for
german and english.


Regards, Rainer
diff --git a/device.c b/device.c
index 3e4d5c4..699a2b7 100755
--- a/device.c
+++ b/device.c
@@ -979,7 +979,7 @@ bool cPvrDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
 {
   bool result = false;
   bool hasPriority = Priority < 0 || Priority > this->Priority();
-  bool needsDetachReceivers = true;
+  bool needsDetachReceivers = Receiving(true);
   int input, LinesPerFrame, dev;
   uint64_t norm;
   eInputType inputType; //only dummy
diff --git a/PLUGINS/src/skincurses/skincurses.c b/PLUGINS/src/skincurses/skincurses.c
index 4abb863..65249df 100644
--- a/PLUGINS/src/skincurses/skincurses.c
+++ b/PLUGINS/src/skincurses/skincurses.c
@@ -194,7 +194,7 @@ private:
 public:
   cSkinCursesDisplayChannel(bool WithInfo);
   virtual ~cSkinCursesDisplayChannel();
-  virtual void SetChannel(const cChannel *Channel, int Number);
+  virtual void SetChannel(cChannelConfig channelConfig, int Number);
   virtual void SetEvents(const cEvent *Present, const cEvent *Following);
   virtual void SetMessage(eMessageType Type, const char *Text);
   virtual void Flush(void);
@@ -214,10 +214,10 @@ cSkinCursesDisplayChannel::~cSkinCursesDisplayChannel()
   delete osd;
 }
 
-void cSkinCursesDisplayChannel::SetChannel(const cChannel *Channel, int Number)
+void cSkinCursesDisplayChannel::SetChannel(cChannelConfig channelConfig, int Number)
 {
   osd->DrawRectangle(0, 0, ScOsdWidth - 1, 0, clrBackground);
-  osd->DrawText(0, 0, ChannelString(Channel, Number), clrWhite, clrBackground, &Font);
+  osd->DrawText(0, 0, ChannelString(channelConfig, Number), clrWhite, clrBackground, &Font);
 }
 
 void cSkinCursesDisplayChannel::SetEvents(const cEvent *Present, const cEvent *Following)
diff --git a/channels.c b/channels.c
index ebc7eb1..74695d1 100644
--- a/channels.c
+++ b/channels.c
@@ -13,6 +13,7 @@
 #include "epg.h"
 #include "libsi/si.h"
 #include "timers.h"
+#include "sources.h"
 
 // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
 // format characters in order to allow any number of blanks after a numeric
@@ -697,6 +698,12 @@ bool cChannel::Save(FILE *f)
   return fprintf(f, "%s", *ToText()) > 0;
 }
 
+bool cChannel::IsAtsc(void) const { return cSource::IsAtsc(source); }
+bool cChannel::IsCable(void) const { return cSource::IsCable(source); }
+bool cChannel::IsSat(void) const { return cSource::IsSat(source); }
+bool cChannel::IsTerr(void) const { return cSource::IsTerr(source); }
+bool cChannel::IsSourceType(char Source) const { return cSource::IsType(source, Source); }
+
 // --- cChannelSorter --------------------------------------------------------
 
 class cChannelSorter : public cListObject {
@@ -924,14 +931,20 @@ cChannel *cChannels::NewChannel(const cChannel *Transponder, const char *Name, c
   return NULL;
 }
 
-cString ChannelString(const cChannel *Channel, int Number)
+cString ChannelString(cChannelConfig cc, int Number)
 {
   char buffer[256];
-  if (Channel) {
-     if (Channel->GroupSep())
-        snprintf(buffer, sizeof(buffer), "%s", Channel->Name());
-     else
-        snprintf(buffer, sizeof(buffer), "%d%s  %s", Channel->Number(), Number ? "-" : "", Channel->Name());
+  const cChannel *selected = Channels.Get(cc.selected()-1);
+  const cChannel *receiving = Channels.Get(cc.receiving()-1);
+  if (selected) {
+     if (selected->GroupSep())
+        snprintf(buffer, sizeof(buffer), "%s", selected->Name());
+     else {
+        if (selected == receiving || receiving == NULL)
+           snprintf(buffer, sizeof(buffer), "%d%s  %s", selected->Number(), Number ? "-" : "", selected->Name());
+        else
+           snprintf(buffer, sizeof(buffer), "%d(%d)%s  %s", selected->Number(), receiving->Number(), Number ? "-" : "", selected->Name());
+        }
      }
   else if (Number)
      snprintf(buffer, sizeof(buffer), "%d-", Number);
diff --git a/channels.h b/channels.h
index b867297..4a0ef83 100644
--- a/channels.h
+++ b/channels.h
@@ -10,10 +10,9 @@
 #ifndef __CHANNELS_H
 #define __CHANNELS_H
 
-#include "config.h"
-#include "sources.h"
 #include "thread.h"
 #include "tools.h"
+#include "config.h"
 
 #define ISTRANSPONDER(f1, f2)  (abs((f1) - (f2)) < 4) //XXX
 
@@ -176,11 +175,11 @@ public:
   const char *Parameters(void) const { return parameters; }
   const cLinkChannels* LinkChannels(void) const { return linkChannels; }
   const cChannel *RefChannel(void) const { return refChannel; }
-  bool IsAtsc(void) const { return cSource::IsAtsc(source); }
-  bool IsCable(void) const { return cSource::IsCable(source); }
-  bool IsSat(void) const { return cSource::IsSat(source); }
-  bool IsTerr(void) const { return cSource::IsTerr(source); }
-  bool IsSourceType(char Source) const { return cSource::IsType(source, Source); }
+  bool IsAtsc(void) const;
+  bool IsCable(void) const;
+  bool IsSat(void) const;
+  bool IsTerr(void) const;
+  bool IsSourceType(char Source) const;
   tChannelID GetChannelID(void) const { return tChannelID(source, nid, (nid || tid) ? tid : Transponder(), sid, rid); }
   bool HasTimer(void) const;
   int Modification(int Mask = CHANNELMOD_ALL);
@@ -234,6 +233,18 @@ public:
 
 extern cChannels Channels;
 
-cString ChannelString(const cChannel *Channel, int Number);
+class cChannelConfig {
+private:
+   const int selectedChannel;
+   const int receivingChannel;
+public:
+   cChannelConfig(
+         int selectedChannel, int receivingChannel) :
+         selectedChannel(selectedChannel), receivingChannel(receivingChannel) { }
+   int selected() {return selectedChannel;}
+   int receiving() {return receivingChannel;}
+};
+
+cString ChannelString(cChannelConfig cc, int Number);
 
 #endif //__CHANNELS_H
diff --git a/config.c b/config.c
index 73b11b1..aa8761a 100644
--- a/config.c
+++ b/config.c
@@ -15,6 +15,7 @@
 #include "interface.h"
 #include "plugin.h"
 #include "recording.h"
+#include "sources.h"
 
 // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
 // format characters in order to allow any number of blanks after a numeric
@@ -304,6 +305,228 @@ bool cSetupLine::Save(FILE *f)
   return fprintf(f, "%s%s%s = %s\n", plugin ? plugin : "", plugin ? "." : "", name, value) > 0;
 }
 
+// -- Class cAlternative ------------------------------------------------------------
+
+cAlternative::cAlternative(const cChannel *master) : master(master), alternativeChannels() {
+}
+
+void cAlternative::AddAlternativeChannel(const cChannel *Alternative) {
+   if (Alternative == master) return; // master cannot be an alternative
+   cAlternativeContainer *next = alternativeChannels.First();
+   while(next) {
+      if (next->channel == Alternative) return; // Already in list
+      if (Alternative->Number() < next->channel->Number()) {
+         alternativeChannels.Ins(new cAlternativeContainer(Alternative), next);
+         return;
+         }
+      next = alternativeChannels.Next(next);
+   }
+   alternativeChannels.Add(new cAlternativeContainer(Alternative));
+}
+
+void cAlternative::RemoveAlternativeChannel(const cChannel *Alternative) {
+   cAlternativeContainer *next = alternativeChannels.First();
+   while(next) {
+      if (next->channel == Alternative) {
+         alternativeChannels.Del(next, true);
+         return;
+         }
+      next = alternativeChannels.Next(next);
+      }
+}
+
+cList<cAlternativeContainer>& cAlternative::GetAlternativeChannels() {
+
+   return alternativeChannels;
+}
+
+bool cAlternative::operator<(const cAlternative& Other) {
+   if (master->Number() < Other.master->Number())
+      return -1;
+   if (master->Number() > Other.master->Number())
+      return 1;
+   return 0;
+}
+
+const cChannel *cAlternative::GetMaster() {
+   return master;
+}
+
+cAlternative::cAlternative(cAlternative *AlternativeToCopy) {
+   master = AlternativeToCopy->master;
+   alternativeChannels.Clear();
+   cAlternativeContainer *next = AlternativeToCopy->alternativeChannels.First();
+   while(next) {
+      alternativeChannels.Add(new cAlternativeContainer(next->channel));
+      next = AlternativeToCopy->alternativeChannels.Next(next);
+      }
+}
+
+cAlternative& cAlternative::operator= (const cAlternative &Other) {
+   master = Other.master;
+   alternativeChannels.Clear();
+   cAlternativeContainer *next = Other.alternativeChannels.First();
+   while(next) {
+      alternativeChannels.Add(new cAlternativeContainer(next->channel));
+      next = Other.alternativeChannels.Next(next);
+      }
+   return *this;
+}
+
+bool cAlternative::operator==(const cAlternative &Other) {
+   if (master != Other.master) return false;
+   cAlternativeContainer *thisNext = alternativeChannels.First();
+   cAlternativeContainer *otherNext = Other.alternativeChannels.First();
+   while(thisNext != NULL && otherNext != NULL) {
+      if (thisNext == NULL || otherNext == NULL) return false;
+      if (thisNext->channel != otherNext->channel) return false;
+      thisNext = alternativeChannels.Next(thisNext);
+      otherNext = Other.alternativeChannels.Next(otherNext);
+      }
+   return true;
+}
+
+
+cAlternative::~cAlternative() {
+}
+
+void cAlternative::Del(const cChannel *Channel) {
+   for(cAlternativeContainer * next = alternativeChannels.First() ; next ; next = alternativeChannels.Next(next)) {
+      if (next->channel == Channel) {
+         alternativeChannels.Del(next);
+         return;
+      }
+   }
+}
+
+
+////////////////////////////////////////////////////
+// Class: cAllAlternatives
+
+cAlternative *cAllAlternatives::FindAlternative(const cChannel *Master) {
+   Init();
+   for(cAlternative *next = alternatives.First() ; next ; next = alternatives.Next(next)) {
+      if (next->GetMaster() == Master) {
+         return next;
+      }
+   }
+   return NULL;
+}
+
+void cAllAlternatives::CreateAlternative(const cChannel *Master) {
+   Init();
+   if (FindAlternative(Master) == NULL) {
+      cAlternative *ptr = new cAlternative(Master);
+      for(cAlternative *next = alternatives.First() ; next ; next = alternatives.Next(next)) {
+         if (Master->Number() < next->GetMaster()->Number()) {
+            alternatives.Ins(ptr, next);
+            ptr = NULL;
+            break;
+            }
+         }
+      if (ptr) alternatives.Add(ptr);
+      }
+}
+
+void cAllAlternatives::RemoveAlternative(cAlternative* AlternativeToRemove) {
+   Init();
+   for(cAlternative *next = alternatives.First() ; next ; next = alternatives.Next(next)) {
+      if (next == AlternativeToRemove) {
+         alternatives.Del(next, true /*delete*/);
+         return;
+      }
+   }
+}
+
+int cAllAlternatives::CountAlternatives() {
+   Init();
+   return alternatives.Count();
+}
+
+
+cAllAlternatives::~cAllAlternatives() {
+   alternatives.Clear();
+   masterToAlternative.Clear();
+}
+
+cAlternative *cAllAlternatives::GetAlternative(int Index) {
+   Init();
+   cAlternative *next = alternatives.First();
+   while(Index > 0 || next == NULL) {
+      next = alternatives.Next(next);
+      Index--;
+   }
+   return next;
+}
+
+void cAllAlternatives::AddAlternative(const cChannel *Master, const cChannel *AlternativeToAdd) {
+   Init();
+   cAlternative *alt = FindAlternative(Master);
+   if (alt == NULL) {
+      CreateAlternative(Master);
+      alt = FindAlternative(Master);
+   }
+   alt->AddAlternativeChannel(AlternativeToAdd);
+}
+
+void cAllAlternatives::LoadAlternative(int MasterNr, int AlternativeNr) {
+   masterToAlternative.Ins(new cIntPair(MasterNr, AlternativeNr));
+}
+
+void cAllAlternatives::Init() {
+  if (!init) {
+     init = true;
+     for(cIntPair *next = masterToAlternative.First() ; next ; next = masterToAlternative.Next(next)) {
+        const cChannel *master = Channels.Get(next->int1-1);
+        const cChannel *alternative = Channels.Get(next->int2-1);
+        if (master == NULL) continue;
+        if (alternative == NULL) continue;
+        AddAlternative(master, alternative);
+        }
+    }
+}
+
+cAllAlternatives& cAllAlternatives::operator= (const cAllAlternatives &Other) {
+   init = Other.init;
+   masterToAlternative.Clear();
+   for(cIntPair *next = Other.masterToAlternative.First() ; next ; next = Other.masterToAlternative.Next(next)) {
+      masterToAlternative.Add(new cIntPair(*next));
+      }
+   alternatives.Clear();
+   for(cAlternative *next = Other.alternatives.First() ; next ; next = Other.alternatives.Next(next)) {
+      alternatives.Add(new cAlternative(next));
+      }
+   return *this;
+}
+
+bool cAllAlternatives::operator==(cAllAlternatives &Other) {
+   Init();
+   Other.Init();
+   cAlternative *thisNext = alternatives.First();
+   cAlternative *otherNext = Other.alternatives.First();
+   while(thisNext != NULL && otherNext != NULL) {
+      if (thisNext == NULL || otherNext == NULL) return false;
+      if (*thisNext != *otherNext) return false;
+      thisNext = alternatives.Next(thisNext);
+      otherNext = Other.alternatives.Next(otherNext);
+      }
+   return true;
+}
+
+
+void cAllAlternatives::Del(const cChannel *Channel) {
+   for(cAlternative *next = alternatives.First() ; next ; next = alternatives.Next(next)) {
+      if (next->GetMaster() == Channel) {
+         alternatives.Del(next);
+         break;
+         }
+      }
+   for(cAlternative *next = alternatives.First() ; next ; next = alternatives.Next(next)) {
+      next->Del(Channel);
+      }
+}
+
+
 // --- cSetup ----------------------------------------------------------------
 
 cSetup Setup;
@@ -404,6 +627,7 @@ cSetup::cSetup(void)
 cSetup& cSetup::operator= (const cSetup &s)
 {
   memcpy(&__BeginData__, &s.__BeginData__, (char *)&s.__EndData__ - (char *)&s.__BeginData__);
+  Alternatives = s.Alternatives;
   return *this;
 }
 
@@ -423,7 +647,7 @@ void cSetup::Store(const char *Name, const char *Value, const char *Plugin, bool
   if (Name && *Name) {
      cSetupLine *l = Get(Name, Plugin);
      if (l && !AllowMultiple)
-        Del(l);
+        this->cList<cSetupLine>::Del(l);
      if (Value)
         Add(new cSetupLine(Name, Value, Plugin));
      }
@@ -590,11 +814,23 @@ bool cSetup::Parse(const char *Name, const char *Value)
   else if (!strcasecmp(Name, "InitialVolume"))       InitialVolume      = atoi(Value);
   else if (!strcasecmp(Name, "ChannelsWrap"))        ChannelsWrap       = atoi(Value);
   else if (!strcasecmp(Name, "EmergencyExit"))       EmergencyExit      = atoi(Value);
+  else if (strstr(Name, "master:"))          return LoadAlternative(Name, Value);
   else
      return false;
   return true;
 }
 
+bool cSetup::LoadAlternative(const char *Name, const char *Value) {
+   Name += strlen("master:");
+   char helper[128];
+   strcpy(helper, Name);
+   helper[strlen(helper)-4] = 0;
+   int masterNr = atoi(helper);
+   int alternativeNr = atoi(Value);
+   Alternatives.LoadAlternative(masterNr, alternativeNr);
+   return true;
+}
+
 bool cSetup::Save(void)
 {
   Store("OSDLanguage",        OSDLanguage);
@@ -686,7 +922,7 @@ bool cSetup::Save(void)
   Store("InitialVolume",      InitialVolume);
   Store("ChannelsWrap",       ChannelsWrap);
   Store("EmergencyExit",      EmergencyExit);
-
+  SaveAlternatives();
   Sort();
 
   if (cConfig<cSetupLine>::Save()) {
@@ -695,3 +931,48 @@ bool cSetup::Save(void)
      }
   return false;
 }
+
+void cSetup::SaveAlternatives() {
+   // Remove old alternatives or otherwise they will stay forever
+   for(cSetupLine *line = First() ; line ; line = Next(line)) {
+      if (strstr(line->Name(), "master:") == line->Name()) {
+         if (line->Prev() != NULL) {
+            cSetupLine *temp = line;
+            line = Prev(line);
+            this->cList<cSetupLine>::Del(temp);
+         } else {
+            this->cList<cSetupLine>::Del(line);
+            line = First();
+         }
+      }
+   }
+   int counter = 1000;
+   for(int i = 0 ; i < Alternatives.CountAlternatives() ; i++) {
+      cAlternative *alt = Alternatives.GetAlternative(i);
+      cList<cAlternativeContainer> &alternatives = alt->GetAlternativeChannels();
+
+      const cChannel *master = alt->GetMaster();
+
+      for(cAlternativeContainer *next = alternatives.First(); next ; next = alternatives.Next(next)) {
+         cString key = cString::sprintf("master:%d%d", master->Number(), counter);
+         Store(*key, next->channel->Number());
+         counter++;
+         }
+      }
+}
+
+cAllAlternatives& cSetup::getAllAlternatives() {
+   return Alternatives;
+}
+
+cList<cAlternativeContainer>& cSetup::findAlternativeChannels(const cChannel* channel) {
+   cAlternative *alt = Alternatives.FindAlternative(channel);
+   if (alt) {
+      return alt->GetAlternativeChannels();
+   }
+   return nullAlternatives;
+}
+
+void cSetup::Del(const cChannel* Channel) {
+  Alternatives.Del(Channel);
+}
diff --git a/config.h b/config.h
index 7c28d41..f4cbd76 100644
--- a/config.h
+++ b/config.h
@@ -7,49 +7,18 @@
  * $Id: config.h 2.28 2010/09/12 11:31:21 kls Exp $
  */
 
-#ifndef __CONFIG_H
-#define __CONFIG_H
+#ifndef __CCONFIG_H
+#define __CCONFIG_H
 
 #include <arpa/inet.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <time.h>
 #include <unistd.h>
 #include "i18n.h"
 #include "font.h"
 #include "tools.h"
 
-// VDR's own version number:
-
-#define VDRVERSION  "1.7.16"
-#define VDRVERSNUM   10716  // Version * 10000 + Major * 100 + Minor
-
-// The plugin API's version number:
-
-#define APIVERSION  "1.7.16"
-#define APIVERSNUM   10716  // Version * 10000 + Major * 100 + Minor
-
-// When loading plugins, VDR searches them by their APIVERSION, which
-// may be smaller than VDRVERSION in case there have been no changes to
-// VDR header files since the last APIVERSION. This allows compiled
-// plugins to work with newer versions of the core VDR as long as no
-// VDR header files have changed.
-
-#define MAXPRIORITY 99
-#define MAXLIFETIME 99
-
-#define MINOSDWIDTH   480
-#define MAXOSDWIDTH  1920
-#define MINOSDHEIGHT  324
-#define MAXOSDHEIGHT 1200
-
-#define MaxFileName 256
-#define MaxSkinName 16
-#define MaxThemeName 16
-
-typedef uint32_t in_addr_t; //XXX from /usr/include/netinet/in.h (apparently this is not defined on systems with glibc < 2.2)
-
 class cSVDRPhost : public cListObject {
 private:
   struct in_addr addr;
@@ -144,6 +113,44 @@ public:
   }
   };
 
+
+#endif //__CCONFIG_H
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+#include "channels.h"
+
+// VDR's own version number:
+
+#define VDRVERSION  "1.7.16"
+#define VDRVERSNUM   10716  // Version * 10000 + Major * 100 + Minor
+
+// The plugin API's version number:
+
+#define APIVERSION  "1.7.16"
+#define APIVERSNUM   10716  // Version * 10000 + Major * 100 + Minor
+
+// When loading plugins, VDR searches them by their APIVERSION, which
+// may be smaller than VDRVERSION in case there have been no changes to
+// VDR header files since the last APIVERSION. This allows compiled
+// plugins to work with newer versions of the core VDR as long as no
+// VDR header files have changed.
+
+#define MAXPRIORITY 99
+#define MAXLIFETIME 99
+
+#define MINOSDWIDTH   480
+#define MAXOSDWIDTH  1920
+#define MINOSDHEIGHT  324
+#define MAXOSDHEIGHT 1200
+
+#define MaxFileName 256
+#define MaxSkinName 16
+#define MaxThemeName 16
+
+typedef uint32_t in_addr_t; //XXX from /usr/include/netinet/in.h (apparently this is not defined on systems with glibc < 2.2)
+
 class cNestedItem : public cListObject {
 private:
   char *text;
@@ -200,6 +207,70 @@ public:
   bool Save(FILE *f);
   };
 
+class cChannel;
+
+// -- Class cAlternativeContainer
+
+class cAlternativeContainer : public cListObject {
+public:
+   const cChannel *channel;
+   cAlternativeContainer(const cChannel *channel) : channel(channel) { };
+   cAlternativeContainer(const cAlternativeContainer& ref) {*this=ref;};
+   cAlternativeContainer& operator=(const cAlternativeContainer& ref) {channel = ref.channel;return *this;};
+};
+
+class cAlternative : public cListObject {
+private:
+   const cChannel *master;
+   cList<cAlternativeContainer> alternativeChannels;
+public:
+   cAlternative(const cChannel *master);
+   cAlternative(cAlternative &other) { *this = other;};
+   cAlternative(cAlternative *other);
+   void AddAlternativeChannel(const cChannel *Alternative);
+   void RemoveAlternativeChannel(const cChannel *alternative);
+   cList<cAlternativeContainer>& GetAlternativeChannels();
+   // < Returns a reference the alternative channel cList
+   const cChannel* GetMaster();
+   bool operator<(const cAlternative& other);
+   cAlternative& operator= (const cAlternative &s);
+   bool operator==(const cAlternative &Other);
+   bool operator!=(const cAlternative &Other) { return !(*this == Other);}
+   virtual ~cAlternative();
+   void Del(const cChannel *Channel);
+};
+
+class cIntPair : public cListObject {
+public:
+   int int1, int2;
+   cIntPair(int int1, int int2) : int1(int1), int2(int2) { }
+};
+
+class cAllAlternatives {
+   friend class cSetup;
+private:
+   cList<cAlternative> alternatives;
+   cList<cIntPair> masterToAlternative;
+   // Cache because when parsing the configuration the channels are not parsed yet.
+   void Init();
+   bool init;
+   cAlternative *FindAlternative(const cChannel *Master);
+public:
+   cAlternative *GetAlternative(int Index); // zero-based index
+   void CreateAlternative(const cChannel *Mmaster);
+   void RemoveAlternative(cAlternative *Alternative);
+   virtual ~cAllAlternatives();
+   int CountAlternatives();
+   void AddAlternative(const cChannel *Master, const cChannel *AlternativeToAdd);
+   cAllAlternatives() : init(false) { };
+   cAllAlternatives(cAllAlternatives &Other) { *this = Other;};
+   void LoadAlternative(int MasterNr, int AlternativeNr);
+   cAllAlternatives& operator= (const cAllAlternatives &Other);
+   bool operator==(cAllAlternatives &Other);
+   bool operator!=(cAllAlternatives &Other) { return !(*this == Other);}
+   void Del(const cChannel *Channel);
+};
+
 class cSetup : public cConfig<cSetupLine> {
   friend class cPlugin; // needs to be able to call Store()
 private:
@@ -210,7 +281,9 @@ private:
   void Store(const char *Name, const char *Value, const char *Plugin = NULL, bool AllowMultiple = false);
   void Store(const char *Name, int Value, const char *Plugin = NULL);
   void Store(const char *Name, double &Value, const char *Plugin = NULL);
+  cList<cAlternativeContainer> nullAlternatives;
 public:
+  cAllAlternatives Alternatives;
   // Also adjust cMenuSetup (menu.c) when adding parameters here!
   int __BeginData__;
   char OSDLanguage[I18N_MAX_LOCALE_LEN];
@@ -296,6 +369,11 @@ public:
   cSetup& operator= (const cSetup &s);
   bool Load(const char *FileName);
   bool Save(void);
+  bool LoadAlternative(const char *Name, const char *Value);
+  void SaveAlternatives();
+  cAllAlternatives& getAllAlternatives();
+  cList<cAlternativeContainer>& findAlternativeChannels(const cChannel* channel);
+  void Del(const cChannel *Channel);
   };
 
 extern cSetup Setup;
diff --git a/device.c b/device.c
index 681049b..a4d156a 100644
--- a/device.c
+++ b/device.c
@@ -67,7 +67,8 @@ bool cDeviceHook::DeviceProvidesTransponder(const cDevice *Device, const cChanne
 int cDevice::numDevices = 0;
 int cDevice::useDevice = 0;
 int cDevice::nextCardIndex = 0;
-int cDevice::currentChannel = 1;
+int cDevice::currentSelectedChannel = 1;
+int cDevice::currentReceivingChannel = 1;
 cDevice *cDevice::device[MAXDEVICES] = { NULL };
 cDevice *cDevice::primaryDevice = NULL;
 cDevice *cDevice::avoidDevice = NULL;
@@ -228,7 +229,52 @@ static int GetClippedNumProvidedSystems(int AvailableBits, cDevice *Device)
   return NumProvidedSystems;
 }
 
-cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView)
+cDevice *cDevice::QueryDevice(const cChannel *Channel, int Priority, bool LiveView, const cChannel *&foundChannelRef, bool allowAlternatives)
+{
+  cList<cAlternativeContainer> &alternatives = Setup.findAlternativeChannels(Channel);
+  if (!LiveView) {
+     /*
+      * Searching for alternative without detaching is not relevant for LiveView.
+      * Why?: There can be other receivers with priority = -1 (like osdteletext) and
+      * there are no other receivers with priority <= 0 for live view. Timers,
+      * streamdev and so on have receivers with priority > 0. So detaching is allowed.
+      */
+     // first try original channel without detaching
+     cDevice *device = GetDevice(Channel, Priority, LiveView, false);
+     if (device) {
+        foundChannelRef = Channel;
+        return device;
+        }
+     // now try alternatives without detaching
+     if (allowAlternatives) {
+        for(cAlternativeContainer *next = alternatives.First(); next ; next = alternatives.Next(next)) {
+           device = GetDevice(next->channel, Priority, LiveView, false);
+           if (device) {
+              foundChannelRef = next->channel;
+              return device;
+              }
+           }
+        }
+     }
+  // once again the same steps, this time detaching is allowed
+  cDevice *device = GetDevice(Channel, Priority, LiveView, true);
+  if (device) {
+     foundChannelRef = Channel;
+     return device;
+     }
+  if (allowAlternatives) {
+     for(cAlternativeContainer *next = alternatives.First(); next ; next = alternatives.Next(next)) {
+        device = GetDevice(next->channel, Priority, LiveView, true);
+        if (device) {
+           foundChannelRef = next->channel;
+           return device;
+           }
+        }
+     }
+  return NULL;
+}
+
+cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView, bool DetachingAllowed)
 {
   cDevice *AvoidDevice = avoidDevice;
   avoidDevice = NULL;
@@ -291,12 +337,18 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView
              imp <<= 1; imp |= device[i]->HasDecoder();                                                              // avoid full featured cards
              imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
              if (imp < Impact) {
+                // If this is the primary device and not live view, this is like detaching.
+                // The primary device can be used if there is a player (=>Not watching live tv)
+                bool likeNdr = device[i]->IsPrimaryDevice() && !LiveView && device[i]->player == NULL;
                 // This device has less impact than any previous one, so we take it.
-                Impact = imp;
-                d = device[i];
-                NeedsDetachReceivers = ndr;
-                if (NumUsableSlots)
-                   s = CamSlots.Get(j);
+                if (DetachingAllowed && (ndr || likeNdr) || !ndr) {
+                   // Only use the device if detaching is allowed or detaching is not needed
+                   Impact = imp;
+                   d = device[i];
+                   NeedsDetachReceivers = ndr;
+                   if (NumUsableSlots)
+                      s = CamSlots.Get(j);
+                   }
                 }
              }
           }
@@ -660,12 +712,13 @@ bool cDevice::SwitchChannel(int Direction)
   Direction = sgn(Direction);
   if (Direction) {
      cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer
-     int n = CurrentChannel() + Direction;
+     int n = CurrentChannelConfig().selected() + Direction;
      int first = n;
      cChannel *channel;
+     const cChannel *ptrAlternative;
      while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
            // try only channels which are currently available
-           if (GetDevice(channel, 0, true))
+           if (QueryDevice(channel, 0, true, ptrAlternative))
               break;
            n = channel->Number() + Direction;
            }
@@ -692,7 +745,15 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
      DELETENULL(dvbSubtitleConverter);
      }
 
-  cDevice *Device = (LiveView && IsPrimaryDevice()) ? GetDevice(Channel, 0, LiveView) : this;
+  cDevice *Device = NULL;
+  const cChannel *SourceChannel = NULL;
+  if (LiveView && IsPrimaryDevice()) {
+        Device = QueryDevice(Channel, 0, LiveView, SourceChannel);
+     }
+  else {
+        Device = this;
+        SourceChannel = Channel;
+     }
 
   bool NeedsTransferMode = Device != this;
 
@@ -704,8 +765,8 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
   if (NeedsTransferMode) {
      if (Device && CanReplay()) {
         cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
-        if (Device->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
-           cControl::Launch(new cTransferControl(Device, Channel));
+        if (Device->SetChannel(SourceChannel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
+           cControl::Launch(new cTransferControl(Device, SourceChannel));
         else
            Result = scrNoTransfer;
         }
@@ -714,6 +775,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
      }
   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) {
@@ -723,11 +785,11 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
      // Tell the camSlot about the channel switch and add all PIDs of this
      // channel to it, for possible later decryption:
      if (camSlot)
-        camSlot->AddChannel(Channel);
-     if (SetChannelDevice(Channel, LiveView)) {
+        camSlot->AddChannel(SourceChannel);
+     if (SetChannelDevice(SourceChannel, LiveView)) {
         // Start section handling:
         if (sectionHandler) {
-           sectionHandler->SetChannel(Channel);
+           sectionHandler->SetChannel(SourceChannel);
            sectionHandler->SetStatus(true);
            }
         // Start decrypting any PIDs that might have been set in SetChannelDevice():
@@ -741,17 +803,18 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
 
   if (Result == scrOk) {
      if (LiveView && IsPrimaryDevice()) {
-        currentChannel = Channel->Number();
+        currentSelectedChannel = Channel->Number();
+        currentReceivingChannel = SourceChannel->Number();
         // Set the available audio tracks:
         ClrAvailableTracks();
         for (int i = 0; i < MAXAPIDS; i++)
-            SetAvailableTrack(ttAudio, i, Channel->Apid(i), Channel->Alang(i));
+            SetAvailableTrack(ttAudio, i, SourceChannel->Apid(i), SourceChannel->Alang(i));
         if (Setup.UseDolbyDigital) {
            for (int i = 0; i < MAXDPIDS; i++)
-               SetAvailableTrack(ttDolby, i, Channel->Dpid(i), Channel->Dlang(i));
+               SetAvailableTrack(ttDolby, i, SourceChannel->Dpid(i), SourceChannel->Dlang(i));
            }
         for (int i = 0; i < MAXSPIDS; i++)
-            SetAvailableTrack(ttSubtitle, i, Channel->Spid(i), Channel->Slang(i));
+            SetAvailableTrack(ttSubtitle, i, SourceChannel->Spid(i), SourceChannel->Slang(i));
         if (!NeedsTransferMode)
            EnsureAudioTrack(true);
         EnsureSubtitleTrack();
@@ -765,7 +828,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
 void cDevice::ForceTransferMode(void)
 {
   if (!cTransferControl::ReceiverDevice()) {
-     cChannel *Channel = Channels.GetByNumber(CurrentChannel());
+     cChannel *Channel = Channels.GetByNumber(CurrentChannelConfig().receiving());
      if (Channel)
         SetChannelDevice(Channel, false); // this implicitly starts Transfer Mode
      }
diff --git a/device.h b/device.h
index cb3bc2c..2430786 100644
--- a/device.h
+++ b/device.h
@@ -138,7 +138,12 @@ public:
          ///< Gets the device with the given Index.
          ///< \param Index must be in the range 0..numDevices-1.
          ///< \return A pointer to the device, or NULL if the Index was invalid.
-  static cDevice *GetDevice(const cChannel *Channel, int Priority, bool LiveView);
+  static cDevice *QueryDevice(const cChannel *Channel, int Priority, bool LiveView, const cChannel *&foundChannelRef, bool allowAlternatives = true);
+         ///< Search for a device providing the given channel Channel. If other
+         ///< receivers would get detached, the alternative channels gets queried.
+         ///< The returned device doesn't have to provide the given channel. It
+         ///< will provide the returned channel foundChannelRef
+  static cDevice *GetDevice(const cChannel *Channel, int Priority, bool LiveView, bool DetachingAllowed = true);
          ///< Returns a device that is able to receive the given Channel at the
          ///< given Priority, with the least impact on active recordings and
          ///< live viewing. The LiveView parameter tells whether the device will
@@ -218,7 +223,9 @@ public:
 // Channel facilities
 
 protected:
-  static int currentChannel;
+  static int currentSelectedChannel;
+
+  static int currentReceivingChannel;
 public:
   virtual bool ProvidesSource(int Source) const;
          ///< Returns true if this device can provide the given source.
@@ -275,9 +282,16 @@ protected:
   virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
          ///< Sets the device to the given channel (actual physical setup).
 public:
-  static int CurrentChannel(void) { return primaryDevice ? currentChannel : 0; }
-         ///< Returns the number of the current channel on the primary device.
-  static void SetCurrentChannel(const cChannel *Channel) { currentChannel = Channel ? Channel->Number() : 0; }
+  static cChannelConfig CurrentChannelConfig(void) { return primaryDevice ? cChannelConfig(currentSelectedChannel, currentReceivingChannel) : cChannelConfig(0,0); }
+  static int CurrentChannel(void) { return primaryDevice ? currentSelectedChannel:0; }
+         ///< Returns the number of the current selected channel on the primary device.
+  static int CurrentReceivingChannel(void) { return primaryDevice ? currentReceivingChannel : 0; }
+         ///< Returns the number of the current receiving channel on the primary device.
+  static void SetCurrentChannel(const cChannel *Channel) { currentSelectedChannel = Channel ? Channel->Number() : 0; }
+         ///< Sets the number of the current channel on the primary device, without
+         ///< actually switching to it. This can be used to correct the current
+         ///< channel number while replaying.
+  static void SetReceivingChannel(const cChannel *Channel) { currentReceivingChannel = Channel ? Channel->Number() : 0; }
          ///< Sets the number of the current channel on the primary device, without
          ///< actually switching to it. This can be used to correct the current
          ///< channel number while replaying.
diff --git a/dvbdevice.c b/dvbdevice.c
index f32b350..28b659d 100644
--- a/dvbdevice.c
+++ b/dvbdevice.c
@@ -20,6 +20,7 @@
 #include "dvbci.h"
 #include "menuitems.h"
 #include "sourceparams.h"
+#include "sources.h"
 
 #define FE_CAN_TURBO_FEC  0x8000000 // TODO: remove this once it is defined in the driver
 
diff --git a/eitscan.c b/eitscan.c
index 2b43e71..00042bc 100644
--- a/eitscan.c
+++ b/eitscan.c
@@ -155,7 +155,7 @@ void cEITScanner::Process(void)
                                      if (!MaySwitchTransponder) {
                                         if (Device == cDevice::ActualDevice() && !currentChannel) {
                                            cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode
-                                           currentChannel = Device->CurrentChannel();
+                                           currentChannel = Device->CurrentChannelConfig().selected();
                                            Skins.Message(mtInfo, tr("Starting EPG scan"));
                                            }
                                         }
diff --git a/menu.c b/menu.c
index 19cfabb..60b2c72 100644
--- a/menu.c
+++ b/menu.c
@@ -399,7 +399,7 @@ void cMenuChannels::Setup(void)
 {
   cChannel *currentChannel = GetChannel(Current());
   if (!currentChannel)
-     currentChannel = Channels.GetByNumber(cDevice::CurrentChannel());
+     currentChannel = Channels.GetByNumber(cDevice::CurrentChannelConfig().selected());
   cMenuChannelItem *currentItem = NULL;
   Clear();
   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) {
@@ -486,7 +486,7 @@ eOSState cMenuChannels::New(void)
 eOSState cMenuChannels::Delete(void)
 {
   if (!HasSubMenu() && Count() > 0) {
-     int CurrentChannelNr = cDevice::CurrentChannel();
+     int CurrentChannelNr = cDevice::CurrentChannelConfig().selected();
      cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr);
      int Index = Current();
      cChannel *channel = GetChannel(Current());
@@ -504,6 +504,7 @@ eOSState cMenuChannels::Delete(void)
            CurrentChannel = Channels.Get(n);
            CurrentChannelNr = 0; // triggers channel switch below
            }
+        ::Setup.Del(channel);
         Channels.Del(channel);
         cOsdMenu::Del(Index);
         Propagate();
@@ -522,8 +523,10 @@ eOSState cMenuChannels::Delete(void)
 
 void cMenuChannels::Move(int From, int To)
 {
-  int CurrentChannelNr = cDevice::CurrentChannel();
+  int CurrentChannelNr = cDevice::CurrentChannelConfig().selected();
+  int ReceivingChannelNr = cDevice::CurrentChannelConfig().receiving();
   cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr);
+  cChannel *ReceivingChannel = Channels.GetByNumber(ReceivingChannelNr);
   cChannel *FromChannel = GetChannel(From);
   cChannel *ToChannel = GetChannel(To);
   if (FromChannel && ToChannel) {
@@ -534,11 +537,16 @@ void cMenuChannels::Move(int From, int To)
      Propagate();
      Channels.SetModified(true);
      isyslog("channel %d moved to %d", FromNumber, ToNumber);
-     if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) {
+     if (CurrentChannel && ReceivingChannel && (
+           CurrentChannel->Number() != CurrentChannelNr ||
+           ReceivingChannel->Number() != ReceivingChannelNr)
+           ) {
         if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring())
            Channels.SwitchTo(CurrentChannel->Number());
-        else
+        else {
            cDevice::SetCurrentChannel(CurrentChannel);
+           cDevice::SetReceivingChannel(ReceivingChannel);
+           }
         }
      }
 }
@@ -1547,7 +1555,7 @@ cMenuSchedule::cMenuSchedule(void)
   timerState = 0;
   Timers.Modified(timerState);
   cMenuScheduleItem::SetSortMode(cMenuScheduleItem::ssmAllThis);
-  cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
+  cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannelConfig().selected());
   if (channel) {
      cMenuWhatsOn::SetCurrentChannel(channel->Number());
      schedules = cSchedules::Schedules(schedulesLock);
@@ -1674,7 +1682,7 @@ eOSState cMenuSchedule::Number(void)
      Channel = Channels.GetByChannelID(Event->ChannelID(), true);
      }
   else
-     Channel = Channels.GetByNumber(cDevice::CurrentChannel());
+     Channel = Channels.GetByNumber(cDevice::CurrentChannelConfig().selected());
   switch (cMenuScheduleItem::SortMode()) {
     case cMenuScheduleItem::ssmAllThis:  PrepareScheduleAllThis(Event, Channel); break;
     case cMenuScheduleItem::ssmThisThis: PrepareScheduleThisThis(Event, Channel); break;
@@ -1778,7 +1786,7 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key)
         if (channel) {
            cMenuScheduleItem::SetSortMode(cMenuScheduleItem::ssmAllThis);
            PrepareScheduleAllThis(NULL, channel);
-           if (channel->Number() != cDevice::CurrentChannel()) {
+           if (channel->Number() != cDevice::CurrentChannelConfig().selected()) {
               otherChannel = channel->Number();
               SetHelp(Count() ? tr("Button$Record") : NULL, tr("Button$Now"), tr("Button$Next"), tr("Button$Switch"));
               }
@@ -3166,6 +3174,284 @@ eOSState cMenuSetupPlugins::ProcessKey(eKeys Key)
   return state;
 }
 
+// --- SelectChannelMenu -----------------------------------------------------
+
+class SelectChannelMenu : public cOsdMenu {
+private:
+   bool menuClosed;
+   static const cChannel *selectedChannel;
+public:
+   SelectChannelMenu();
+   static const cChannel *getSelectedChannel();
+   bool menuHasToBeClosed();;
+   virtual eOSState ProcessKey(eKeys Key);
+};
+
+const cChannel *SelectChannelMenu::selectedChannel = NULL;
+
+SelectChannelMenu::SelectChannelMenu() : cOsdMenu(tr("select channel"))
+{
+   menuClosed = false;
+   selectedChannel = NULL;
+   if (Channels.Count() > 0) {
+      const cChannel *channel = Channels.First();
+      while(true) {
+         Add(new cOsdItem(strdup(channel->Name()), osUnknown, true));
+         if (channel == Channels.Last())
+            break;
+         channel = Channels.Next(channel);
+      }
+   }
+}
+
+eOSState SelectChannelMenu::ProcessKey(eKeys Key) {
+   eOSState state = osUnknown;
+   if (Key == kOk) {
+      if (Channels.Count() > 0) {
+         selectedChannel = Channels.Get(Current());
+      }
+      state = osBack;
+   } else if (Key == kBack) {
+      state = osBack;
+   } else {
+      cOsdMenu::ProcessKey(Key);
+   }
+   return state;
+}
+
+bool SelectChannelMenu::menuHasToBeClosed() {
+   return menuClosed;
+}
+
+const cChannel *SelectChannelMenu::getSelectedChannel() {
+   return selectedChannel;
+}
+
+
+// --- cMenuSetupAlternativeMenuItem -----------------------------------------------------
+
+class cMenuSetupAlternativeMenuItem : public cOsdItem {
+   const cChannel *channel;
+public:
+   cMenuSetupAlternativeMenuItem(const cChannel *alternative, bool selectable = false);
+   const cChannel *getChannel();
+};
+
+cMenuSetupAlternativeMenuItem::cMenuSetupAlternativeMenuItem(const cChannel *alternative, bool selectable) : cOsdItem(NULL, osUnknown, selectable) {
+   char buf[128];
+   strcpy(buf, "  ");
+   strcat(buf, alternative->Name());
+   SetText(buf, true /* copy the string */);
+   this->channel = alternative;
+}
+
+const cChannel *cMenuSetupAlternativeMenuItem::getChannel() {
+   return channel;
+}
+
+// --- AltEditAlternative -----------------------------------------------------
+
+class cMenuSetupEditAlternative : public cOsdMenu {
+private:
+   cAlternative *alternative;
+   bool closeMenu;
+   void refreshHelp();
+   void refreshMenu();
+   bool IsDeletionEnabled();
+   void refreshAll();
+   SelectChannelMenu *selectChannelSubmenu;
+
+public:
+   cMenuSetupEditAlternative(cAlternative *alternative);
+   virtual eOSState ProcessKey(eKeys Key);
+   bool menuHasToBeClosed();
+};
+
+cMenuSetupEditAlternative::cMenuSetupEditAlternative(cAlternative *alternative) : cOsdMenu(tr("Edit Alternatives"))
+{
+   this->alternative = alternative;
+   selectChannelSubmenu = NULL;
+   refreshAll();
+   closeMenu = false;
+}
+
+bool cMenuSetupEditAlternative::IsDeletionEnabled()
+{
+    return alternative->GetAlternativeChannels().Count() > 0;
+}
+
+void cMenuSetupEditAlternative::refreshAll() {
+   refreshHelp();
+   refreshMenu();
+}
+
+void cMenuSetupEditAlternative::refreshHelp() {
+   if (IsDeletionEnabled()) {
+      SetHelp(tr("Add Channel"), tr("Remove Channel"));
+   } else {
+      SetHelp(tr("Add Channel"));
+   }
+}
+
+bool cMenuSetupEditAlternative::menuHasToBeClosed() {
+   return closeMenu;
+}
+
+void cMenuSetupEditAlternative::refreshMenu() {
+   Clear();
+   Add(new cOsdItem(alternative->GetMaster()->Name(), osUnknown, false));
+   cList<cAlternativeContainer>& ref = alternative->GetAlternativeChannels();
+   for(cAlternativeContainer *next = ref.First() ; next ; next = ref.Next(next)) {
+      Add(new cMenuSetupAlternativeMenuItem(next->channel, true));
+   }
+   RefreshCurrent();
+   Display();
+}
+
+eOSState cMenuSetupEditAlternative::ProcessKey(eKeys Key) {
+   eOSState state = osUnknown;
+   if (HasSubMenu()) {
+      state = cOsdMenu::ProcessKey(Key);
+      if (!HasSubMenu()) {
+         const cChannel *selected = SelectChannelMenu::getSelectedChannel();
+         if (selected) {
+            alternative->AddAlternativeChannel(selected);
+         }
+         refreshAll();
+         state = osContinue;
+      }
+   } else {
+      if (Key == kRed) {
+         AddSubMenu(selectChannelSubmenu = new SelectChannelMenu());
+      } else if (Key == kGreen) {
+         if (IsDeletionEnabled()) {
+            cMenuSetupAlternativeMenuItem *selected = static_cast<cMenuSetupAlternativeMenuItem *>(Get(Current()));
+            alternative->RemoveAlternativeChannel(selected->getChannel());
+            refreshAll();
+         }
+      } else {
+         state = cOsdMenu::ProcessKey(Key);
+      }
+   }
+   return state;
+}
+
+// --- MasterMenuItem -----------------------------------------------------
+
+class cMenuSetupAlternativeMasterMenuItem : public cOsdItem {
+private:
+   cAlternative* alternative;
+public:
+   cMenuSetupAlternativeMasterMenuItem(cAlternative *alternative);
+   cAlternative *getAlternative();
+};
+
+cMenuSetupAlternativeMasterMenuItem::cMenuSetupAlternativeMasterMenuItem(cAlternative *alternatives) : cOsdItem(alternatives->GetMaster()->Name(), osUnknown, true) {
+   this->alternative = alternatives;
+}
+
+cAlternative *cMenuSetupAlternativeMasterMenuItem::getAlternative() {
+   return alternative;
+}
+
+
+// --- cMenuSetupAlternative -----------------------------------------------------
+
+class cMenuSetupAlternative : public cMenuSetupBase {
+private:
+   SelectChannelMenu *selectChannelSubmenu;
+   cMenuSetupEditAlternative *editAlternative;
+
+protected:
+   virtual void Store(void);
+   void update();
+public:
+   cMenuSetupAlternative();
+   virtual eOSState ProcessKey(eKeys Key);
+};
+
+cMenuSetupAlternative::cMenuSetupAlternative() {
+    update();
+    selectChannelSubmenu = NULL;
+    editAlternative = NULL;
+}
+
+void cMenuSetupAlternative::Store() {
+   cMenuSetupBase::Store();
+}
+
+eOSState cMenuSetupAlternative::ProcessKey(eKeys Key) {
+   eOSState state  = osContinue;
+   if (HasSubMenu()) {
+      state = cMenuSetupPage::ProcessKey(Key);
+      if (!HasSubMenu()) { // submenu closed ?
+         if (selectChannelSubmenu) {
+            selectChannelSubmenu = NULL;
+            const cChannel *master = SelectChannelMenu::getSelectedChannel();
+            if (master != NULL) data.getAllAlternatives().CreateAlternative(master);
+            update();
+         } else if (editAlternative) {
+            editAlternative = NULL;
+            update();
+         }
+         state = osContinue;
+      }
+   } else {
+      switch (Key) {
+      case kRed:
+         AddSubMenu(selectChannelSubmenu = new SelectChannelMenu());
+         break;
+      case kGreen:
+         if (data.getAllAlternatives().CountAlternatives() > 0) {
+            cMenuSetupAlternativeMasterMenuItem *master = static_cast<cMenuSetupAlternativeMasterMenuItem *>(Get(Current()));
+            data.getAllAlternatives().RemoveAlternative(master->getAlternative());
+         }
+         update();
+         break;
+      case kYellow:
+      {
+         cMenuSetupAlternativeMasterMenuItem *item = static_cast<cMenuSetupAlternativeMasterMenuItem *>(Get(Current()));
+         AddSubMenu(editAlternative = new cMenuSetupEditAlternative(item->getAlternative()));
+         break;
+      }
+      case kBack:
+      {
+         if (Setup.Alternatives == data.Alternatives) {
+            state = cMenuSetupPage::ProcessKey(Key);
+            }
+         else {
+            if (Interface->Confirm(tr("Cancel without saving ?")))
+               state = cMenuSetupPage::ProcessKey(Key);
+            }
+         break;
+      }
+      default:
+         state = cMenuSetupPage::ProcessKey(Key);
+      }
+   }
+   return state;
+}
+
+void cMenuSetupAlternative::update() {
+   Clear();
+   for(int i = 0 ; i < data.getAllAlternatives().CountAlternatives() ; i++) {
+      cAlternative *alternative = data.getAllAlternatives().GetAlternative(i);
+      Add(new cMenuSetupAlternativeMasterMenuItem(alternative));
+      cList<cAlternativeContainer>& ref = alternative->GetAlternativeChannels();
+      for(cAlternativeContainer *next = ref.First() ; next ; next = ref.Next(next)) {
+         Add(new cMenuSetupAlternativeMenuItem(next->channel));
+      }
+   }
+   if (data.getAllAlternatives().CountAlternatives() == 0)
+      SetHelp(tr("New Alternative"));
+   else {
+      SetHelp(tr("New Alternative"), tr("Delete Alternative"), tr("Edit Alternative"));
+   }
+   RefreshCurrent();
+   Display();
+}
+
+
 // --- cMenuSetup ------------------------------------------------------------
 
 class cMenuSetup : public cOsdMenu {
@@ -3198,9 +3484,10 @@ void cMenuSetup::Set(void)
   Add(new cOsdItem(hk(tr("Recording")),     osUser6));
   Add(new cOsdItem(hk(tr("Replay")),        osUser7));
   Add(new cOsdItem(hk(tr("Miscellaneous")), osUser8));
+  Add(new cOsdItem(hk(tr("Alternatives")),  osUser9));
   if (cPluginManager::HasPlugins())
-  Add(new cOsdItem(hk(tr("Plugins")),       osUser9));
-  Add(new cOsdItem(hk(tr("Restart")),       osUser10));
+  Add(new cOsdItem(hk(tr("Plugins")),       osUser10));
+  Add(new cOsdItem(hk(tr("Restart")),       osUser11));
 }
 
 eOSState cMenuSetup::Restart(void)
@@ -3226,8 +3513,9 @@ eOSState cMenuSetup::ProcessKey(eKeys Key)
     case osUser6: return AddSubMenu(new cMenuSetupRecord);
     case osUser7: return AddSubMenu(new cMenuSetupReplay);
     case osUser8: return AddSubMenu(new cMenuSetupMisc);
-    case osUser9: return AddSubMenu(new cMenuSetupPlugins);
-    case osUser10: return Restart();
+    case osUser9: return AddSubMenu(new cMenuSetupAlternative);
+    case osUser10: return AddSubMenu(new cMenuSetupPlugins);
+    case osUser11: return Restart();
     default: ;
     }
   if (I18nCurrentLanguage() != osdLanguage) {
@@ -3519,7 +3807,7 @@ static void SetTrackDescriptions(int LiveChannel)
 
 cDisplayChannel *cDisplayChannel::currentDisplayChannel = NULL;
 
-cDisplayChannel::cDisplayChannel(int Number, bool Switched)
+cDisplayChannel::cDisplayChannel(int Number, int NumberReceiving, bool Switched)
 :cOsdObject(true)
 {
   currentDisplayChannel = this;
@@ -3528,9 +3816,10 @@ cDisplayChannel::cDisplayChannel(int Number, bool Switched)
   displayChannel = Skins.Current()->DisplayChannel(withInfo);
   number = 0;
   timeout = Switched || Setup.TimeoutRequChInfo;
-  channel = Channels.GetByNumber(Number);
+  selectedChannel = Channels.GetByNumber(Number);
+  receivingChannel = Channels.GetByNumber(NumberReceiving);
   lastPresent = lastFollowing = NULL;
-  if (channel) {
+  if (selectedChannel) {
      DisplayChannel();
      DisplayInfo();
      displayChannel->Flush();
@@ -3549,7 +3838,8 @@ cDisplayChannel::cDisplayChannel(eKeys FirstKey)
   lastTime.Set();
   withInfo = Setup.ShowInfoOnChSwitch;
   displayChannel = Skins.Current()->DisplayChannel(withInfo);
-  channel = Channels.GetByNumber(cDevice::CurrentChannel());
+  selectedChannel = Channels.GetByNumber(cDevice::CurrentChannelConfig().selected());
+  receivingChannel = Channels.GetByNumber(cDevice::CurrentChannelConfig().receiving());
   ProcessKey(FirstKey);
 }
 
@@ -3562,23 +3852,24 @@ cDisplayChannel::~cDisplayChannel()
 
 void cDisplayChannel::DisplayChannel(void)
 {
-  displayChannel->SetChannel(channel, number);
-  cStatus::MsgOsdChannel(ChannelString(channel, number));
+  cChannelConfig cc(selectedChannel->Number(), receivingChannel->Number());
+  displayChannel->SetChannel(cc, number);
+  cStatus::MsgOsdChannel(ChannelString(cc, number));
   lastPresent = lastFollowing = NULL;
 }
 
 void cDisplayChannel::DisplayInfo(void)
 {
-  if (withInfo && channel) {
+  if (withInfo && selectedChannel) {
      cSchedulesLock SchedulesLock;
      const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
      if (Schedules) {
-        const cSchedule *Schedule = Schedules->GetSchedule(channel);
+        const cSchedule *Schedule = Schedules->GetSchedule(selectedChannel);
         if (Schedule) {
            const cEvent *Present = Schedule->GetPresentEvent();
            const cEvent *Following = Schedule->GetFollowingEvent();
            if (Present != lastPresent || Following != lastFollowing) {
-              SetTrackDescriptions(channel->Number());
+              SetTrackDescriptions(receivingChannel->Number());
               displayChannel->SetEvents(Present, Following);
               cStatus::MsgOsdProgramme(Present ? Present->StartTime() : 0, Present ? Present->Title() : NULL, Present ? Present->ShortText() : NULL, Following ? Following->StartTime() : 0, Following ? Following->Title() : NULL, Following ? Following->ShortText() : NULL);
               lastPresent = Present;
@@ -3595,15 +3886,18 @@ void cDisplayChannel::Refresh(void)
   displayChannel->SetEvents(NULL, NULL);
 }
 
-cChannel *cDisplayChannel::NextAvailableChannel(cChannel *Channel, int Direction)
+cChannel *cDisplayChannel::NextAvailableChannel(cChannel *Channel, int Direction, const cChannel *&RefReceiving)
 {
   if (Direction) {
      while (Channel) {
            Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
            if (!Channel && Setup.ChannelsWrap)
               Channel = Direction > 0 ? Channels.First() : Channels.Last();
-           if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, 0, true))
+           const cChannel *PtrAlternative = NULL;
+           if (Channel && !Channel->GroupSep() && cDevice::QueryDevice(Channel, 0, true, PtrAlternative)) {
+        	  RefReceiving = PtrAlternative;
               return Channel;
+              }
            }
      }
   return NULL;
@@ -3628,13 +3922,13 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
                number = Key - k0;
             else
                number = number * 10 + Key - k0;
-            channel = Channels.GetByNumber(number);
+            receivingChannel = selectedChannel = Channels.GetByNumber(number); // also assign receiving for hiding parenthese
             Refresh();
             withInfo = false;
             // Lets see if there can be any useful further input:
-            int n = channel ? number * 10 : 0;
+            int n = selectedChannel ? number * 10 : 0;
             int m = 10;
-            cChannel *ch = channel;
+            cChannel *ch = selectedChannel;
             while (ch && (ch = Channels.Next(ch)) != NULL) {
                   if (!ch->GroupSep()) {
                      if (n <= ch->Number() && ch->Number() < n + m) {
@@ -3649,7 +3943,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
                   }
             if (n > 0) {
                // This channel is the only one that fits the input, so let's take it right away:
-               NewChannel = channel;
+               NewChannel = selectedChannel;
                withInfo = true;
                number = 0;
                Refresh();
@@ -3667,7 +3961,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
          withInfo = false;
          number = 0;
          if (group < 0) {
-            cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
+            cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannelConfig().selected());
             if (channel)
                group = channel->Index();
             }
@@ -3679,10 +3973,10 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
                group = Channels.GetPrevGroup(group < 1 ? 1 : group);
             if (group < 0)
                group = SaveGroup;
-            channel = Channels.Get(group);
-            if (channel) {
+            selectedChannel = Channels.Get(group);
+            if (selectedChannel) {
                Refresh();
-               if (!channel->GroupSep())
+               if (!selectedChannel->GroupSep())
                   group = -1;
                }
             }
@@ -3696,10 +3990,13 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
     case kChanDn|k_Repeat:
     case kChanDn: {
          eKeys k = NORMALKEY(Key);
-         cChannel *ch = NextAvailableChannel(channel, (k == kUp || k == kChanUp) ? 1 : -1);
-         if (ch)
-            channel = ch;
-         else if (channel && channel->Number() != cDevice::CurrentChannel())
+         const cChannel *rec = NULL;
+         cChannel *ch = NextAvailableChannel(selectedChannel, (k == kUp || k == kChanUp) ? 1 : -1, rec);
+         if (ch) {
+            selectedChannel = ch;
+            receivingChannel = ch; // the correct channel will be set later after the break
+         }
+         else if (selectedChannel && selectedChannel->Number() != cDevice::CurrentChannelConfig().selected())
             Key = k; // immediately switches channel when hitting the beginning/end of the channel list with k_Repeat
          }
          // no break here
@@ -3709,8 +4006,8 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
     case kChanDn|k_Release:
     case kNext|k_Release:
     case kPrev|k_Release:
-         if (!(Key & k_Repeat) && channel && channel->Number() != cDevice::CurrentChannel())
-            NewChannel = channel;
+         if (!(Key & k_Repeat) && selectedChannel && selectedChannel->Number() != cDevice::CurrentChannelConfig().selected())
+            NewChannel = selectedChannel;
          withInfo = true;
          group = -1;
          number = 0;
@@ -3718,9 +4015,9 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
          break;
     case kNone:
          if (number && Setup.ChannelEntryTimeout && int(lastTime.Elapsed()) > Setup.ChannelEntryTimeout) {
-            channel = Channels.GetByNumber(number);
-            if (channel)
-               NewChannel = channel;
+            selectedChannel = Channels.GetByNumber(number);
+            if (selectedChannel)
+               NewChannel = selectedChannel;
             withInfo = true;
             number = 0;
             Refresh();
@@ -3732,17 +4029,17 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
     //XXX case kYellow: return osEventNext;
     case kOk:
          if (group >= 0) {
-            channel = Channels.Get(Channels.GetNextNormal(group));
-            if (channel)
-               NewChannel = channel;
+            selectedChannel = Channels.Get(Channels.GetNextNormal(group));
+            if (selectedChannel)
+               NewChannel = selectedChannel;
             withInfo = true;
             group = -1;
             Refresh();
             }
          else if (number > 0) {
-            channel = Channels.GetByNumber(number);
-            if (channel)
-               NewChannel = channel;
+            selectedChannel = Channels.GetByNumber(number);
+            if (selectedChannel)
+               NewChannel = selectedChannel;
             withInfo = true;
             number = 0;
             Refresh();
@@ -3757,9 +4054,9 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
             }
     };
   if (!timeout || lastTime.Elapsed() < (uint64_t)(Setup.ChannelInfoTime * 1000)) {
-     if (Key == kNone && !number && group < 0 && !NewChannel && channel && channel->Number() != cDevice::CurrentChannel()) {
+     if (Key == kNone && !number && group < 0 && !NewChannel && selectedChannel && selectedChannel->Number() != cDevice::CurrentChannelConfig().selected()) {
         // makes sure a channel switch through the SVDRP CHAN command is displayed
-        channel = Channels.GetByNumber(cDevice::CurrentChannel());
+        selectedChannel = Channels.GetByNumber(cDevice::CurrentChannelConfig().selected());
         Refresh();
         lastTime.Set();
         }
@@ -3769,7 +4066,9 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
         SetTrackDescriptions(NewChannel->Number()); // to make them immediately visible in the channel display
         Channels.SwitchTo(NewChannel->Number());
         SetTrackDescriptions(NewChannel->Number()); // switching the channel has cleared them
-        channel = NewChannel;
+        selectedChannel = NewChannel;
+        receivingChannel = Channels.Get(cDevice::CurrentChannelConfig().receiving()-1); // .Get is zero-based
+        Refresh();
         }
      return osContinue;
      }
@@ -3853,7 +4152,7 @@ cDisplayTracks::cDisplayTracks(void)
 :cOsdObject(true)
 {
   cDevice::PrimaryDevice()->EnsureAudioTrack();
-  SetTrackDescriptions(!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring() ? cDevice::CurrentChannel() : 0);
+  SetTrackDescriptions(!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring() ? cDevice::CurrentChannelConfig().receiving() : 0);
   currentDisplayTracks = this;
   numTracks = track = 0;
   audioChannel = cDevice::PrimaryDevice()->GetAudioChannel();
@@ -3972,7 +4271,7 @@ cDisplaySubtitleTracks *cDisplaySubtitleTracks::currentDisplayTracks = NULL;
 cDisplaySubtitleTracks::cDisplaySubtitleTracks(void)
 :cOsdObject(true)
 {
-  SetTrackDescriptions(!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring() ? cDevice::CurrentChannel() : 0);
+  SetTrackDescriptions(!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring() ? cDevice::CurrentChannelConfig().receiving() : 0);
   currentDisplayTracks = this;
   numTracks = track = 0;
   types[numTracks] = ttNone;
@@ -4219,13 +4518,16 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause)
   LastNoDiskSpaceMessage = 0;
 
   ChangeState();
-  int ch = Timer ? Timer->Channel()->Number() : cDevice::CurrentChannel();
+  int ch = Timer ? Timer->Channel()->Number() : cDevice::CurrentChannelConfig().selected();
   cChannel *channel = Channels.GetByNumber(ch);
 
   if (channel) {
      int Priority = Timer ? Timer->Priority() : Pause ? Setup.PausePriority : Setup.DefaultPriority;
-     cDevice *device = cDevice::GetDevice(channel, Priority, false);
+     const cChannel *receivingChannel = NULL;
+     cDevice *device = cDevice::QueryDevice(channel, Priority, false, receivingChannel);
      if (device) {
+    	channel = Channels.Get(receivingChannel->Number()-1); // receivingChannel is const, channel not
+    	if (Timer) Timer->SetChannel(channel);
         dsyslog("switching device %d to channel %d", device->DeviceNumber() + 1, channel->Number());
         if (!device->SwitchChannel(channel, false)) {
            ShutdownHandler.RequestEmergencyExit();
diff --git a/menu.h b/menu.h
index ec1c175..a3d8dcd 100644
--- a/menu.h
+++ b/menu.h
@@ -119,16 +119,17 @@ private:
   cTimeMs lastTime;
   int number;
   bool timeout;
-  cChannel *channel;
+  cChannel *selectedChannel;
+  const cChannel *receivingChannel;
   const cEvent *lastPresent;
   const cEvent *lastFollowing;
   static cDisplayChannel *currentDisplayChannel;
   void DisplayChannel(void);
   void DisplayInfo(void);
   void Refresh(void);
-  cChannel *NextAvailableChannel(cChannel *Channel, int Direction);
+  cChannel *NextAvailableChannel(cChannel *Channel, int Direction, const cChannel *&RefReceiving);
 public:
-  cDisplayChannel(int Number, bool Switched);
+  cDisplayChannel(int Number, int NumberReceiving, bool Switched);
   cDisplayChannel(eKeys FirstKey);
   virtual ~cDisplayChannel();
   virtual eOSState ProcessKey(eKeys Key);
diff --git a/nit.c b/nit.c
index dfd0207..9c50a4b 100644
--- a/nit.c
+++ b/nit.c
@@ -15,6 +15,7 @@
 #include "libsi/section.h"
 #include "libsi/descriptor.h"
 #include "tools.h"
+#include "sources.h"
 
 cNitFilter::cNitFilter(void)
 {
diff --git a/osdbase.h b/osdbase.h
index 91c5ff7..231d64f 100644
--- a/osdbase.h
+++ b/osdbase.h
@@ -44,6 +44,11 @@ enum eOSState { osUnknown,
                 osUser8,
                 osUser9,
                 osUser10,
+                osUser11,
+                osUser12,
+                osUser13,
+                osUser14,
+                osUser15,
               };
 
 class cOsdItem : public cListObject {
diff --git a/rcu.c b/rcu.c
index 2e20314..a8131f9 100644
--- a/rcu.c
+++ b/rcu.c
@@ -320,7 +320,7 @@ bool cRcuRemote::DetectCode(unsigned char *Code)
 void cRcuRemote::ChannelSwitch(const cDevice *Device, int ChannelNumber)
 {
   if (ChannelNumber && Device->IsPrimaryDevice())
-     SetNumber(cDevice::CurrentChannel());
+     SetNumber(cDevice::CurrentChannelConfig().selected());
 }
 
 void cRcuRemote::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On)
diff --git a/sdt.c b/sdt.c
index 5f2502b..cb1c070 100644
--- a/sdt.c
+++ b/sdt.c
@@ -12,6 +12,7 @@
 #include "config.h"
 #include "libsi/section.h"
 #include "libsi/descriptor.h"
+#include "sources.h"
 
 // --- cSdtFilter ------------------------------------------------------------
 
diff --git a/skinclassic.c b/skinclassic.c
index 3852010..9fd1b23 100644
--- a/skinclassic.c
+++ b/skinclassic.c
@@ -83,7 +83,7 @@ private:
 public:
   cSkinClassicDisplayChannel(bool WithInfo);
   virtual ~cSkinClassicDisplayChannel();
-  virtual void SetChannel(const cChannel *Channel, int Number);
+  virtual void SetChannel(cChannelConfig channelConfig, int Number);
   virtual void SetEvents(const cEvent *Present, const cEvent *Following);
   virtual void SetMessage(eMessageType Type, const char *Text);
   virtual void Flush(void);
@@ -112,10 +112,10 @@ cSkinClassicDisplayChannel::~cSkinClassicDisplayChannel()
   delete osd;
 }
 
-void cSkinClassicDisplayChannel::SetChannel(const cChannel *Channel, int Number)
+void cSkinClassicDisplayChannel::SetChannel(cChannelConfig channelConfig, int Number)
 {
   osd->DrawRectangle(0, 0, osd->Width() - 1, lineHeight - 1, Theme.Color(clrBackground));
-  osd->DrawText(TextFrame, 0, ChannelString(Channel, Number), Theme.Color(clrChannelName), Theme.Color(clrBackground), cFont::GetFont(fontOsd));
+  osd->DrawText(TextFrame, 0, ChannelString(channelConfig, Number), Theme.Color(clrChannelName), Theme.Color(clrBackground), cFont::GetFont(fontOsd));
   lastDate = NULL;
 }
 
diff --git a/skins.h b/skins.h
index 6f41da5..93abc4b 100644
--- a/skins.h
+++ b/skins.h
@@ -50,7 +50,7 @@ class cSkinDisplayChannel : public cSkinDisplay {
        ///< the present and following EPG even. How and to what extent this
        ///< is done is totally up to the derived class.
 public:
-  virtual void SetChannel(const cChannel *Channel, int Number) = 0;
+  virtual void SetChannel(cChannelConfig channelConfig, int Number) = 0;
        ///< Sets the current channel to Channel. If Number is not 0, the
        ///< user is in the process of entering a channel number, which must
        ///< be displayed accordingly.
diff --git a/skinsttng.c b/skinsttng.c
index b37a2e9..bdd5df0 100644
--- a/skinsttng.c
+++ b/skinsttng.c
@@ -7,7 +7,7 @@
  * $Id: skinsttng.c 2.5 2010/02/13 13:30:59 kls Exp $
  */
 
-// Star Trek: The Next Generation® is a registered trademark of Paramount Pictures
+// Star Trek: The Next Generation� is a registered trademark of Paramount Pictures
 // registered in the United States Patent and Trademark Office.
 // No infringement intended.
 
@@ -139,7 +139,7 @@ private:
 public:
   cSkinSTTNGDisplayChannel(bool WithInfo);
   virtual ~cSkinSTTNGDisplayChannel();
-  virtual void SetChannel(const cChannel *Channel, int Number);
+  virtual void SetChannel(cChannelConfig channelConfig, int Number);
   virtual void SetEvents(const cEvent *Present, const cEvent *Following);
   virtual void SetMessage(eMessageType Type, const char *Text);
   virtual void Flush(void);
@@ -240,10 +240,11 @@ cSkinSTTNGDisplayChannel::~cSkinSTTNGDisplayChannel()
   delete osd;
 }
 
-void cSkinSTTNGDisplayChannel::SetChannel(const cChannel *Channel, int Number)
+void cSkinSTTNGDisplayChannel::SetChannel(cChannelConfig channelConfig, int Number)
 {
   osd->DrawRectangle(x3, y0, x4 - 1, y1 - 1, frameColor);
   int x = x4 - SymbolSpacing;
+  const cChannel *Channel = Channels.Get(channelConfig.selected());
   if (Channel && !Channel->GroupSep()) {
      bool rec = cRecordControls::Active();
      x -= bmRecording.Width() + SymbolSpacing;
@@ -263,7 +264,7 @@ void cSkinSTTNGDisplayChannel::SetChannel(const cChannel *Channel, int Number)
         osd->DrawBitmap(x, y0 + (y1 - y0 - bmRadio.Height()) / 2, bmRadio, Theme.Color(clrChannelSymbolOn), frameColor);
         }
      }
-  osd->DrawText(x3 + TextFrame, y0, ChannelString(Channel, Number), Theme.Color(clrChannelName), frameColor, cFont::GetFont(fontOsd), x - x3 - TextFrame);
+  osd->DrawText(x3 + TextFrame, y0, ChannelString(channelConfig, Number), Theme.Color(clrChannelName), frameColor, cFont::GetFont(fontOsd), x - x3 - TextFrame);
 }
 
 void cSkinSTTNGDisplayChannel::SetEvents(const cEvent *Present, const cEvent *Following)
diff --git a/sources.h b/sources.h
index 6b362e3..a6f0db3 100644
--- a/sources.h
+++ b/sources.h
@@ -10,6 +10,7 @@
 #ifndef __SOURCES_H
 #define __SOURCES_H
 
+#include "tools.h"
 #include "config.h"
 
 class cSource : public cListObject {
diff --git a/svdrp.c b/svdrp.c
index cb9a935..4dbe742 100644
--- a/svdrp.c
+++ b/svdrp.c
@@ -488,14 +488,14 @@ void cSVDRP::CmdCHAN(const char *Option)
            n = o;
         }
      else if (strcmp(Option, "-") == 0) {
-        n = cDevice::CurrentChannel();
+        n = cDevice::CurrentChannelConfig().selected();
         if (n > 1) {
            n--;
            d = -1;
            }
         }
      else if (strcmp(Option, "+") == 0) {
-        n = cDevice::CurrentChannel();
+        n = cDevice::CurrentChannelConfig().selected();
         if (n < Channels.MaxNumber()) {
            n++;
            d = 1;
@@ -536,11 +536,11 @@ void cSVDRP::CmdCHAN(const char *Option)
      else
         cDevice::SwitchChannel(d);
      }
-  cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
+  cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannelConfig().selected());
   if (channel)
      Reply(250, "%d %s", channel->Number(), channel->Name());
   else
-     Reply(550, "Unable to find channel \"%d\"", cDevice::CurrentChannel());
+     Reply(550, "Unable to find channel \"%d\"", cDevice::CurrentChannelConfig().selected());
 }
 
 void cSVDRP::CmdCLRE(const char *Option)
@@ -613,7 +613,7 @@ void cSVDRP::CmdDELC(const char *Option)
                      return;
                      }
                   }
-              int CurrentChannelNr = cDevice::CurrentChannel();
+              int CurrentChannelNr = cDevice::CurrentChannelConfig().selected();
               cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr);
               if (CurrentChannel && channel == CurrentChannel) {
                  int n = Channels.GetNextNormal(CurrentChannel->Index());
@@ -1214,7 +1214,7 @@ void cSVDRP::CmdMOVC(const char *Option)
            tail = skipspace(tail);
            if (tail && tail != Option) {
               int To = strtol(tail, NULL, 10);
-              int CurrentChannelNr = cDevice::CurrentChannel();
+              int CurrentChannelNr = cDevice::CurrentChannelConfig().selected();
               cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr);
               cChannel *FromChannel = Channels.GetByNumber(From);
               if (FromChannel) {
diff --git a/timers.c b/timers.c
index 246fd86..be18a26 100644
--- a/timers.c
+++ b/timers.c
@@ -33,7 +33,7 @@ cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel)
   flags = tfNone;
   if (Instant)
      SetFlags(tfActive | tfInstant);
-  channel = Channel ? Channel : Channels.GetByNumber(cDevice::CurrentChannel());
+  channel = Channel ? Channel : Channels.GetByNumber(cDevice::CurrentChannelConfig().selected());
   time_t t = time(NULL);
   struct tm tm_r;
   struct tm *now = localtime_r(&t, &tm_r);
@@ -771,3 +771,7 @@ void cTimers::DeleteExpired(void)
         }
   lastDeleteExpired = time(NULL);
 }
+
+void cTimer::SetChannel(cChannel *channel) {
+	this->channel = channel;
+}
diff --git a/timers.h b/timers.h
index 8f67add..846b4c0 100644
--- a/timers.h
+++ b/timers.h
@@ -92,6 +92,7 @@ public:
   void Skip(void);
   void OnOff(void);
   cString PrintFirstDay(void) const;
+  void SetChannel(cChannel *channel);
   static int TimeToInt(int t);
   static bool ParseDay(const char *s, time_t &Day, int &WeekDays);
   static cString PrintDay(time_t Day, int WeekDays, bool SingleByteChars);
diff --git a/vdr.c b/vdr.c
index 93f586a..ed54c79 100644
--- a/vdr.c
+++ b/vdr.c
@@ -747,9 +747,10 @@ int main(int argc, char *argv[])
         if (!EITScanner.Active() && cDevice::PrimaryDevice()->HasDecoder() && !cDevice::PrimaryDevice()->HasProgramme()) {
            static time_t lastTime = 0;
            if ((!Menu || CheckHasProgramme) && Now - lastTime > MINCHANNELWAIT) { // !Menu to avoid interfering with the CAM if a CAM menu is open
-              cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel());
+              cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannelConfig().selected());
               if (Channel && (Channel->Vpid() || Channel->Apid(0))) {
-                 if (!Channels.SwitchTo(cDevice::CurrentChannel()) // try to switch to the original channel...
+                 /// TODO: search for an alternative
+                 if (!Channels.SwitchTo(Channel->Number()) // try to switch to the original channel...
                      && !(LastTimerChannel > 0 && Channels.SwitchTo(LastTimerChannel))) // ...or the one used by the last timer...
                     ;
                  }
@@ -795,7 +796,7 @@ int main(int argc, char *argv[])
               for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
                   if (Channel->Modification(CHANNELMOD_RETUNE)) {
                      cRecordControls::ChannelDataModified(Channel);
-                     if (Channel->Number() == cDevice::CurrentChannel()) {
+                     if (Channel->Number() == cDevice::CurrentChannelConfig().receiving()) {
                         if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) {
                            if (cDevice::ActualDevice()->ProvidesTransponder(Channel)) { // avoids retune on devices that don't really access the transponder
                               isyslog("retuning due to modification of channel %d", Channel->Number());
@@ -809,10 +810,10 @@ int main(int argc, char *argv[])
               }
            }
         // Channel display:
-        if (!EITScanner.Active() && cDevice::CurrentChannel() != LastChannel) {
+        if (!EITScanner.Active() && cDevice::CurrentChannelConfig().selected() != LastChannel) {
            if (!Menu)
-              Menu = new cDisplayChannel(cDevice::CurrentChannel(), LastChannel >= 0);
-           LastChannel = cDevice::CurrentChannel();
+              Menu = new cDisplayChannel(cDevice::CurrentChannelConfig().selected(), cDevice::CurrentChannelConfig().receiving(), LastChannel >= 0);
+           LastChannel = cDevice::CurrentChannelConfig().selected();
            LastChannelChanged = Now;
            }
         if (Now - LastChannelChanged >= Setup.ZapTimeout && LastChannel != PreviousChannel[PreviousChannelIndex])
@@ -903,7 +904,8 @@ int main(int argc, char *argv[])
                         if (cDevice::PrimaryDevice()->HasDecoder() && !cDevice::PrimaryDevice()->HasProgramme()) {
                            // the previous SwitchChannel() has switched away the current live channel
                            cDevice::SetAvoidDevice(Device);
-                           if (!Channels.SwitchTo(cDevice::CurrentChannel())) // try to switch to the original channel on a different device...
+                           /// TODO: search for an alternative
+                           if (!Channels.SwitchTo(cDevice::CurrentChannelConfig().selected())) // try to switch to the original channel on a different device...
                               Channels.SwitchTo(Timer->Channel()->Number()); // ...or avoid toggling between old channel and black screen
                            Skins.Message(mtInfo, tr("Upcoming recording!"));
                            }
@@ -1306,7 +1308,7 @@ Exit:
   Skins.Clear();
   SourceParams.Clear();
   if (ShutdownHandler.GetExitCode() != 2) {
-     Setup.CurrentChannel = cDevice::CurrentChannel();
+     Setup.CurrentChannel = cDevice::CurrentChannelConfig().selected();
      Setup.CurrentVolume  = cDevice::CurrentVolume();
      Setup.Save();
      }
_______________________________________________
vdr mailing list
vdr@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr

[Index of Archives]     [Linux Media]     [Asterisk]     [DCCP]     [Netdev]     [Xorg]     [Util Linux NG]     [Xfree86]     [Big List of Linux Books]     [Fedora Users]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux