Index: libs/libmythtv/remoteutil.h
===================================================================
--- libs/libmythtv/remoteutil.h	(revision 13296)
+++ libs/libmythtv/remoteutil.h	(working copy)
@@ -27,6 +27,21 @@
     int weight;
 };
 
+class MPUBLIC InputInfo
+{
+  public:
+    InputInfo() : name(QString::null), sourceid(0), inputid(0), cardid(0) {}
+    InputInfo(const QString &name, uint sourceid, uint inputid, uint cardid);
+    InputInfo(const InputInfo &other);
+    InputInfo &operator=(const InputInfo &other);
+
+  public:
+    QString name;
+    uint sourceid;
+    uint inputid;
+    uint cardid;
+};
+
 MPUBLIC vector<ProgramInfo *> *RemoteGetRecordedList(bool deltype);
 MPUBLIC vector<FileSystemInfo> RemoteGetFreeSpace();
 MPUBLIC bool RemoteGetLoad(float load[3]);
@@ -52,10 +67,15 @@
 RemoteEncoder *RemoteRequestFreeRecorderFromList(QStringList &qualifiedRecorders);
 MPUBLIC RemoteEncoder *RemoteGetExistingRecorder(ProgramInfo *pginfo);
 MPUBLIC RemoteEncoder *RemoteGetExistingRecorder(int recordernum);
+MPUBLIC vector<uint> RemoteRequestFreeRecorderList(void);
+MPUBLIC vector<InputInfo> RemoteRequestFreeInputList(
+    uint cardid, vector<uint> excluded_cardids);
+MPUBLIC InputInfo RemoteRequestBusyInputID(uint cardid);
 MPUBLIC void RemoteGeneratePreviewPixmap(ProgramInfo *pginfo);
 MPUBLIC QString RemoteGetPreviewLastModified(ProgramInfo *pginfo);
 MPUBLIC void RemoteFillProginfo(ProgramInfo *pginfo,
                                 const QString &playbackhostname);
+MPUBLIC bool RemoteIsBusy(uint cardid);
 MPUBLIC int RemoteIsRecording(void);
 MPUBLIC int RemoteGetRecordingMask(void);
 MPUBLIC int RemoteGetFreeRecorderCount(void);
Index: libs/libmythtv/cardutil.h
===================================================================
--- libs/libmythtv/cardutil.h	(revision 13346)
+++ libs/libmythtv/cardutil.h	(working copy)
@@ -132,10 +132,24 @@
                                  uint sid, const QString &val)
         { return set_on_source(col, cid, sid, val); }
 
-
+    // Inputs
     static QString      GetDefaultInput(uint cardid);
     static QStringList  GetInputNames(uint cardid, uint sourceid);
+    static bool         GetInputInfo(uint inputid, QString &inputname,
+                                     uint &sourceid, uint &cardid,
+                                     vector<uint> &groupids);
+    static uint         GetCardID(uint inputid);
+    static QString      GetInputName(uint inputid);
+    static QString      GetDisplayName(uint inputid);
+    static QString      GetDisplayName(uint cardid, const QString &inputname);
 
+    // Input Groups
+    static uint         CreateInputGroup(const QString &name);
+    static bool         LinkInputGroup(uint inputid, uint inputgroupid);
+    static bool         UnlinkInputGroup(uint inputid, uint inputgroupid);
+    static vector<uint> GetInputGroups(uint inputid);
+    static vector<uint> GetGroupCardIDs(uint inputgroupid);
+
     static QString      GetDeviceLabel(uint    cardid,
                                        QString cardtype,
                                        QString videodevice);
@@ -152,12 +166,8 @@
                                       int                 parentid = 0);
 
     static bool         DeleteCard(uint cardid);
+    static vector<uint> GetCardList(bool primaryOnly = true);
 
