Index: libs/libmythtv/tv_play.cpp
===================================================================
--- libs/libmythtv/tv_play.cpp	(revision 7265)
+++ libs/libmythtv/tv_play.cpp	(working copy)
@@ -199,6 +199,7 @@
       // Channel changing state variables
       channelqueued(false), channelKeys(""), lookForChannel(false),
       lastCC(""), lastCCDir(0), muteTimer(new QTimer(this)),
+      lockTimerOn(false), lockTimeout(3000),
       // previous channel functionality state variables
       prevChanKeyCnt(0), prevChanTimer(new QTimer(this)),
       // channel browsing state variables
@@ -712,6 +713,7 @@
         }
         else
         {
+            lockTimerOn = false;
             prbuffer = new RingBuffer(name, filesize, smudge, recorder);
 
             SET_NEXT();
@@ -736,6 +738,21 @@
 
                 SET_LAST();
             }
+            else
+            {
+                MSqlQuery query(MSqlQuery::InitCon());
+                query.prepare("SELECT channel_timeout "
+                              "FROM capturecard "
+                              "WHERE cardid = :CARDID");
+                query.bindValue(":CARDID", recorder->GetRecorderNumber());
+                if (!query.exec() || !query.isActive() || !query.next())
+                    MythContext::DBError("Getting timeout", query);
+
+                lockTimeout = query.value(0).toInt();
+                cerr<<"set lockTimeout to: "<<lockTimeout<<endl;
+                lockTimer.start();
+                lockTimerOn = true;
+            }
         }
     }
     else if (TRANSITION(kState_WatchingLiveTV, kState_None))
@@ -1269,6 +1286,8 @@
             lastSignalMsg.clear();
         }
 
+        UpdateOSDTimeoutMessage();
+
         if (IsErrored())
         {
             VERBOSE(VB_IMPORTANT, "TVPlay: RunTV encountered "
@@ -1699,6 +1718,10 @@
                     else if (result == 3)
                         recorder->CancelNextRecording();
                 }
+                else if (dialogname == "channel_timed_out")
+                {
+                    lockTimerOn = false;
+                }
 
                 while (GetOSD()->DialogShowing(dialogname))
                 {
@@ -3240,8 +3263,56 @@
     GetOSD()->ClearAllText("program_info");
     GetOSD()->SetText("program_info", infoMap, osd_display_time);
     lastSignalMsgTime.start();
+
+    // Turn of lock timer since we have a signal lock now...
+    if (allGood)
+        lockTimerOn = false;
 }
 
+void TV::UpdateOSDTimeoutMessage(void)
+{
+    QString dlg_name("channel_timed_out");
+    bool timed_out = lockTimerOn && (lockTimer.elapsed() > (int)lockTimeout);
+    OSD *osd = GetOSD();
+
+    if (!osd)
+    {
+        if (timed_out)
+            VERBOSE(VB_IMPORTANT, "Error: You have no OSD, "
+                    "but tuning has already taken too long.");
+        return;
+    }
+
+    if (!timed_out)
+    {
+        if (osd->DialogShowing(dlg_name))
+            osd->TurnDialogOff(dlg_name);
+        return;
+    }
+
+    if (osd->DialogShowing(dlg_name))
+        return;
+
+    // create dialog...
+    static QString chan_up   = GET_KEY("TV Playback", "CHANNELUP");
+    static QString chan_down = GET_KEY("TV Playback", "CHANNELDOWN");
+    static QString tog_in    = GET_KEY("TV Playback", "TOGGLEINPUTS");
+    static QString tog_cards = GET_KEY("TV Playback", "SWITCHCARDS");
+
+    QString message = tr(
+        "You should have gotten a channel lock by now. "
+        "You can continue to wait for a signal, or you "
+        "can change the channels with %1 or %2, change "
+        "input's (%3), capture cards (%4), etc.")
+        .arg(chan_up).arg(chan_down).arg(tog_in).arg(tog_cards);
+
+    QStringList options;
+    options += tr("OK");
+
+    dialogname = dlg_name;
+    osd->NewDialogBox(dialogname, message, options, 0);
+}
+
 void TV::UpdateLCD(void)
 {
     // Make sure the LCD information gets updated shortly
@@ -4917,6 +4988,7 @@
 void TV::PauseLiveTV(void)
 {
     VERBOSE(VB_PLAYBACK, "PauseLiveTV()");
+    lockTimerOn = false;
 
     if (activenvp)
         activenvp->Pause(false);
@@ -4958,4 +5030,7 @@
         UpdateLCD();
         AddPreviousChannel();
     }
+
+    lockTimer.start();
+    lockTimerOn = true;
 }
Index: libs/libmythtv/tv_play.h
===================================================================
--- libs/libmythtv/tv_play.h	(revision 7265)
+++ libs/libmythtv/tv_play.h	(working copy)
@@ -210,6 +210,7 @@
     void UpdateOSD(void);
     void UpdateOSDInput(void);
     void UpdateOSDSignal(const QStringList& strlist);
+    void UpdateOSDTimeoutMessage(void);
 
     void LoadMenu(void);
 
@@ -327,7 +328,7 @@
      *  or decreased to speedup or slowdown playback.
      *  Ignored when doing Fast Forward or Rewind.
      */
-    float normal_speed; 
+    float normal_speed;
 
     float frameRate;     ///< Estimated framerate from recorder
 
@@ -340,6 +341,12 @@
     int     lastCCDir;      ///< Last channel changing direction
     QTimer *muteTimer;      ///< For temp. audio muting during channel changes
 
+    // Channel changing timeout notification variables
+    QTime   lockTimer;
+    bool    lockTimerOn;
+    uint    lockTimeout;
+    
+
     // Previous channel functionality state variables
     str_vec_t prevChan;       ///< Previous channels
     uint      prevChanKeyCnt; ///< Number of repeated channel button presses
Index: libs/libmyth/mythdialogs.cpp
===================================================================
--- libs/libmyth/mythdialogs.cpp	(revision 7265)
+++ libs/libmyth/mythdialogs.cpp	(working copy)
@@ -538,6 +538,26 @@
     BindKey(context, action, keybind);
 }
 
+QString MythMainWindow::GetKey(const QString &context,
+                               const QString &action) const
+{
+    MSqlQuery query(MSqlQuery::InitCon());
+    if (!query.isConnected())
+        return "?";
+
+    query.prepare("SELECT keylist FROM keybindings WHERE "
+                  "context = :CONTEXT AND action = :ACTION AND "
+                  "hostname = :HOSTNAME ;");
+    query.bindValue(":CONTEXT", context);
+    query.bindValue(":ACTION", action);
+    query.bindValue(":HOSTNAME", gContext->GetHostName());
+
+    if (!query.exec() || !query.isActive() || !query.next())
+        return "?";
+
+    return query.value(0).toString();
+}
+
 void MythMainWindow::ClearJump(const QString &destination)
 {
     /* make sure that the jump point exists (using [] would add it)*/
Index: libs/libmyth/mythdialogs.h
===================================================================
--- libs/libmyth/mythdialogs.h	(revision 7265)
+++ libs/libmyth/mythdialogs.h	(working copy)
@@ -59,6 +59,7 @@
 };
 
 #define REG_KEY(a, b, c, d) gContext->GetMainWindow()->RegisterKey(a, b, c, d)
+#define GET_KEY(a, b) gContext->GetMainWindow()->GetKey(a, b)
 #define REG_JUMP(a, b, c, d) gContext->GetMainWindow()->RegisterJump(a, b, c, d)
 #define REG_MEDIA_HANDLER(a, b, c, d, e) gContext->GetMainWindow()->RegisterMediaHandler(a, b, c, d, e)
 #define REG_MEDIAPLAYER(a,b,c) gContext->GetMainWindow()->RegisterMediaPlugin(a, b, c)
@@ -90,6 +91,7 @@
 		 const QString &key);
     void RegisterKey(const QString &context, const QString &action,
                      const QString &description, const QString &key);
+    QString GetKey(const QString &context, const QString &action) const;
 
     void ClearJump(const QString &destination);
     void BindJump(const QString &destination, const QString &key);