-    // Input Groups
-    static uint         CreateInputGroup(const QString &name);
-    static bool         LinkInputGroup(uint inputid, uint inputgroupid);
-    static bool         UnlinkInputGroup(uint inputid, uint inputgroupid);
-
     // DTV info
     static bool         GetTimeouts(uint cardid,
                                     uint &signal_timeout,
Index: libs/libmythtv/tv_play.h
===================================================================
--- libs/libmythtv/tv_play.h	(revision 13347)
+++ libs/libmythtv/tv_play.h	(working copy)
@@ -258,10 +258,10 @@
     QString GetQueuedChanNum(void) const;
     uint    GetQueuedChanID(void)  const { return queuedChanID; }
 
-    void ToggleInputs(void); 
+    void SwitchInputs(uint inputid);
+    void ToggleInputs(uint inputid = 0); 
+    void SwitchCards(uint chanid = 0, QString channum = "", uint inputid = 0);
 
-    void SwitchCards(uint chanid = 0, QString channum = "");
-
     void ToggleSleepTimer(void);
     void ToggleSleepTimer(const QString);
 
@@ -419,6 +419,7 @@
     mutable QMutex     stateLock;
     TVState            internalState;
 
+    uint switchToInputId;
     bool menurunning;
     bool runMainLoop;
     bool wantsToQuit;
Index: libs/libmythtv/sourceutil.h
===================================================================
--- libs/libmythtv/sourceutil.h	(revision 13296)
+++ libs/libmythtv/sourceutil.h	(working copy)
@@ -10,6 +10,8 @@
 class MPUBLIC SourceUtil
 {
   public:
+    static bool    HasDigitalChannel(uint sourceid);
+    static QString GetSourceName(uint sourceid);
     static QString GetChannelSeparator(uint sourceid);
     static QString GetChannelFormat(uint sourceid);
     static uint    GetChannelCount(uint sourceid);
Index: libs/libmythtv/tv_play.cpp
===================================================================
--- libs/libmythtv/tv_play.cpp	(revision 13347)
+++ libs/libmythtv/tv_play.cpp	(working copy)
@@ -4,6 +4,9 @@
 #include <unistd.h>
 #include <pthread.h>
 
+#include <algorithm>
+using namespace std;
+
 #include <qapplication.h>
 #include <qregexp.h>
 #include <qfile.h>
@@ -40,6 +43,7 @@
 #include "DVDRingBuffer.h"
 #include "datadirect.h"
 #include "sourceutil.h"
+#include "cardutil.h"
 
 #ifndef HAVE_ROUND
 #define round(x) ((int) ((x) + 0.5))
@@ -475,6 +479,7 @@
       vbimode(VBIMode::None),
       // State variables
       internalState(kState_None),
+      switchToInputId(0),
       menurunning(false), runMainLoop(false), wantsToQuit(true),
       exitPlayer(false), paused(false), errored(false),
       stretchAdjustment(false),
@@ -1987,6 +1992,13 @@
             ClearInputQueues(true);
         }   
 
+        if (switchToInputId)
+        {
+            uint tmp = switchToInputId;
+            switchToInputId = 0;
+            SwitchInputs(tmp);
+        }
+
         if (class LCD * lcd = LCD::Get())
         {
             QDateTime curTime = QDateTime::currentDateTime();
@@ -3915,24 +3927,47 @@
         muteTimer->start(kMuteTimeout, true);
 }
 
-void TV::SwitchCards(uint chanid, QString channum)
+void TV::SwitchInputs(uint inputid)
 {
+    VERBOSE(VB_PLAYBACK, LOC + QString("SwitchInputd(%1)").arg(inputid));
+
+    if ((uint)activerecorder->GetRecorderNumber() == 
+        CardUtil::GetCardID(inputid))
+    {
+        ToggleInputs(inputid);
+    }
+    else
+    {
+        SwitchCards(0, QString::null, inputid);
+    }
+}
+
+void TV::SwitchCards(uint chanid, QString channum, uint inputid)
+{
     VERBOSE(VB_PLAYBACK, LOC +
-            QString("SwitchCards(%1,'%2')").arg(chanid).arg(channum));
+            QString("SwitchCards(%1,'%2',%3)")
+            .arg(chanid).arg(channum).arg(inputid));
 
     RemoteEncoder *testrec = NULL;
 
     if (!StateIsLiveTV(GetState()) || (activenvp != nvp) || pipnvp)
         return;
 
-    if (/*chanid || */!channum.isEmpty())
+    // If we are switching to a channel not on the current recorder
+    // we need to find the next free recorder with that channel.
+    QStringList reclist;
+    if (!channum.isEmpty())
+        reclist = GetValidRecorderList(chanid, channum);
+    else if (inputid)
     {
-        // If we are switching to a channel not on the current recorder
-        // we need to find the next free recorder with that channel.
-        QStringList reclist = GetValidRecorderList(chanid, channum);
-        testrec = RemoteRequestFreeRecorderFromList(reclist);
+        uint cardid = CardUtil::GetCardID(inputid);
+        if (cardid)
+            reclist.push_back(QString::number(cardid));
     }
 
+    if (!reclist.empty())
+        testrec = RemoteRequestFreeRecorderFromList(reclist);
+
     // If we are just switching recorders find first available recorder.
     if (!testrec)
         testrec = RemoteRequestNextFreeRecorder(recorder->GetRecorderNumber());
@@ -4030,7 +4065,7 @@
     ITVRestart(true);
 }
 
-void TV::ToggleInputs(void)
+void TV::ToggleInputs(uint inputid)
 {
     // If main Nuppel Video Player is paused, unpause it
     if (activenvp == nvp && paused)
@@ -4047,11 +4082,21 @@
         inputname = activerecorder->GetInput();
     else
     {
-        // Pause the backend recorder, send command, and then unpause..
-        PauseLiveTV();
-        lockTimerOn = false;
-        inputname = activerecorder->SetInput("SwitchToNextInput");
-        UnpauseLiveTV();
+        inputname = "SwitchToNextInput";
+        if (inputid)
+        {
+            QString tmp = CardUtil::GetInputName(inputid);
+            inputname = (tmp.isEmpty()) ? inputname : tmp;
+        }
+
+        if (activerecorder->GetInput() != inputname)
+        {
+            // Pause the backend recorder, send command, and then unpause..
+            PauseLiveTV();
+            lockTimerOn = false;
+            inputname = activerecorder->SetInput(inputname);
+            UnpauseLiveTV();
+        }
     }        
 
     // If activenvp is main nvp, show new input in on screen display
@@ -4640,8 +4685,6 @@
 
 void TV::UpdateOSDInput(QString inputname)
 {
-    QString displayName = QString::null;
-
     if (!activerecorder || !tvchain)
         return;
 
@@ -4650,17 +4693,7 @@
     if (inputname.isEmpty())
         inputname = tvchain->GetInputName(-1);
 
-    // Try to get display name
-    MSqlQuery query(MSqlQuery::InitCon());
-    query.prepare("SELECT displayname "
-                  "FROM cardinput "
-                  "WHERE inputname = :INPUTNAME AND "
-                  "      cardid    = :CARDID");
-    query.bindValue(":CARDID",    cardid);
-    query.bindValue(":INPUTNAME", inputname);
-    if (query.exec() && query.isActive() && query.next())
-        displayName = query.value(0).toString();
-
+    QString displayName = CardUtil::GetDisplayName(cardid, inputname);
     // If a display name doesn't exist use cardid and inputname
     if (displayName.isEmpty())
         displayName = QString("%1: %2").arg(cardid).arg(inputname);
@@ -6527,6 +6560,8 @@
             BrowseStart();
         else if (action == "PREVCHAN")
             PreviousChannel();
+        else if (action.left(14) == "SWITCHTOINPUT_")
+            switchToInputId = action.mid(14).toUInt();
         else
         {
             VERBOSE(VB_IMPORTANT, LOC_ERR +
@@ -6789,12 +6824,87 @@
 
     if (freeRecorders)
     {
+        // Picture-in-Picture
         item = new OSDGenericTree(treeMenu, tr("Picture-in-Picture"));
         subitem = new OSDGenericTree(item, tr("Enable/Disable"),
                                      "TOGGLEPIPMODE");
         subitem = new OSDGenericTree(item, tr("Swap PiP/Main"), "SWAPPIP");
         subitem = new OSDGenericTree(item, tr("Change Active Window"),
                                      "TOGGLEPIPWINDOW");
+
+        // Input switching
+        item = NULL;
+
+        QMap<uint,InputInfo> sources;
+        vector<uint> cardids = RemoteRequestFreeRecorderList();
+        uint         cardid  = activerecorder->GetRecorderNumber();
+        cardids.push_back(cardid);
+        stable_sort(cardids.begin(), cardids.end());
+
+        vector<uint> excluded_cardids;
+        excluded_cardids.push_back(cardid);
+
+        InfoMap info;
+        activerecorder->GetChannelInfo(info);
+        uint sourceid = info["sourceid"].toUInt();
+
+        vector<uint>::const_iterator it = cardids.begin();
+        for (; it != cardids.end(); ++it)
+        {
+            vector<InputInfo> inputs = RemoteRequestFreeInputList(
+                *it, excluded_cardids);
+
+            if (inputs.empty())
+                continue;
+
+            if (!item)
+                item = new OSDGenericTree(treeMenu, tr("Switch Input"));
+
+            for (uint i = 0; i < inputs.size(); i++)
+            {
+                // prefer the current card's input in sources list
+                if ((sources.find(inputs[i].sourceid) == sources.end()) ||
+                    ((cardid == inputs[i].cardid) && 
+                     (cardid != sources[inputs[i].sourceid].cardid)))
+                {
+                    sources[inputs[i].sourceid] = inputs[i];
+                }
+
+                // don't add current input to list
+                if ((inputs[i].cardid   == cardid) &&
+                    (inputs[i].sourceid == sourceid))
+                {
+                    continue;
+                }
+
+                QString name = CardUtil::GetDisplayName(inputs[i].inputid);
+                if (name.isEmpty())
+                {
+                    name = tr("C", "Card") + ":" + QString::number(*it) + " " +
+                        tr("I", "Input") + ":" + inputs[i].name;
+                }
+
+                subitem = new OSDGenericTree(
+                    item, name,
+                    QString("SWITCHTOINPUT_%1").arg(inputs[i].inputid));
+            }
+        }
+
+        // Source switching
+
+        // delete current source from list
+        sources.erase(sourceid);
+
+        // create menu if we have any sources left
+        QMap<uint,InputInfo>::const_iterator sit = sources.begin();
+        if (sit != sources.end())
+            item = new OSDGenericTree(treeMenu, tr("Switch Source"));
+        for (; sit != sources.end(); ++sit)
+        {
+            subitem = new OSDGenericTree(
+                item, SourceUtil::GetSourceName((*sit).sourceid),
+                QString("SWITCHTOINPUT_%1").arg((*sit).inputid));
+        }
     }
 
     if (!persistentbrowsemode)
Index: libs/libmythtv/cardutil.cpp
===================================================================
--- libs/libmythtv/cardutil.cpp	(revision 13346)
+++ libs/libmythtv/cardutil.cpp	(working copy)
@@ -377,6 +377,99 @@
     return list;
 }
 
+bool CardUtil::GetInputInfo(
+    uint inputid,
+    QString &inputname, uint &sourceid, uint &cardid, vector<uint> &groupids)
+{
+    MSqlQuery query(MSqlQuery::InitCon());
+    query.prepare("SELECT inputname, sourceid, cardid "
+                  "FROM cardinput "
+                  "WHERE cardinputid = :INPUTID");
+    query.bindValue(":INPUTID", inputid);
+
+    if (!query.exec())
+    {
+        MythContext::DBError("CardUtil::GetCardID(inputid)", query);
+        return false;
+    }
+
+    if (!query.next())
+        return false;
+
+    inputname = query.value(0).toString();
+    sourceid  = query.value(1).toUInt();
+    cardid    = query.value(2).toUInt();
+    groupids  = GetInputGroups(inputid);
+
+    return true;
+}
+
+uint CardUtil::GetCardID(uint inputid)
+{
+    MSqlQuery query(MSqlQuery::InitCon());
+    query.prepare("SELECT cardid "
+                  "FROM cardinput "
+                  "WHERE cardinputid = :INPUTID");
+    query.bindValue(":INPUTID", inputid);
+
+    if (!query.exec())
+        MythContext::DBError("CardUtil::GetCardID(inputid)", query);
+    else if (query.next())
+        return query.value(0).toUInt();
+
+    return 0;
+}
+
+QString CardUtil::GetInputName(uint inputid)
+{
+    MSqlQuery query(MSqlQuery::InitCon());
+    query.prepare("SELECT inputname "
+                  "FROM cardinput "
+                  "WHERE cardinputid = :INPUTID");
+    query.bindValue(":INPUTID", inputid);
+
+    if (!query.exec())
+        MythContext::DBError("CardUtil::GetInputName()", query);
+    else if (query.next())
+        return query.value(0).toString();
+
+    return QString::null;
+}
+
+QString CardUtil::GetDisplayName(uint inputid)
+{
+    MSqlQuery query(MSqlQuery::InitCon());
+    query.prepare("SELECT displayname "
+                  "FROM cardinput "
+                  "WHERE cardinputid = :INPUTID");
+    query.bindValue(":INPUTID", inputid);
+
+    if (!query.exec())
+        MythContext::DBError("CardUtil::GetDisplayName(uint)", query);
+    else if (query.next())
+        return QString::fromUtf8(query.value(0).toString());
+
+    return QString::null;
+}
+
+QString CardUtil::GetDisplayName(uint cardid, const QString &inputname)
+{
+    MSqlQuery query(MSqlQuery::InitCon());
+    query.prepare("SELECT displayname "
+                  "FROM cardinput "
+                  "WHERE inputname = :INPUTNAME AND "
+                  "      cardid    = :CARDID");
+    query.bindValue(":INPUTNAME", inputname);
+    query.bindValue(":CARDID",    cardid);
+
+    if (!query.exec())
+        MythContext::DBError("CardUtil::GetDisplayName(uint,QString)", query);
+    else if (query.next())
+        return QString::fromUtf8(query.value(0).toString());
+
+    return QString::null;
+}
+
 uint CardUtil::CreateInputGroup(const QString &name)
 {
     MSqlQuery query(MSqlQuery::InitCon());
@@ -476,6 +569,59 @@
     return true;
 }
 
+vector<uint> CardUtil::GetInputGroups(uint inputid)
+{
+    vector<uint> list;
+
+    MSqlQuery query(MSqlQuery::InitCon());
+
+    query.prepare(
+        "SELECT inputgroupid "
+        "FROM inputgroup "
+        "WHERE cardinputid = :INPUTID "
+        "ORDER BY inputgroupid, cardinputid, inputgroupname");
+
+    query.bindValue(":INPUTID", inputid);
+
+    if (!query.exec())
+    {
+        MythContext::DBError("CardUtil::GetInputGroups()", query);
+        return list;
+    }
+
+    while (query.next())
+        list.push_back(query.value(0).toUInt());
+
+    return list;
+}
+
+vector<uint> CardUtil::GetGroupCardIDs(uint inputgroupid)
+{
+    vector<uint> list;
+
+    MSqlQuery query(MSqlQuery::InitCon());
+
+    query.prepare(
+        "SELECT cardinputid "
+        "FROM inputgroup "
+        "WHERE inputgroupid = :GROUPID "
+        "ORDER BY inputgroupid, cardinputid, inputgroupname");
+
+    query.bindValue(":GROUPID", inputgroupid);
+
+    if (!query.exec())
+    {
+        MythContext::DBError("CardUtil::GetInputGroups()", query);
+        return list;
+    }
+
+    while (query.next())
+        list.push_back(query.value(0).toUInt());
+
+    return list;
+}
+
+
 bool CardUtil::GetTimeouts(uint cardid,
                            uint &signal_timeout, uint &channel_timeout)
 {
@@ -999,6 +1145,39 @@
     return true;
 }
 
+vector<uint> CardUtil::GetCardList(bool primaryOnly)
+{
+    vector<uint> list;
+
+    MSqlQuery query(MSqlQuery::InitCon());
+    if (primaryOnly)
+    {
+        query.prepare(
+            "SELECT cardid "
+            "FROM capturecard "
+            "WHERE parentid='0' "
+            "ORDER BY cardid");
+    }
+    else
+    {
+        query.prepare(
+            "SELECT cardid "
+            "FROM capturecard "
+            "ORDER BY cardid");
+    }
+
+    if (!query.exec())
+        MythContext::DBError("CardUtil::GetCardList()", query);
+    else
+    {
+        while (query.next())
+            list.push_back(query.value(0).toUInt());
+    }
+
+    return list;
+}
+
+
 QString CardUtil::GetDeviceName(dvb_dev_type_t type, uint cardnum)
 {
     if (DVB_DEV_FRONTEND == type)
Index: libs/libmythtv/remoteutil.cpp
===================================================================
--- libs/libmythtv/remoteutil.cpp	(revision 13296)
+++ libs/libmythtv/remoteutil.cpp	(working copy)
@@ -1,3 +1,4 @@
+#include <qdeepcopy.h>
 #include <qstringlist.h>
 
 #include "util.h"
@@ -6,6 +7,32 @@
 #include "mythcontext.h"
 #include "remoteencoder.h"
 
+InputInfo::InputInfo(
+    const QString &_name, uint _sourceid, uint _inputid, uint _cardid) :
+    name(QDeepCopy<QString>(_name)),
+    sourceid(_sourceid),
+    inputid(_inputid),
+    cardid(_cardid)
+{
+}
+
+InputInfo::InputInfo(const InputInfo &other) :
+    name(QDeepCopy<QString>(other.name)),
+    sourceid(other.sourceid),
+    inputid(other.inputid),
+    cardid(other.cardid)
+{
+}
+
+InputInfo &InputInfo::operator=(const InputInfo &other)
+{
+    name     = QDeepCopy<QString>(other.name);
+    sourceid = other.sourceid;
+    inputid  = other.inputid;
+    cardid   = other.cardid;
+    return *this;
+}
+
 vector<ProgramInfo *> *RemoteGetRecordedList(bool deltype)
 {
     QString str = "QUERY_RECORDINGS ";
@@ -291,6 +318,105 @@
     return new RemoteEncoder(recordernum, hostname, port);
 }
 
+vector<uint> RemoteRequestFreeRecorderList(void)
+{
+    vector<uint> list;
+
+    QStringList strlist = "GET_FREE_RECORDER_LIST";
+
+    if (!gContext->SendReceiveStringList(strlist, true))
+        return list;
+
+    QStringList::const_iterator it = strlist.begin();
+    for (; it != strlist.end(); ++it) 
+        list.push_back((*it).toUInt());
+
+    return list;
+}
+
+vector<InputInfo> RemoteRequestFreeInputList(uint cardid,
+                                             vector<uint> excluded_cardids)
+{
+    vector<InputInfo> list;
+
+    QStringList strlist = QString("QUERY_RECORDER %1").arg(cardid);
+    strlist << "GET_FREE_INPUTS";
+    for (uint i = 0; i < excluded_cardids.size(); i++)
+        strlist << QString::number(excluded_cardids[i]);
+
+    if (!gContext->SendReceiveStringList(strlist))
+        return list;
+
+    QStringList::const_iterator it = strlist.begin();
+    if ((it == strlist.end()) || (*it == "EMPTY_LIST"))
+        return list;
+
+    while (it != strlist.end())
+    {
+        QString inputname = *it;
+        uint sourceid, inputid, cardid;
+        ++it;
+        if (it == strlist.end())
+            break;
+        sourceid = (*it).toUInt();
+        ++it;
+        if (it == strlist.end())
+            break;
+        inputid = (*it).toUInt();
+        ++it;
+        if (it == strlist.end())
+            break;
+        cardid = (*it).toUInt();
+        ++it;
+
+        if (!inputname.isEmpty() && sourceid && inputid && cardid)
+            list.push_back(InputInfo(inputname, sourceid, inputid, cardid));
+    }
+
+    return list;
+}
+
+InputInfo RemoteRequestBusyInputID(uint cardid)
+{
+    InputInfo blank;
+
+    QStringList strlist = QString("QUERY_RECORDER %1").arg(cardid);
+    strlist << "GET_BUSY_INPUT";
+
+    if (!gContext->SendReceiveStringList(strlist))
+        return blank;
+
+    QStringList::const_iterator it = strlist.begin();
+    if ((it == strlist.end()) || (*it == "EMPTY_LIST"))
+        return blank;
+
+    InputInfo info;
+
+    if (it == strlist.end())
+        return blank;
+    info.name = *it;
+
+    ++it;
+    if (it == strlist.end())
+        return blank;
+    info.sourceid = (*it).toUInt();
+
+    ++it;
+    if (it == strlist.end())
+        return blank;
+    info.inputid = (*it).toUInt();
+
+    ++it;
+    if (it == strlist.end())
+        return blank;
+    info.cardid = (*it).toUInt();
+
+    if (!info.name.isEmpty() && info.sourceid && info.inputid && info.cardid)
+        return info;
+
+    return blank;
+}
+
 void RemoteSendMessage(const QString &message)
 {
     QStringList strlist = "MESSAGE";
@@ -330,6 +456,18 @@
         pginfo->FromStringList(strlist, 0);
 }
 
+bool RemoteIsBusy(uint cardid)
+{
+    QStringList strlist = QString("QUERY_REMOTEENCODER %1").arg(cardid);
+    strlist << "IS_BUSY";
+
+    if (!gContext->SendReceiveStringList(strlist) || strlist.empty())
+        return true;
+
+    bool state = strlist[0].toInt();
+    return state;
+}
+
 int RemoteIsRecording(void)
 {
     QStringList strlist = "QUERY_ISRECORDING";
Index: libs/libmythtv/tv_rec.cpp
===================================================================
--- libs/libmythtv/tv_rec.cpp	(revision 13296)
+++ libs/libmythtv/tv_rec.cpp	(working copy)
@@ -59,6 +59,8 @@
 #include "hdhrrecorder.h"
 #include "iptvrecorder.h"
 #include "firewirerecorder.h"
+#include "remoteutil.h"
+#include "sourceutil.h"
 
 #ifdef USING_V4L
 #include "channel.h"
@@ -305,13 +307,13 @@
     SetRingBuffer(NULL);
 }
 
-/** \fn TVRec::GetState()
+/** \fn TVRec::GetState() const
  *  \brief Returns the TVState of the recorder.
  *
  *   If there is a pending state change kState_ChangingState is returned.
  *  \sa EncoderLink::GetState(), \ref recorder_subsystem
  */
-TVState TVRec::GetState(void)
+TVState TVRec::GetState(void) const
 {
     if (changeState)
         return kState_ChangingState;
@@ -2221,12 +2223,12 @@
             HasFlags(kFlagDummyRecorderRunning));
 }
 
-/** \fn TVRec::IsBusy()
+/** \fn TVRec::IsBusy() const
  *  \brief Returns true if the recorder is busy, or will be within
  *         the next 5 seconds.
  *  \sa EncoderLink::IsBusy(), 
  */
-bool TVRec::IsBusy(void)
+bool TVRec::IsBusy(void) const
 {
     QMutexLocker lock(&stateChangeLock);
 
@@ -2717,6 +2719,175 @@
     return ret / 655;
 }
 
+static bool is_input_group_busy(
+    uint                inputid,
+    uint                groupid,
+    const vector<uint> &excluded_cardids,
+    QMap<uint,bool>    &busygrp,
+    QMap<uint,bool>    &busyrec)
+{
+    bool is_busy_grp = busygrp[groupid];
+    if (!is_busy_grp)
+        return false;
+
+    // quick check...
+    vector<uint> cardids = CardUtil::GetGroupCardIDs(groupid);
+    for (uint i = 0; i < cardids.size() && !is_busy_grp; i++)
+    {
+        if (find(excluded_cardids.begin(),
+                 excluded_cardids.end(), cardids[i]) != excluded_cardids.end())
+            continue;
+
+        QMap<uint,bool>::const_iterator it = busyrec.find(cardids[i]);
+        if (it == busyrec.end())
+            busyrec[cardids[i]] = RemoteIsBusy(cardids[i]);
+
+        is_busy_grp = busyrec[cardids[i]];
+    }
+
+    busygrp[groupid] = is_busy_grp;
+    if (!is_busy_grp)
+        return false;
+
+    // check more carefully...
+    vector<InputInfo> conflicts;
+    for (uint i = 0; i < cardids.size(); i++)
+    {
+        if (find(excluded_cardids.begin(),
+                 excluded_cardids.end(), cardids[i]) != excluded_cardids.end())
+            continue;
+
+        QMap<uint,bool>::const_iterator it = busyrec.find(cardids[i]);
+        if (it == busyrec.end())
+            busyrec[cardids[i]] = RemoteIsBusy(cardids[i]);
+
+        if (!busyrec[cardids[i]])
+            continue;
+
+        InputInfo info = RemoteRequestBusyInputID(cardids[i]);
+        conflicts.push_back(info);
+    }
+
+    InputInfo in;
+    vector<uint> groups;
+    in.inputid = inputid;
+    if (!CardUtil::GetInputInfo(
+            inputid, in.name, in.sourceid, in.cardid, groups))
+    {
+        return true;
+    }
+
+    // If they aren't using the same source they are definately busy
+    bool is_busy_input = false;
+    for (uint i = 0; i < conflicts.size() && !is_busy_input; i++)
+        is_busy_input = in.sourceid != conflicts[i].sourceid;
+    if (is_busy_input)
+        return true;
+
+    // If the source's channels aren't digitally tuned then there is a conflict
+    return !SourceUtil::HasDigitalChannel(in.sourceid);
+}
+
+/** \fn TVRec::GetFreeInputs(vector<uint>) const
+ *  \brief Returns TVRec's recorders available inputs.
+ *
+ *  This filters the result of GetConnectedInputs() so that inputs
+ *  that belong to an input group which is busy are removed from the
+ *  list. Recorders in the excluded cardids will not be considered
+ *  busy for the sake of determining free inputs.
+ *
+ *  Then additional information is added to the list, in addition 
+ *  to the name of the input this adds the sourceid, inputid,
+ *  and cardid.
+ *
+ */
+QStringList TVRec::GetFreeInputs(vector<uint> excluded_cardids) const
+{
+    QStringList list = GetConnectedInputs();
+    if (list.empty())
+        return list;
+
+    // Check each input to make sure it doesn't belong to an
+    // input group which is attached to a busy recorder.
+    QStringList new_list;
+    QMap<uint,bool> busygrp;
+    QMap<uint,bool> busyrec;
+
+    busyrec[cardid] = IsBusy();
+
+    QStringList::const_iterator it;
+    for (it = list.begin(); it != list.end(); ++it)
+    {
+        uint inputid = channel->GetInputByName(*it);
+        QString name;
+        uint sourceid, cardid;
+        vector<uint> groups;
+        if (!CardUtil::GetInputInfo(inputid, name, sourceid, cardid, groups))
+            continue;
+
+        bool is_busy_grp = false;
+        for (uint i = 0; i < groups.size() && !is_busy_grp; i++)
+        {
+            is_busy_grp |= is_input_group_busy(
+                inputid, groups[i], excluded_cardids, busygrp, busyrec);
+        }
+
+        if (!is_busy_grp)
+        {
+            new_list += name;
+            new_list += QString::number(sourceid);
+            new_list += QString::number(inputid);
+            new_list += QString::number(cardid);
+        }
+    }
+
+    return new_list;
+}
+
+QStringList TVRec::GetBusyInput(void) const
+{
+    QMutexLocker lock(&stateChangeLock);
+
+    QStringList new_list;
+    if (!channel)
+        return new_list;
+
+    QStringList list = channel->GetConnectedInputs();
+    if (list.empty())
+        return new_list;
+
+    uint inputid = 0;
+    if (GetState() != kState_None)
+        inputid = channel->GetCurrentInputNum();
+
+    if (!inputid && pendingRecording)
+    {
+        int timeLeft = QDateTime::currentDateTime().secsTo(recordPendingStart);
+        if (timeLeft <= 5)
+        {
+            QString channum = QString::null, input = QString::null;
+            if (pendingRecording->GetChannel(channum, input))
+                inputid = channel->GetInputByName(input);
+        }
+    }
+
+    if (inputid)
+    {
+        QString name;
+        uint sourceid, cardid;
+        vector<uint> groups;
+        if (CardUtil::GetInputInfo(inputid, name, sourceid, cardid, groups))
+        {
+            new_list += name;
+            new_list += QString::number(sourceid);
+            new_list += QString::number(inputid);
+            new_list += QString::number(cardid);
+        }
+    }
+
+    return new_list;
+}
+
 /** \fn TVRec::GetConnectedInputs(void) const
  *  \brief Returns TVRec's recorders connected inputs.
  */
Index: libs/libmythtv/tv_rec.h
===================================================================
--- libs/libmythtv/tv_rec.h	(revision 13296)
+++ libs/libmythtv/tv_rec.h	(working copy)
@@ -157,7 +157,7 @@
     /// \brief Tells TVRec to stop event loop
     void Stop(void)             { ClearFlags(kFlagRunMainLoop); }
 
-    TVState GetState(void);
+    TVState GetState(void) const;
     /// \brief Returns "state == kState_RecordingPreRecorded"
     bool IsPlaying(void) { return StateIsPlaying(internalState); }
     /// \brief Returns "state == kState_RecordingRecordedOnly"
@@ -166,7 +166,7 @@
 
     bool SetVideoFiltersForChannel(uint sourceid, const QString &channum);
 
-    bool IsBusy(void);
+    bool IsBusy(void) const;
     bool IsReallyRecording(void);
 
     float GetFramerate(void);
@@ -182,6 +182,8 @@
 
     void SetLiveRecording(int recording);
 
+    QStringList GetFreeInputs(vector<uint> excluded_cardids) const;
+    QStringList GetBusyInput(void) const;
     QStringList GetConnectedInputs(void) const;
     QString     GetInput(void) const;
     QString     SetInput(QString input, uint requestType = kFlagDetect);
@@ -337,7 +339,7 @@
     DBox2DBOptions     dboxOpt;
 
     // State variables
-    QMutex         stateChangeLock;
+    mutable QMutex stateChangeLock;
     TVState        internalState;
     TVState        desiredNextState;
     bool           changeState;
Index: libs/libmythtv/sourceutil.cpp
===================================================================
--- libs/libmythtv/sourceutil.cpp	(revision 13296)
+++ libs/libmythtv/sourceutil.cpp	(working copy)
@@ -9,6 +9,57 @@
 #include "mythdbcon.h"
 #include "util.h"
 
+bool SourceUtil::HasDigitalChannel(uint sourceid)
+{
+    MSqlQuery query(MSqlQuery::InitCon());
+
+    query.prepare(
+        "SELECT mplexid, atsc_minor_chan, serviceid "
+        "FROM videosource "
+        "WHERE sourceid = :SOURCEID");
+    query.bindValue(":SOURCEID", sourceid);
+
+    if (!query.exec())
+    {
+        MythContext::DBError("SourceUtil::HasDigitalChannel()", query);
+        return false;
+    }
+
+    if (!query.next())
+        return false;
+
+    uint mplexid = query.value(0).toUInt();
+    uint minor   = query.value(1).toUInt();
+    uint prognum = query.value(2).toUInt();
+
+    mplexid = (32767 == mplexid) ? 0 : mplexid;
+
+    return mplexid && (minor || prognum);
+}
+
+QString SourceUtil::GetSourceName(uint sourceid)
+{
+    MSqlQuery query(MSqlQuery::InitCon());
+
+    query.prepare(
+        "SELECT name "
+        "FROM videosource "
+        "WHERE sourceid = :SOURCEID");
+    query.bindValue(":SOURCEID", sourceid);
+
+    if (!query.exec())
+    {
+        MythContext::DBError("SourceUtil::GetSourceName()", query);
+        return QString::null;
+    }
+    else if (!query.next())
+    {
+        return QString::null;
+    }
+
+    return query.value(0).toString();
+}
+
 QString SourceUtil::GetChannelSeparator(uint sourceid)
 {
     MSqlQuery query(MSqlQuery::InitCon());
Index: programs/mythbackend/encoderlink.h
===================================================================
--- programs/mythbackend/encoderlink.h	(revision 13296)
+++ programs/mythbackend/encoderlink.h	(working copy)
@@ -76,6 +76,7 @@
     void PauseRecorder(void);
     void SetLiveRecording(int);
     void SetNextLiveTVDir(QString dir);
+    QStringList GetFreeInputs(vector<uint> excluded_cardids) const;
     QStringList GetConnectedInputs(void) const;
     QString GetInput(void) const;
     QString SetInput(QString);
Index: programs/mythbackend/mainserver.cpp
===================================================================
--- programs/mythbackend/mainserver.cpp	(revision 13296)
+++ programs/mythbackend/mainserver.cpp	(working copy)
@@ -2814,6 +2814,27 @@
         enc->SetLiveRecording(recording);
         retlist << "ok";
     }
+    else if (command == "GET_FREE_INPUTS")
+    {
+        vector<uint> excluded_cardids;
+        for (uint i = 2; i < slist.size(); i++)
+            excluded_cardids.push_back(slist[i].toUInt());
+
+        QStringList ret = enc->GetFreeInputs(excluded_cardids);
+
+        if (ret.empty())
+            retlist << "EMPTY_LIST";
+        else
+            retlist += ret;
+    }
+    else if (command == "GET_BUSY_INPUT")
+    {
+        QStringList ret = enc->GetBusyInput();
+        if (ret.empty())
+            retlist << "EMPTY_LIST";
+        else
+            retlist += ret;
+    }
     else if (command == "GET_CONNECTED_INPUTS")
     {
         QStringList ret = enc->GetConnectedInputs();
Index: programs/mythbackend/encoderlink.cpp
===================================================================
--- programs/mythbackend/encoderlink.cpp	(revision 13296)
+++ programs/mythbackend/encoderlink.cpp	(working copy)
@@ -586,6 +586,24 @@
         sock->SetNextLiveTVDir(m_capturecardnum, dir);
 }
 
+/** \fn EncoderLink::GetFreeInputs(vector<uint>) const
+ *  \brief Returns TVRec's recorders inputs available for recording now.
+ *         <b>This only works on local recorders.</b>
+ *
+ *  \sa TVRec::GetFreeInputs(vector<uint>) const
+ */
+QStringList EncoderLink::GetFreeInputs(vector<uint> excluded_cardids) const
+{
+    QStringList list;
+
+    if (local)
+        list = tv->GetFreeInputs(excluded_cardids);
+    else
+        VERBOSE(VB_IMPORTANT, "Should be local only query: GetFreeInputs");
+
+    return list;
+}
+
 /** \fn EncoderLink::GetConnectedInputs(void) const
  *  \brief Returns TVRec's recorders connected inputs.
  *         <b>This only works on local recorders.</b>
@@ -625,7 +643,7 @@
  *
  *   You must call PauseRecorder(void) before calling this.
  *
- *  \param input Input to switch to, or "SwitchToNectInput".
+ *  \param input Input to switch to, or "SwitchToNextInput".
  *  \return input we have switched to
  *  \sa TVRec::SetInput(QString)
  */
