• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files

tv_play.cpp

Go to the documentation of this file.
00001 #include <cstdlib>
00002 #include <cstring>
00003 #include <cmath>
00004 #include <unistd.h>
00005 #include <pthread.h>
00006 
00007 #include <algorithm>
00008 using namespace std;
00009 
00010 #include <qapplication.h>
00011 #include <qregexp.h>
00012 #include <qfile.h>
00013 #include <qtimer.h>
00014 #include <qdir.h>
00015 
00016 #include "mythdbcon.h"
00017 #include "tv_play.h"
00018 #include "tv_rec.h"
00019 #include "osd.h"
00020 #include "osdsurface.h"
00021 #include "osdtypes.h"
00022 #include "osdlistbtntype.h"
00023 #include "mythcontext.h"
00024 #include "dialogbox.h"
00025 #include "remoteencoder.h"
00026 #include "remoteutil.h"
00027 #include "guidegrid.h"
00028 #include "progfind.h"
00029 #include "NuppelVideoPlayer.h"
00030 #include "programinfo.h"
00031 #include "udpnotify.h"
00032 #include "vsync.h"
00033 #include "lcddevice.h"
00034 #include "jobqueue.h"
00035 #include "audiooutput.h"
00036 #include "DisplayRes.h"
00037 #include "signalmonitorvalue.h"
00038 #include "scheduledrecording.h"
00039 #include "previewgenerator.h"
00040 #include "config.h"
00041 #include "livetvchain.h"
00042 #include "playgroup.h"
00043 #include "DVDRingBuffer.h"
00044 #include "datadirect.h"
00045 #include "sourceutil.h"
00046 #include "cardutil.h"
00047 #include "util-osx-cocoa.h"
00048 #include "compat.h"
00049 
00050 #ifndef HAVE_ROUND
00051 #define round(x) ((int) ((x) + 0.5))
00052 #endif
00053 
00054 const int TV::kInitFFRWSpeed  = 0;
00055 const int TV::kMuteTimeout    = 800;
00056 const int TV::kLCDTimeout     = 1;    // seconds
00057 const int TV::kBrowseTimeout  = 30000;
00058 const int TV::kSMExitTimeout  = 2000;
00059 const int TV::kInputKeysMax   = 6;
00060 const int TV::kInputModeTimeout=5000;
00061 const uint TV::kNextSource    = 1;
00062 const uint TV::kPreviousSource= 2;
00063 
00064 #define DEBUG_CHANNEL_PREFIX 0 
00065 #define DEBUG_ACTIONS 0 
00066 #define LOC QString("TV: ")
00067 #define LOC_WARN QString("TV Warning: ")
00068 #define LOC_ERR QString("TV Error: ")
00069 
00070 /*
00071  * \brief stores last program info. maintains info so long as
00072  * mythfrontend is active
00073  */
00074 QStringList TV::lastProgramStringList = QStringList();
00075 
00076 /*
00077  * \brief function pointer for RunPlaybackBox in playbackbox.cpp
00078  */
00079 RUNPLAYBACKBOX TV::RunPlaybackBoxPtr = NULL;
00080 
00083 RUNVIEWSCHEDULED TV::RunViewScheduledPtr = NULL;
00084 
00085 /*
00086  \brief returns true if the recording completed when exiting.
00087  */
00088 bool TV::StartTV (ProgramInfo *tvrec, bool startInGuide, 
00089                 bool inPlaylist, bool initByNetworkCommand)
00090 {
00091     TV *tv = new TV();
00092     bool quitAll = false;
00093     bool showDialogs = true;
00094     bool playCompleted = false;
00095     ProgramInfo *curProgram = NULL;
00096     
00097 
00098     if (tvrec)
00099         curProgram = new ProgramInfo(*tvrec);
00100     
00101     // Initialize TV
00102     if (!tv->Init())
00103     {
00104         VERBOSE(VB_IMPORTANT, "Experienced fatal error:"
00105                 "Failed initializing TV");
00106         delete tv;
00107         return false;
00108     }
00109 
00110     if (!lastProgramStringList.empty())
00111     {
00112         ProgramInfo *p = new ProgramInfo();
00113         p->FromStringList(lastProgramStringList, 0);
00114         tv->setLastProgram(p);
00115         delete p;
00116     }
00117 
00118     gContext->sendPlaybackStart();
00119 
00120     while (!quitAll)
00121     {
00122         int freeRecorders = RemoteGetFreeRecorderCount();
00123         if (curProgram)
00124         {
00125             if (!tv->Playback(curProgram))
00126                 quitAll = true;
00127         }
00128         else if (!freeRecorders)
00129         {
00130             vector<ProgramInfo *> *reclist;
00131             reclist = RemoteGetCurrentlyRecordingList();
00132             if (reclist->empty())
00133             {
00134                 VERBOSE(VB_PLAYBACK, LOC_ERR + 
00135                         "Failed to get recording show list");
00136                 quitAll = true;
00137             }
00138 
00139             int numrecordings = reclist->size();
00140             if (numrecordings > 0)
00141             {
00142                 ProgramInfo *p = NULL;
00143                 QStringList recTitles;
00144                 QString buttonTitle;
00145                 vector<ProgramInfo *>::iterator it = reclist->begin();
00146                 recTitles.append(tr("Exit"));
00147                 while (it != reclist->end())
00148                 {
00149                     p = *it;
00150                     buttonTitle = tr("Chan %1: %2")
00151                         .arg(p->chanstr).arg(p->title);
00152                     recTitles.append(buttonTitle);
00153                     it++;
00154                 }
00155                 DialogCode ret = MythPopupBox::ShowButtonPopup(
00156                     gContext->GetMainWindow(),
00157                     "",
00158                     tr("All Tuners are Busy.\n"
00159                         "Select a Current Recording"),
00160                         recTitles, kDialogCodeButton1);
00161 
00162                 int idx = MythDialog::CalcItemIndex(ret) - 1;
00163                 if ((0 <= idx) && (idx < (int)reclist->size()))
00164                 {
00165                     p = reclist->at(idx);
00166                     curProgram = new ProgramInfo(*p);
00167                 }
00168                 else
00169                 {
00170                     quitAll = true;
00171                 }
00172             }
00173 
00174             if (reclist)
00175                 delete reclist;
00176             continue;
00177         }
00178         else
00179         {
00180             if (!tv->LiveTV(showDialogs, startInGuide))
00181             {
00182                 tv->StopLiveTV();
00183                 quitAll = true;
00184             }
00185         }
00186         
00187         tv->setInPlayList(inPlaylist);
00188         tv->setUnderNetworkControl(initByNetworkCommand);
00189         
00190         // Process Events
00191         while (tv->GetState() != kState_None)
00192         {
00193             qApp->unlock();
00194             qApp->processEvents();
00195             usleep(100000);
00196             qApp->lock();
00197         }
00198 
00199         if (tv->getJumpToProgram())
00200         {
00201             
00202             ProgramInfo *tmpProgram  = tv->getLastProgram();
00203             ProgramInfo *nextProgram = new ProgramInfo(*tmpProgram);
00204 
00205             if (curProgram)
00206             {
00207                 tv->setLastProgram(curProgram);
00208                 delete curProgram;
00209             }
00210             else
00211                 tv->setLastProgram(NULL);
00212 
00213             curProgram = nextProgram;
00214 
00215             continue;
00216         }
00217         
00218         if (tv->WantsToQuit())
00219             quitAll = true;
00220     }
00221 
00222     while (tv->IsMenuRunning())
00223     {
00224         qApp->unlock();
00225         qApp->processEvents();
00226         usleep(100000);
00227         qApp->lock();
00228     }
00229 
00230     // check if the show has reached the end.
00231     if (tvrec && tv->getEndOfRecording())
00232         playCompleted = true;
00233     
00234     bool allowrerecord = tv->getAllowRerecord();
00235     bool deleterecording = tv->getRequestDelete();
00236     
00237     delete tv;
00238     
00239     if (curProgram)
00240     {
00241         if (deleterecording)
00242         {
00243             curProgram->UpdateLastDelete(true);
00244             RemoteDeleteRecording(curProgram, allowrerecord, false);
00245         }
00246         else if (!curProgram->isVideo)
00247         {
00248             lastProgramStringList.clear();
00249             curProgram->ToStringList(lastProgramStringList);
00250         }
00251         
00252         delete curProgram;
00253     }
00254 
00255     gContext->sendPlaybackEnd();
00256 
00257     return playCompleted;
00258 }
00259 
00264 void TV::SetFuncPtr(const char *string, void *lptr)
00265 {
00266     QString name(string);
00267     if (name == "playbackbox")
00268         RunPlaybackBoxPtr = (RUNPLAYBACKBOX)lptr;
00269     else if (name == "viewscheduled")
00270         RunViewScheduledPtr = (RUNVIEWSCHEDULED)lptr;
00271 }
00272 
00273 void TV::InitKeys(void)
00274 {
00275     REG_KEY("TV Frontend", "PAGEUP", "Page Up", "3");
00276     REG_KEY("TV Frontend", "PAGEDOWN", "Page Down", "9");
00277     REG_KEY("TV Frontend", "PAGETOP", "Page to top of list", "");
00278     REG_KEY("TV Frontend", "PAGEMIDDLE", "Page to middle of list", "");
00279     REG_KEY("TV Frontend", "PAGEBOTTOM", "Page to bottom of list", "");
00280     REG_KEY("TV Frontend", "DELETE", "Delete Program", "D");
00281     REG_KEY("TV Frontend", "PLAYBACK", "Play Program", "P");
00282     REG_KEY("TV Frontend", "TOGGLERECORD", "Toggle recording status of current "
00283             "program", "R");
00284     REG_KEY("TV Frontend", "DAYLEFT", "Page the program guide back one day", 
00285             "Home,7");
00286     REG_KEY("TV Frontend", "DAYRIGHT", "Page the program guide forward one day",
00287             "End,1");
00288     REG_KEY("TV Frontend", "PAGELEFT", "Page the program guide left",
00289             ",,<");
00290     REG_KEY("TV Frontend", "PAGERIGHT", "Page the program guide right",
00291             ">,.");
00292     REG_KEY("TV Frontend", "TOGGLEFAV", "Toggle the current channel as a "
00293             "favorite", "?");
00294     REG_KEY("TV Frontend", "TOGGLEEPGORDER", "Reverse the channel order "
00295             "in the program guide", "0");
00296     REG_KEY("TV Frontend", "GUIDE", "Show the Program Guide", "S");
00297     REG_KEY("TV Frontend", "FINDER", "Show the Program Finder", "#");
00298     REG_KEY("TV Frontend", "NEXTFAV", "Toggle showing all channels or just "
00299             "favorites in the program guide.", "/");
00300     REG_KEY("TV Frontend", "CHANUPDATE", "Switch channels without exiting "
00301             "guide in Live TV mode.", "X");
00302     REG_KEY("TV Frontend", "VOLUMEDOWN", "Volume down", "[,{,F10,Volume Down");
00303     REG_KEY("TV Frontend", "VOLUMEUP",   "Volume up",   "],},F11,Volume Up");
00304     REG_KEY("TV Frontend", "MUTE",       "Mute",        "|,\\,F9,Volume Mute");
00305     REG_KEY("TV Frontend", "RANKINC", "Increase program or channel rank",
00306             "Right");
00307     REG_KEY("TV Frontend", "RANKDEC", "Decrease program or channel rank",
00308             "Left");
00309     REG_KEY("TV Frontend", "UPCOMING", "List upcoming episodes", "O");
00310     REG_KEY("TV Frontend", "DETAILS", "Show program details", "U");
00311     REG_KEY("TV Frontend", "VIEWCARD", "Switch Capture Card view", "Y");
00312     REG_KEY("TV Frontend", "VIEWINPUT", "Switch Capture Card view", "C");
00313     REG_KEY("TV Frontend", "CUSTOMEDIT", "Edit Custom Record Rule", "E");
00314     REG_KEY("TV Frontend", "CHANGERECGROUP", "Change Recording Group", "");
00315     REG_KEY("TV Frontend", "CHANGEGROUPVIEW", "Change Group View", "");
00316 
00317     REG_KEY("TV Playback", "CLEAROSD", "Clear OSD", "Backspace");
00318     REG_KEY("TV Playback", "PAUSE", "Pause", "P");
00319     REG_KEY("TV Playback", "DELETE", "Delete Program", "D");
00320     REG_KEY("TV Playback", "SEEKFFWD", "Fast Forward", "Right");
00321     REG_KEY("TV Playback", "SEEKRWND", "Rewind", "Left");
00322     REG_KEY("TV Playback", "ARBSEEK", "Arbitrary Seek", "*");
00323     REG_KEY("TV Playback", "CHANNELUP", "Channel up", "Up");
00324     REG_KEY("TV Playback", "CHANNELDOWN", "Channel down", "Down");
00325     REG_KEY("TV Playback", "NEXTFAV", "Switch to the next favorite channel",
00326             "/");
00327     REG_KEY("TV Playback", "PREVCHAN", "Switch to the previous channel", "H");
00328     REG_KEY("TV Playback", "JUMPFFWD", "Jump ahead", "PgDown");
00329     REG_KEY("TV Playback", "JUMPRWND", "Jump back", "PgUp");
00330     REG_KEY("TV Playback", "JUMPBKMRK", "Jump to bookmark", "K");
00331     REG_KEY("TV Playback", "FFWDSTICKY", "Fast Forward (Sticky) or Forward one "
00332             "frame while paused", ">,.");
00333     REG_KEY("TV Playback", "RWNDSTICKY", "Rewind (Sticky) or Rewind one frame "
00334             "while paused", ",,<");
00335     REG_KEY("TV Playback", "NEXTSOURCE",    "Next Video Source",     "Y");
00336     REG_KEY("TV Playback", "PREVSOURCE",    "Previous Video Source", "Ctrl+Y");
00337     REG_KEY("TV Playback", "NEXTINPUT",     "Next Input",            "C");
00338     REG_KEY("TV Playback", "NEXTCARD",      "Next Card",             "");
00339     REG_KEY("TV Playback", "SKIPCOMMERCIAL", "Skip Commercial", "Z,End");
00340     REG_KEY("TV Playback", "SKIPCOMMBACK", "Skip Commercial (Reverse)",
00341             "Q,Home");
00342     REG_KEY("TV Playback", "JUMPSTART", "Jump to the start of the recording.", "Ctrl+B");            
00343     REG_KEY("TV Playback", "TOGGLEBROWSE", "Toggle channel browse mode", "O");
00344     REG_KEY("TV Playback", "TOGGLERECORD", "Toggle recording status of current "
00345             "program", "R");
00346     REG_KEY("TV Playback", "TOGGLEFAV", "Toggle the current channel as a "
00347             "favorite", "?");
00348     REG_KEY("TV Playback", "VOLUMEDOWN", "Volume down", "[,{,F10,Volume Down");
00349     REG_KEY("TV Playback", "VOLUMEUP",   "Volume up",   "],},F11,Volume Up");
00350     REG_KEY("TV Playback", "MUTE",       "Mute",        "|,\\,F9,Volume Mute");
00351     REG_KEY("TV Playback", "TOGGLEPIPMODE", "Toggle Picture-in-Picture mode",
00352             "V");
00353     REG_KEY("TV Playback", "TOGGLEPIPWINDOW", "Toggle active PiP window", "B");
00354     REG_KEY("TV Playback", "SWAPPIP", "Swap PiP/Main", "N");
00355     REG_KEY("TV Playback", "TOGGLEASPECT",
00356             "Toggle the video aspect ratio", "Ctrl+W");
00357     REG_KEY("TV Playback", "TOGGLEFILL", "Next Preconfigured Zoom mode", "W");
00358 
00359     REG_KEY("TV Playback", "TOGGLECC",      "Toggle any captions",   "T");
00360     REG_KEY("TV Playback", "TOGGLETTC",     "Toggle Teletext Captions","");
00361     REG_KEY("TV Playback", "TOGGLESUBTITLE","Toggle Subtitles",      "");
00362     REG_KEY("TV Playback", "TOGGLECC608",   "Toggle VBI CC",         "");
00363     REG_KEY("TV Playback", "TOGGLECC708",   "Toggle ATSC CC",        "");
00364 
00365     REG_KEY("TV Playback", "TOGGLETTM",     "Toggle Teletext Menu",  "");
00366 
00367     REG_KEY("TV Playback", "SELECTAUDIO_0", "Play audio track 1",    "");
00368     REG_KEY("TV Playback", "SELECTAUDIO_1", "Play audio track 2",    "");
00369     REG_KEY("TV Playback", "SELECTSUBTITLE_0","Display subtitle 1",  "");
00370     REG_KEY("TV Playback", "SELECTSUBTITLE_1","Display subtitle 2",  "");
00371     REG_KEY("TV Playback", "SELECTCC608_0", "Display VBI CC1",       "");
00372     REG_KEY("TV Playback", "SELECTCC608_1", "Display VBI CC2",       "");
00373     REG_KEY("TV Playback", "SELECTCC608_2", "Display VBI CC3",       "");
00374     REG_KEY("TV Playback", "SELECTCC608_3", "Display VBI CC4",       "");
00375     REG_KEY("TV Playback", "SELECTCC708_0", "Display ATSC CC1",      "");
00376     REG_KEY("TV Playback", "SELECTCC708_1", "Display ATSC CC2",      "");
00377     REG_KEY("TV Playback", "SELECTCC708_2", "Display ATSC CC3",      "");
00378     REG_KEY("TV Playback", "SELECTCC708_3", "Display ATSC CC4",      "");
00379 
00380     REG_KEY("TV Playback", "NEXTAUDIO",    "Next audio track",         "+");
00381     REG_KEY("TV Playback", "PREVAUDIO",    "Previous audio track",     "-");
00382     REG_KEY("TV Playback", "NEXTSUBTITLE", "Next subtitle track",      "");
00383     REG_KEY("TV Playback", "PREVSUBTITLE", "Previous subtitle track",  "");
00384     REG_KEY("TV Playback", "NEXTCC608",    "Next VBI CC track",        "");
00385     REG_KEY("TV Playback", "PREVCC608",    "Previous VBI CC track",    "");
00386     REG_KEY("TV Playback", "NEXTCC708",    "Next ATSC CC track",       "");
00387     REG_KEY("TV Playback", "PREVCC708",    "Previous ATSC CC track",   "");
00388     REG_KEY("TV Playback", "NEXTCC",       "Next of any captions",     "");
00389 
00390     REG_KEY("TV Playback", "NEXTSCAN",    "Next video scan overidemode", "");
00391     REG_KEY("TV Playback", "QUEUETRANSCODE", "Queue the current recording for "
00392             "transcoding", "X");
00393     REG_KEY("TV Playback", "SPEEDINC", "Increase the playback speed", "U");
00394     REG_KEY("TV Playback", "SPEEDDEC", "Decrease the playback speed", "J");
00395     REG_KEY("TV Playback", "ADJUSTSTRETCH", "Turn on time stretch control", "A");
00396     REG_KEY("TV Playback", "STRETCHINC", "Increase time stretch speed", "");
00397     REG_KEY("TV Playback", "STRETCHDEC", "Decrease time stretch speed", "");
00398     REG_KEY("TV Playback", "TOGGLESTRETCH", "Toggle time stretch speed", "");
00399     REG_KEY("TV Playback", "TOGGLEAUDIOSYNC",
00400             "Turn on audio sync adjustment controls", "");
00401     REG_KEY("TV Playback", "TOGGLEPICCONTROLS",
00402             "Playback picture adjustments",                    "F");
00403     REG_KEY("TV Playback", "TOGGLECHANCONTROLS",
00404             "Recording picture adjustments for this channel",  "Ctrl+G");
00405     REG_KEY("TV Playback", "TOGGLERECCONTROLS",
00406             "Recording picture adjustments for this recorder", "G");
00407     REG_KEY("TV Playback", "TOGGLEEDIT", "Start Edit Mode", "E");
00408     REG_KEY("TV Playback", "CYCLECOMMSKIPMODE", "Cycle Commercial Skip mode", "");
00409     REG_KEY("TV Playback", "GUIDE", "Show the Program Guide", "S");
00410     REG_KEY("TV Playback", "FINDER", "Show the Program Finder", "#");
00411     REG_KEY("TV Playback", "TOGGLESLEEP", "Toggle the Sleep Timer", "F8");
00412     REG_KEY("TV Playback", "PLAY", "Play", "Ctrl+P");
00413     REG_KEY("TV Playback", "JUMPPREV", "Jump to previously played recording", "");
00414     REG_KEY("TV Playback", "JUMPREC", "Display menu of recorded programs to jump to", "");
00415     REG_KEY("TV Playback", "VIEWSCHEDULED", "Display scheduled recording list", "");
00416     REG_KEY("TV Playback", "SIGNALMON", "Monitor Signal Quality", "Alt+F7");
00417     REG_KEY("TV Playback", "JUMPTODVDROOTMENU", "Jump to the DVD Root Menu", "");
00418     REG_KEY("TV Playback", "EXITSHOWNOPROMPTS","Exit Show without any prompts", "");
00419     REG_KEY("TV Playback", "SCREENSHOT","Save screenshot of current video frame", "");
00420 
00421     /* Interactive Television keys */
00422     REG_KEY("TV Playback", "MENURED",    "Menu Red",    "F2");
00423     REG_KEY("TV Playback", "MENUGREEN",  "Menu Green",  "F3");
00424     REG_KEY("TV Playback", "MENUYELLOW", "Menu Yellow", "F4");
00425     REG_KEY("TV Playback", "MENUBLUE",   "Menu Blue",   "F5");
00426     REG_KEY("TV Playback", "TEXTEXIT",   "Menu Exit",   "F6");
00427     REG_KEY("TV Playback", "MENUTEXT",   "Menu Text",   "F7");
00428     REG_KEY("TV Playback", "MENUEPG",    "Menu EPG",    "F12");
00429 
00430     /* Editing keys */
00431     REG_KEY("TV Editing", "CLEARMAP", "Clear editing cut points", "C,Q,Home");
00432     REG_KEY("TV Editing", "INVERTMAP", "Invert Begin/End cut points", "I");
00433     REG_KEY("TV Editing", "LOADCOMMSKIP", "Load cut list from commercial skips",
00434             "Z,End");
00435     REG_KEY("TV Editing", "NEXTCUT", "Jump to the next cut point", "PgDown");
00436     REG_KEY("TV Editing", "PREVCUT", "Jump to the previous cut point", "PgUp");
00437     REG_KEY("TV Editing", "BIGJUMPREW", "Jump back 10x the normal amount",
00438             ",,<");
00439     REG_KEY("TV Editing", "BIGJUMPFWD", "Jump forward 10x the normal amount",
00440             ">,.");
00441     REG_KEY("TV Editing", "TOGGLEEDIT", "Exit out of Edit Mode", "E");
00442 
00443     /* Teletext keys */
00444     REG_KEY("Teletext Menu", "NEXTPAGE",    "Next Page",             "Down");
00445     REG_KEY("Teletext Menu", "PREVPAGE",    "Previous Page",         "Up");
00446     REG_KEY("Teletext Menu", "NEXTSUBPAGE", "Next Subpage",          "Right");
00447     REG_KEY("Teletext Menu", "PREVSUBPAGE", "Previous Subpage",      "Left");
00448     REG_KEY("Teletext Menu", "TOGGLETT",    "Toggle Teletext",       "T"); 
00449     REG_KEY("Teletext Menu", "MENURED",     "Menu Red",              "F2");
00450     REG_KEY("Teletext Menu", "MENUGREEN",   "Menu Green",            "F3");
00451     REG_KEY("Teletext Menu", "MENUYELLOW",  "Menu Yellow",           "F4");
00452     REG_KEY("Teletext Menu", "MENUBLUE",    "Menu Blue",             "F5");
00453     REG_KEY("Teletext Menu", "MENUWHITE",   "Menu White",            "F6");
00454     REG_KEY("Teletext Menu", "TOGGLEBACKGROUND","Toggle Background", "F7");
00455     REG_KEY("Teletext Menu", "REVEAL",      "Reveal hidden Text",    "F8");
00456 
00457 /*
00458   keys already used:
00459 
00460   Global:           I   M              0123456789
00461   Playback: ABCDEFGH JK  NOPQRSTUVWXYZ
00462   Frontend:   CD          OP R  U  XY  01 3   7 9
00463   Editing:    C E   I       Q        Z
00464   Teletext:                    T
00465 
00466   Playback: <>,.?/|[]{}\+-*#^
00467   Frontend: <>,.?/
00468   Editing:  <>,.
00469 
00470   Global:   PgDown, PgUp,  Right, Left, Home, End, Up, Down, 
00471   Playback: PgDown, PgUp,  Right, Left, Home, End, Up, Down, Backspace,
00472   Frontend:                Right, Left, Home, End
00473   Editing:  PgDown, PgUp,               Home, End
00474   Teletext:                Right, Left,            Up, Down,
00475 
00476   Global:   Return, Enter, Space, Esc
00477 
00478   Global:   F1,
00479   Playback:                   F7,F8,F9,F10,F11
00480   Teletext     F2,F3,F4,F5,F6,F7,F8
00481   ITV          F2,F3,F4,F5,F6,F7,F12
00482 
00483   Playback: Ctrl-B,Ctrl-G,Ctrl-Y
00484 */
00485 }
00486 
00487 void *SpawnDecode(void *param)
00488 {
00489     // OS X needs a garbage collector allocated..
00490     void *decoder_thread_pool = CreateOSXCocoaPool();
00491     NuppelVideoPlayer *nvp = (NuppelVideoPlayer *)param;
00492     nvp->StartPlaying();
00493     nvp->StopPlaying();
00494     DeleteOSXCocoaPool(decoder_thread_pool);
00495     return NULL;
00496 }
00497 
00502 TV::TV(void)
00503     : QObject(NULL, "TV"),
00504       // Configuration variables from database
00505       baseFilters(""), 
00506       db_channel_format("<num> <sign>"),
00507       db_time_format("h:mm AP"), db_short_date_format("M/d"),
00508       fftime(0), rewtime(0),
00509       jumptime(0), smartChannelChange(false),
00510       MuteIndividualChannels(false), arrowAccel(false),
00511       osd_general_timeout(2), osd_prog_info_timeout(3),
00512       autoCommercialSkip(CommSkipOff), tryUnflaggedSkip(false),
00513       smartForward(false), stickykeys(0),
00514       ff_rew_repos(1.0f), ff_rew_reverse(false),
00515       jumped_back(false),
00516       vbimode(VBIMode::None),
00517       // State variables
00518       internalState(kState_None),
00519       switchToInputId(0),
00520       menurunning(false), runMainLoop(false), wantsToQuit(true),
00521       exitPlayer(false), paused(false), errored(false),
00522       stretchAdjustment(false),
00523       audiosyncAdjustment(false), audiosyncBaseline(LONG_LONG_MIN),
00524       editmode(false),     zoomMode(false),
00525       sigMonMode(false),
00526       update_osd_pos(false), endOfRecording(false), 
00527       requestDelete(false),  allowRerecord(false),
00528       doSmartForward(false),
00529       queuedTranscode(false), getRecorderPlaybackInfo(false),
00530       adjustingPicture(kAdjustingPicture_None),
00531       adjustingPictureAttribute(kPictureAttribute_None),
00532       askAllowType(kAskAllowCancel), askAllowLock(true),
00533       ignoreKeys(false), needToSwapPIP(false), needToJumpMenu(false),
00534       // Channel Editing
00535       chanEditMapLock(true), ddMapSourceId(0), ddMapLoaderRunning(false),
00536       // Sleep Timer
00537       sleep_index(0), sleepTimer(new QTimer(this)),
00538       // Idle Timer
00539       idleTimer(new QTimer(this)),
00540       // Key processing buffer, lock, and state
00541       keyRepeat(true), keyrepeatTimer(new QTimer(this)),
00542       // Fast forward state
00543       doing_ff_rew(0), ff_rew_index(0), speed_index(0),
00544       // Time Stretch state
00545       normal_speed(1.0f), prev_speed(1.5f), 
00546       // Estimated framerate from recorder
00547       frameRate(30.0f),
00548       // CC/Teletext input state variables
00549       ccInputMode(false), ccInputModeExpires(QTime::currentTime()),
00550       // Arbritary seek input state variables
00551       asInputMode(false), asInputModeExpires(QTime::currentTime()),
00552       // Channel changing state variables
00553       queuedChanNum(""),
00554       muteTimer(new QTimer(this)),
00555       lockTimerOn(false),
00556       // previous channel functionality state variables
00557       prevChanKeyCnt(0), prevChanTimer(new QTimer(this)),
00558       // channel browsing state variables
00559       browsemode(false), persistentbrowsemode(false),
00560       browseTimer(new QTimer(this)),
00561       browsechannum(""), browsechanid(""), browsestarttime(""),
00562       // Program Info for currently playing video
00563       recorderPlaybackInfo(NULL),
00564       playbackinfo(NULL), playbackLen(0),
00565       lastProgram(NULL), jumpToProgram(false),
00566       inPlaylist(false), underNetworkControl(false),
00567       isnearend(false),
00568       // Video Players
00569       nvp(NULL), pipnvp(NULL), activenvp(NULL),
00570       // Remote Encoders
00571       recorder(NULL), piprecorder(NULL), activerecorder(NULL),
00572       switchToRec(NULL), lastrecordernum(-1),
00573       // LiveTVChain
00574       tvchain(NULL), piptvchain(NULL),
00575       // RingBuffers
00576       prbuffer(NULL), piprbuffer(NULL), activerbuffer(NULL),
00577       // OSD info
00578       dialogname(""), treeMenu(NULL), udpnotify(NULL), osdlock(true),
00579       // LCD Info
00580       lcdTitle(""), lcdSubtitle(""), lcdCallsign(""),
00581       // Window info (GUI is optional, transcoding, preview img, etc)
00582       myWindow(NULL), embedWinID(0), embedBounds(0,0,0,0)
00583 {
00584     for (uint i = 0; i < 2; i++)
00585     {
00586         pseudoLiveTVRec[i]   = NULL;
00587         pseudoLiveTVState[i] = kPseudoNormalLiveTV;
00588     }
00589 
00590     lastLcdUpdate = QDateTime::currentDateTime();
00591     lastLcdUpdate.addYears(-1); // make last LCD update last year..
00592     lastSignalMsgTime.start();
00593     lastSignalMsgTime.addMSecs(-2 * kSMExitTimeout);
00594 
00595     sleep_times.push_back(SleepTimerInfo(QObject::tr("Off"),       0));
00596     sleep_times.push_back(SleepTimerInfo(QObject::tr("30m"),   30*60));
00597     sleep_times.push_back(SleepTimerInfo(QObject::tr("1h"),    60*60));
00598     sleep_times.push_back(SleepTimerInfo(QObject::tr("1h30m"), 90*60));
00599     sleep_times.push_back(SleepTimerInfo(QObject::tr("2h"),   120*60));
00600 
00601     gContext->addListener(this);
00602     gContext->addCurrentLocation("Playback");
00603 
00604     connect(prevChanTimer,    SIGNAL(timeout()), SLOT(SetPreviousChannel()));
00605     connect(browseTimer,      SIGNAL(timeout()), SLOT(BrowseEndTimer()));
00606     connect(muteTimer,        SIGNAL(timeout()), SLOT(UnMute()));
00607     connect(keyrepeatTimer,   SIGNAL(timeout()), SLOT(KeyRepeatOK()));
00608     connect(sleepTimer,       SIGNAL(timeout()), SLOT(SleepEndTimer()));
00609     connect(idleTimer,       SIGNAL(timeout()), SLOT(IdleDialog()));
00610 }
00611 
00618 bool TV::Init(bool createWindow)
00619 {
00620     MSqlQuery query(MSqlQuery::InitCon());
00621     if (!query.isConnected())
00622     {
00623         VERBOSE(VB_IMPORTANT, LOC_ERR +
00624                 "Init(): Could not open DB connection in player");
00625         errored = true;
00626         return false;
00627     }
00628 
00629     baseFilters         += gContext->GetSetting("CustomFilters");
00630     db_channel_format    =gContext->GetSetting("ChannelFormat","<num> <sign>");
00631     db_time_format       = gContext->GetSetting("TimeFormat", "h:mm AP");
00632     db_short_date_format = gContext->GetSetting("ShortDateFormat", "M/d");
00633     smartChannelChange   = gContext->GetNumSetting("SmartChannelChange", 0);
00634     MuteIndividualChannels=gContext->GetNumSetting("IndividualMuteControl", 0);
00635     arrowAccel           = gContext->GetNumSetting("UseArrowAccels", 1);
00636     persistentbrowsemode = gContext->GetNumSetting("PersistentBrowseMode", 0);
00637     osd_general_timeout  = gContext->GetNumSetting("OSDGeneralTimeout", 2);
00638     osd_prog_info_timeout= gContext->GetNumSetting("OSDProgramInfoTimeout", 3);
00639     autoCommercialSkip   = (enum commSkipMode)gContext->GetNumSetting(
00640                             "AutoCommercialSkip", CommSkipOff);
00641     tryUnflaggedSkip     = gContext->GetNumSetting("TryUnflaggedSkip", 0);
00642     smartForward         = gContext->GetNumSetting("SmartForward", 0);
00643     stickykeys           = gContext->GetNumSetting("StickyKeys");
00644     ff_rew_repos         = gContext->GetNumSetting("FFRewReposTime", 100)/100.0;
00645     ff_rew_reverse       = gContext->GetNumSetting("FFRewReverse", 1);
00646     int def[8] = { 3, 5, 10, 20, 30, 60, 120, 180 };
00647     for (uint i = 0; i < sizeof(def)/sizeof(def[0]); i++)
00648         ff_rew_speeds.push_back(
00649             gContext->GetNumSetting(QString("FFRewSpeed%1").arg(i), def[i]));
00650 
00651     vbimode = VBIMode::Parse(gContext->GetSetting("VbiFormat"));
00652 
00653     if (createWindow)
00654     {
00655         MythMainWindow *mainWindow = gContext->GetMainWindow();
00656         bool fullscreen = !gContext->GetNumSetting("GuiSizeForTV", 0);
00657         bool switchMode = gContext->GetNumSetting("UseVideoModes", 0);
00658 
00659         saved_gui_bounds = QRect(mainWindow->geometry().topLeft(),
00660                                  mainWindow->size());
00661 
00662         // adjust for window manager wierdness.
00663         {
00664             int xbase, width, ybase, height;
00665             float wmult, hmult;
00666             gContext->GetScreenSettings(xbase, width, wmult,
00667                                         ybase, height, hmult);
00668             if ((abs(saved_gui_bounds.x()-xbase) < 3) &&
00669                 (abs(saved_gui_bounds.y()-ybase) < 3))
00670             {
00671                 saved_gui_bounds = QRect(QPoint(xbase, ybase),
00672                                          mainWindow->size());
00673             }
00674         }
00675 
00676         // if width && height are zero users expect fullscreen playback
00677         if (!fullscreen)
00678         {
00679             int gui_width = 0, gui_height = 0;
00680             gContext->GetResolutionSetting("Gui", gui_width, gui_height);
00681             fullscreen |= (0 == gui_width && 0 == gui_height);
00682         }
00683 
00684         player_bounds = saved_gui_bounds;
00685         if (fullscreen)
00686         {
00687             int xbase, width, ybase, height;
00688             gContext->GetScreenBounds(xbase, ybase, width, height);
00689             player_bounds = QRect(xbase, ybase, width, height);
00690         }
00691 
00692         // main window sizing
00693         DisplayRes *display_res = DisplayRes::GetDisplayRes();
00694         int maxWidth = 1920, maxHeight = 1440;
00695         if (switchMode && display_res)
00696         {
00697             // The very first Resize needs to be the maximum possible
00698             // desired res, because X will mask off anything outside
00699             // the initial dimensions
00700             maxWidth = display_res->GetMaxWidth();
00701             maxHeight = display_res->GetMaxHeight();
00702 
00703             // bit of a hack, but it's ok if the window is too
00704             // big in fullscreen mode
00705             if (fullscreen)
00706             {
00707                 player_bounds.setSize(QSize(maxWidth, maxHeight));
00708 
00709                 // resize possibly avoids a bug on some systems
00710                 mainWindow->resize(player_bounds.size());
00711             }
00712         }
00713 
00714         // player window sizing
00715         myWindow = new MythDialog(mainWindow, "video playback window");
00716 
00717         myWindow->installEventFilter(this);
00718         myWindow->setNoErase();
00719         QRect win_bounds(0, 0, player_bounds.width(), player_bounds.height());
00720         myWindow->setGeometry(win_bounds);
00721         myWindow->setFixedSize(win_bounds.size());
00722 
00723         // resize main window
00724         mainWindow->setGeometry(player_bounds);
00725         mainWindow->setFixedSize(player_bounds.size());
00726 
00727         // finally we put the player window on screen...
00728         myWindow->show();
00729         myWindow->setBackgroundColor(Qt::black);
00730         qApp->processEvents();
00731     }
00732 
00733     mainLoopCondLock.lock();
00734     pthread_create(&event, NULL, EventThread, this);
00735     mainLoopCond.wait(&mainLoopCondLock);
00736     mainLoopCondLock.unlock();
00737 
00738     return !IsErrored();
00739 }
00740 
00741 TV::~TV(void)
00742 {
00743     QMutexLocker locker(&osdlock); // prevent UpdateOSDSignal from continuing.
00744 
00745     if (sleepTimer)
00746     {
00747         sleepTimer->disconnect();
00748         sleepTimer->deleteLater();
00749         sleepTimer = NULL;
00750     }
00751 
00752     if (idleTimer)
00753     {
00754         idleTimer->disconnect();
00755         idleTimer->deleteLater();
00756         idleTimer = NULL;
00757     }
00758 
00759     if (keyrepeatTimer)
00760     {
00761         keyrepeatTimer->disconnect();
00762         keyrepeatTimer->deleteLater();
00763         keyrepeatTimer = NULL;
00764     }
00765 
00766     if (muteTimer)
00767     {
00768         muteTimer->disconnect();
00769         muteTimer->deleteLater();
00770         muteTimer = NULL;
00771     }
00772 
00773     if (prevChanTimer)
00774     {
00775         prevChanTimer->disconnect();
00776         prevChanTimer->deleteLater();
00777         prevChanTimer = NULL;
00778     }
00779 
00780     if (browseTimer)
00781     {
00782         browseTimer->disconnect();
00783         browseTimer->deleteLater();
00784         browseTimer = NULL;
00785     }
00786 
00787     gContext->removeListener(this);
00788     gContext->removeCurrentLocation();
00789 
00790     runMainLoop = false;
00791     pthread_join(event, NULL);
00792 
00793     if (prbuffer)
00794         delete prbuffer;
00795     if (nvp)
00796         delete nvp;
00797     if (myWindow)
00798     {
00799         myWindow->deleteLater();
00800         myWindow = NULL;
00801         MythMainWindow* mwnd = gContext->GetMainWindow();
00802         mwnd->resize(saved_gui_bounds.size());
00803         mwnd->setFixedSize(saved_gui_bounds.size());
00804         mwnd->show();
00805         if (!gContext->GetNumSetting("GuiSizeForTV", 0))
00806             mwnd->move(saved_gui_bounds.topLeft());
00807     }
00808     if (recorderPlaybackInfo)
00809         delete recorderPlaybackInfo;
00810 
00811     if (treeMenu)
00812         delete treeMenu;
00813 
00814     if (playbackinfo)
00815         delete playbackinfo;
00816 
00817     if (lastProgram)
00818         delete lastProgram;
00819 
00820     if (class LCD * lcd = LCD::Get())
00821         lcd->switchToTime();
00822 
00823     if (tvchain)
00824     {
00825         VERBOSE(VB_IMPORTANT, LOC + "Deleting TV Chain in destructor");
00826         tvchain->DestroyChain();
00827         delete tvchain;
00828     }
00829 
00830     if (piptvchain)
00831     {
00832         VERBOSE(VB_IMPORTANT, LOC + "Deleting PiP TV Chain in destructor");
00833         piptvchain->DestroyChain();
00834         delete piptvchain;
00835     }    
00836 
00837     if (ddMapLoaderRunning)
00838     {
00839         pthread_join(ddMapLoader, NULL);
00840         ddMapLoaderRunning = false;
00841 
00842         if (ddMapSourceId)
00843         {            
00844             int *src = new int;
00845             *src = ddMapSourceId;
00846             pthread_create(&ddMapLoader, NULL, load_dd_map_post_thunk, src);
00847             pthread_detach(ddMapLoader);
00848         }
00849     }
00850 }
00851 
00852 TVState TV::GetState(void) const
00853 {
00854     if (InStateChange())
00855         return kState_ChangingState;
00856     return internalState;
00857 }
00858 
00859 void TV::GetPlayGroupSettings(const QString &group)
00860 {
00861     fftime       = PlayGroup::GetSetting(group, "skipahead", 30);
00862     rewtime      = PlayGroup::GetSetting(group, "skipback", 5);
00863     jumptime     = PlayGroup::GetSetting(group, "jump", 10);
00864     normal_speed = PlayGroup::GetSetting(group, "timestretch", 100) / 100.0;
00865     if (normal_speed == 1.0f)
00866         prev_speed = 1.5f;
00867     else
00868         prev_speed = normal_speed;
00869 }
00870 
00876 int TV::LiveTV(bool showDialogs, bool startInGuide)
00877 {
00878     requestDelete = false;
00879     allowRerecord = false;
00880     
00881     if (internalState == kState_None && RequestNextRecorder(showDialogs))
00882     {
00883         if (tvchain)
00884         {
00885             tvchain->DestroyChain();
00886             delete tvchain;
00887         }
00888         tvchain = new LiveTVChain();
00889         tvchain->InitializeNewChain(gContext->GetHostName());
00890 
00891         ChangeState(kState_WatchingLiveTV);
00892         switchToRec = NULL;
00893 
00894         GetPlayGroupSettings("Default");
00895 
00896         // Start Idle Timer
00897         int idletimeout = gContext->GetNumSetting("LiveTVIdleTimeout", 0);
00898         if (idletimeout > 0)
00899         {
00900             idleTimer->start(idletimeout * 60 * 1000, TRUE);
00901             VERBOSE(VB_GENERAL, QString("Using Idle Timer. %1 minutes")
00902                                   .arg(idletimeout));
00903         }
00904 
00905         if (startInGuide || gContext->GetNumSetting("WatchTVGuide", 0))
00906         {
00907             MSqlQuery query(MSqlQuery::InitCon()); 
00908             query.prepare("SELECT keylist FROM keybindings WHERE "
00909                           "context = 'TV Playback' AND action = 'GUIDE' AND "
00910                           "hostname = :HOSTNAME ;");
00911             query.bindValue(":HOSTNAME", gContext->GetHostName());
00912 
00913             if (query.exec() && query.isActive() && query.size() > 0)
00914             {
00915                 query.next();
00916 
00917                 QKeySequence keyseq(query.value(0).toString());
00918 
00919                 int keynum = keyseq[0];
00920                 keynum &= ~Qt::UNICODE_ACCEL;
00921    
00922                 keyList.prepend(new QKeyEvent(QEvent::KeyPress, keynum, 0, 0));
00923             }
00924         }
00925 
00926         return 1;
00927     }
00928     return 0;
00929 }
00930 
00931 int TV::GetLastRecorderNum(void) const
00932 {
00933     if (!recorder)
00934         return lastrecordernum;
00935     return recorder->GetRecorderNumber();
00936 }
00937 
00938 void TV::DeleteRecorder()
00939 {
00940     RemoteEncoder *rec = recorder;
00941     activerecorder = recorder = NULL;
00942     if (rec)
00943     {
00944         lastrecordernum = rec->GetRecorderNumber();
00945         delete rec;
00946     }
00947 }
00948 
00949 bool TV::RequestNextRecorder(bool showDialogs)
00950 {
00951     DeleteRecorder();
00952 
00953     RemoteEncoder *testrec = NULL;
00954     if (switchToRec)
00955     {
00956         // If this is set we, already got a new recorder in SwitchCards()
00957         testrec = switchToRec;
00958         switchToRec = NULL;
00959     }
00960     else
00961     {
00962         // When starting LiveTV we just get the next free recorder
00963         testrec = RemoteRequestNextFreeRecorder(-1);
00964     }
00965 
00966     if (!testrec)
00967         return false;
00968     
00969     if (!testrec->IsValidRecorder())
00970     {
00971         if (showDialogs)
00972             ShowNoRecorderDialog();
00973         
00974         delete testrec;
00975         
00976         return false;
00977     }
00978     
00979     activerecorder = recorder = testrec;
00980     return true;
00981 }
00982 
00983 void TV::FinishRecording(void)
00984 {
00985     if (!IsRecording())
00986         return;
00987 
00988     activerecorder->FinishRecording();
00989 }
00990 
00991 void TV::AskAllowRecording(const QStringList &msg, int timeuntil,
00992                            bool hasrec, bool haslater)
00993 {
00994     //VERBOSE(VB_IMPORTANT, LOC + "AskAllowRecording");
00995     if (!StateIsLiveTV(GetState()))
00996        return;
00997 
00998     ProgramInfo *info = new ProgramInfo;
00999     QStringList::const_iterator it = msg.begin();
01000     info->FromStringList(it, msg.end());
01001 
01002     QMutexLocker locker(&askAllowLock);
01003     QString key = info->MakeUniqueKey();
01004     if (timeuntil > 0)
01005     {
01006         // add program to list
01007         //VERBOSE(VB_IMPORTANT, LOC + "AskAllowRecording -- " +
01008         //        QString("adding '%1'").arg(info->title));
01009         QDateTime expiry = QDateTime::currentDateTime().addSecs(timeuntil);
01010         askAllowPrograms[key] = AskProgramInfo(expiry, hasrec, haslater, info);
01011     }
01012     else
01013     {
01014         // remove program from list
01015         VERBOSE(VB_IMPORTANT, LOC + "AskAllowRecording -- " +
01016                 QString("removing '%1'").arg(info->title));
01017         QMap<QString,AskProgramInfo>::iterator it = askAllowPrograms.find(key);
01018         if (it != askAllowPrograms.end())
01019         {
01020             delete (*it).info;
01021             askAllowPrograms.erase(it);
01022         }
01023         delete info;
01024     }
01025 
01026     UpdateOSDAskAllowDialog();
01027 }
01028 
01029 void TV::UpdateOSDAskAllowDialog(void)
01030 {
01031     QMutexLocker locker(&askAllowLock);
01032     uint cardid = recorder->GetRecorderNumber();
01033 
01034     QString single_rec =
01035         tr("MythTV wants to record \"%1\" on %2 in %d seconds. "
01036            "Do you want to:");
01037 
01038     QString record_watch  = tr("Record and watch while it records");
01039     QString let_record1   = tr("Let it record and go back to the Main Menu");
01040     QString let_recordm   = tr("Let them record and go back to the Main Menu");
01041     QString record_later1 = tr("Record it later, I want to watch TV");
01042     QString record_laterm = tr("Record them later, I want to watch TV");
01043     QString do_not_record1= tr("Don't let it record, I want to watch TV");
01044     QString do_not_recordm= tr("Don't let them record, I want to watch TV");
01045 
01046     // eliminate timed out programs
01047     QDateTime timeNow = QDateTime::currentDateTime();
01048     QMap<QString,AskProgramInfo>::iterator it = askAllowPrograms.begin();
01049     QMap<QString,AskProgramInfo>::iterator next = it;
01050     while (it != askAllowPrograms.end())
01051     {
01052         next = it; next++;
01053         if ((*it).expiry <= timeNow)
01054         {
01055             //VERBOSE(VB_IMPORTANT, LOC + "UpdateOSDAskAllowDialog -- " +
01056             //        QString("removing '%1'").arg((*it).info->title));
01057             delete (*it).info;
01058             askAllowPrograms.erase(it);
01059         }
01060         it = next;
01061     }        
01062 
01063     AskAllowType type      = kAskAllowCancel;
01064     int          sel       = 0;
01065     int          timeuntil = 0;
01066     QString      message   = QString::null;
01067     QStringList  options;
01068 
01069     uint conflict_count = askAllowPrograms.size();
01070 
01071     it = askAllowPrograms.begin();
01072     if ((1 == askAllowPrograms.size()) && ((uint)(*it).info->cardid == cardid))
01073     {
01074         (*it).is_in_same_input_group = (*it).is_conflicting = true;
01075     }
01076     else if (!askAllowPrograms.empty())
01077     {
01078         // get the currently used input on our card
01079         bool busy_input_grps_loaded = false;
01080         vector<uint> busy_input_grps;
01081         TunedInputInfo busy_input;
01082         RemoteIsBusy(cardid, busy_input);
01083 
01084         // check if current input can conflict
01085         it = askAllowPrograms.begin();
01086         for (; it != askAllowPrograms.end(); ++it)
01087         {
01088             (*it).is_in_same_input_group =
01089                 (cardid == (uint)(*it).info->cardid);
01090 
01091             if ((*it).is_in_same_input_group)
01092                 continue;
01093 
01094             // is busy_input in same input group as recording
01095             if (!busy_input_grps_loaded)
01096             {
01097                 busy_input_grps = CardUtil::GetInputGroups(busy_input.inputid);
01098                 busy_input_grps_loaded = true;
01099             }
01100 
01101             vector<uint> input_grps =
01102                 CardUtil::GetInputGroups((*it).info->inputid);
01103 
01104             for (uint i = 0; i < input_grps.size(); i++)
01105             {
01106                 if (find(busy_input_grps.begin(), busy_input_grps.end(),
01107                          input_grps[i]) !=  busy_input_grps.end())
01108                 {
01109                     (*it).is_in_same_input_group = true;
01110                     break;
01111                 }
01112             }
01113         }
01114 
01115         // check if inputs that can conflict are ok
01116         conflict_count = 0;
01117         it = askAllowPrograms.begin();
01118         for (; it != askAllowPrograms.end(); ++it)
01119         {
01120             if (!(*it).is_in_same_input_group)
01121                 (*it).is_conflicting = false;
01122             else if ((cardid == (uint)(*it).info->cardid))
01123                 (*it).is_conflicting = true;
01124             else if (!CardUtil::IsTunerShared(cardid, (*it).info->cardid))
01125                 (*it).is_conflicting = true;
01126             else if ((busy_input.sourceid == (uint)(*it).info->sourceid) &&
01127                      (busy_input.mplexid  == (uint)(*it).info->GetMplexID()))
01128                 (*it).is_conflicting = false;
01129             else
01130                 (*it).is_conflicting = true;
01131 
01132             conflict_count += (*it).is_conflicting ? 1 : 0;
01133         }
01134     }
01135 
01136     it = askAllowPrograms.begin();
01137     for (; it != askAllowPrograms.end() && !(*it).is_conflicting; ++it);
01138 
01139     if (conflict_count == 0)
01140     {
01141         VERBOSE(VB_GENERAL, LOC + "The scheduler wants to make "
01142                 "a non-conflicting recording.");
01143         // TODO take down mplexid and inform user of problem
01144         // on channel changes.
01145     }
01146     else if (conflict_count == 1 && ((uint)(*it).info->cardid == cardid))
01147     {
01148         //VERBOSE(VB_IMPORTANT, LOC + "UpdateOSDAskAllowDialog -- " +
01149         //        "kAskAllowOneRec");
01150 
01151         type = kAskAllowOneRec;
01152         it = askAllowPrograms.begin();
01153 
01154         QString channel = QDeepCopy<QString>(db_channel_format);
01155         channel
01156             .replace("<num>",  (*it).info->chanstr)
01157             .replace("<sign>", (*it).info->chansign)
01158             .replace("<name>", (*it).info->channame);
01159     
01160         message = single_rec.arg((*it).info->title).arg(channel);
01161     
01162         options += record_watch;
01163         options += let_record1;
01164         options += ((*it).has_later) ? record_later1 : do_not_record1;
01165 
01166         sel = ((*it).has_rec) ? 2 : 0;
01167         timeuntil = QDateTime::currentDateTime().secsTo((*it).expiry);
01168     }
01169     else
01170     {
01171         type = kAskAllowMultiRec;
01172 
01173         if (conflict_count > 1)
01174         {
01175             message = QObject::tr(
01176                 "MythTV wants to record these programs in %d seconds:");
01177             message += "\n";
01178         }
01179 
01180         bool has_rec = false;
01181         it = askAllowPrograms.begin();
01182         for (; it != askAllowPrograms.end(); ++it)
01183         {
01184             if (!(*it).is_conflicting)
01185                 continue;
01186 
01187             QString title = (*it).info->title;
01188             if ((title.length() < 10) && !(*it).info->subtitle.isEmpty())
01189                 title += ": " + (*it).info->subtitle;
01190             if (title.length() > 20)
01191                 title = title.left(17) + "...";
01192 
01193             QString channel = QDeepCopy<QString>(db_channel_format);
01194             channel
01195                 .replace("<num>",  (*it).info->chanstr)
01196                 .replace("<sign>", (*it).info->chansign)
01197                 .replace("<name>", (*it).info->channame);
01198 
01199             if (conflict_count > 1)
01200             {
01201                 message += QObject::tr("\"%1\" on %2").arg(title).arg(channel);
01202                 message += "\n";
01203             }
01204             else
01205             {
01206                 message = single_rec.arg((*it).info->title).arg(channel);
01207                 has_rec = (*it).has_rec;
01208             }
01209         }
01210 
01211         if (conflict_count > 1)
01212         {
01213             message += "\n";
01214             message += QObject::tr("Do you want to:");
01215         }
01216 
01217         bool all_have_later = true;
01218         it = askAllowPrograms.begin();
01219         for (; it != askAllowPrograms.end(); ++it)
01220         {
01221             if ((*it).is_conflicting)
01222                 all_have_later &= (*it).has_later;
01223         }
01224 
01225         if (conflict_count > 1)
01226         {
01227             options += let_recordm;
01228             options += (all_have_later) ? record_laterm : do_not_recordm;
01229             sel = 0;
01230         }
01231         else
01232         {
01233             options += let_record1;
01234             options += (all_have_later) ? record_later1 : do_not_record1;
01235             sel = (has_rec) ? 1 : 0;
01236         }
01237 
01238         it = askAllowPrograms.begin();
01239         timeuntil = 9999;
01240         for (; it != askAllowPrograms.end(); ++it)
01241         {
01242             if (!(*it).is_conflicting)
01243                 continue;
01244             int tmp = QDateTime::currentDateTime().secsTo((*it).expiry);
01245             timeuntil = min(timeuntil, max(tmp, 0));
01246         }
01247         timeuntil = (9999 == timeuntil) ? 0 : timeuntil;
01248     }
01249 
01250 
01251     //VERBOSE(VB_IMPORTANT, LOC + "UpdateOSDAskAllowDialog -- waiting begin");
01252 
01253     while (!GetOSD())
01254     {
01255         //cerr<<":";
01256         qApp->unlock();
01257         qApp->processEvents();
01258         usleep(1000);
01259         qApp->lock();
01260     }
01261 
01262     //VERBOSE(VB_IMPORTANT, LOC + "UpdateOSDAskAllowDialog -- waiting done");
01263 
01264     if ((dialogname == "allowrecordingbox") &&
01265         GetOSD()->DialogShowing(dialogname))
01266     {
01267         //VERBOSE(VB_IMPORTANT, LOC + "UpdateOSDAskAllowDialog -- closing");
01268         askAllowType = kAskAllowCancel;
01269         GetOSD()->HideSet("allowrecordingbox");
01270         while (GetOSD()->DialogShowing(dialogname))
01271         {
01272             //cerr<<".";
01273             usleep(1000);
01274         }
01275     }
01276 
01277     askAllowType = type;
01278 
01279     if (kAskAllowCancel != askAllowType)
01280     {
01281         //VERBOSE(VB_IMPORTANT, LOC + "UpdateOSDAskAllowDialog -- opening");
01282         dialogname = "allowrecordingbox";
01283         GetOSD()->NewDialogBox(dialogname, message, options, timeuntil, sel);
01284     }
01285 
01286     //VERBOSE(VB_IMPORTANT, LOC + "UpdateOSDAskAllowDialog -- done");
01287 }
01288 
01289 void TV::HandleOSDAskAllowResponse(void)
01290 {
01291     if (!askAllowLock.tryLock())
01292     {
01293         //VERBOSE(VB_IMPORTANT, "allowrecordingbox : askAllowLock is locked");
01294         return;
01295     }
01296 
01297     int result = GetOSD()->GetDialogResponse(dialogname);
01298     if (kAskAllowOneRec == askAllowType)
01299     {
01300         //VERBOSE(VB_IMPORTANT, "allowrecordingbox : one : "<<result);
01301         switch (result)
01302         {
01303             default:
01304             case 1:
01305                 // watch while it records
01306                 recorder->CancelNextRecording(false);
01307                 break;
01308             case 2:
01309                 // return to main menu
01310                 StopLiveTV();
01311                 break;
01312             case 3:
01313                 // cancel scheduled recording
01314                 recorder->CancelNextRecording(true);
01315                 break;                        
01316         }
01317     }
01318     else if (kAskAllowMultiRec == askAllowType)
01319     {
01320         //VERBOSE(VB_IMPORTANT, "allowrecordingbox : multi : "<<result);
01321         switch (result)
01322         {
01323             default:
01324             case 1:
01325                 // return to main menu
01326                 StopLiveTV();
01327                 break;
01328             case 2:
01329             {
01330                 // cancel conflicting scheduled recordings
01331                 QMap<QString,AskProgramInfo>::iterator it =
01332                     askAllowPrograms.begin();
01333                 for (; it != askAllowPrograms.end(); ++it)
01334                 {
01335                     if ((*it).is_conflicting)
01336                         RemoteCancelNextRecording((*it).info->cardid, true);
01337                 }
01338                 break;
01339             }
01340         }
01341     }
01342     else
01343     {
01344         //VERBOSE(VB_IMPORTANT,
01345         //        "allowrecordingbox : cancel : "<<result);
01346     }
01347 
01348     askAllowLock.unlock();
01349 }
01350 
01351 
01352 int TV::Playback(ProgramInfo *rcinfo)
01353 {
01354     wantsToQuit   = false;
01355     jumpToProgram = false;
01356     allowRerecord = false;
01357     requestDelete = false;
01358     
01359     if (internalState != kState_None)
01360         return 0;
01361 
01362     playbackLen = rcinfo->CalculateLength();
01363     playbackinfo = new ProgramInfo(*rcinfo);
01364 
01365     int overrecordseconds = gContext->GetNumSetting("RecordOverTime");
01366     QDateTime curtime = QDateTime::currentDateTime();
01367     QDateTime recendts = rcinfo->recendts.addSecs(overrecordseconds);
01368 
01369     if (curtime < recendts && !rcinfo->isVideo)
01370         ChangeState(kState_WatchingRecording);
01371     else
01372         ChangeState(kState_WatchingPreRecorded);
01373 
01374     GetPlayGroupSettings(playbackinfo->playgroup);
01375 
01376     if (class LCD * lcd = LCD::Get())
01377         lcd->switchToChannel(rcinfo->chansign, rcinfo->title, rcinfo->subtitle);
01378 
01379     return 1;
01380 }
01381 
01382 int TV::PlayFromRecorder(int recordernum)
01383 {
01384     int retval = 0;
01385 
01386     if (recorder)
01387     {
01388         VERBOSE(VB_IMPORTANT, LOC +
01389                 QString("PlayFromRecorder(%1): Recorder already exists!")
01390                 .arg(recordernum));
01391         return -1;
01392     }
01393 
01394     activerecorder = recorder = RemoteGetExistingRecorder(recordernum);
01395     if (!recorder)
01396         return -1;
01397 
01398     if (recorder->IsValidRecorder())
01399     {
01400         // let the mainloop get the programinfo from encoder,
01401         // connecting to encoder won't work from here
01402         getRecorderPlaybackInfo = true;
01403         while (getRecorderPlaybackInfo)
01404         {
01405             qApp->unlock();
01406             qApp->processEvents();
01407             usleep(1000);
01408             qApp->lock();
01409         }
01410     }
01411 
01412     DeleteRecorder();
01413 
01414     if (recorderPlaybackInfo)
01415     {
01416         bool fileexists = false;
01417         if (recorderPlaybackInfo->pathname.left(7) == "myth://")
01418             fileexists = RemoteCheckFile(recorderPlaybackInfo);
01419         else
01420         {
01421             QFile checkFile(recorderPlaybackInfo->GetPlaybackURL());
01422             fileexists = checkFile.exists();
01423         }
01424 
01425         if (fileexists)
01426         {
01427             Playback(recorderPlaybackInfo);
01428             retval = 1;
01429         }
01430     }
01431 
01432     return retval;
01433 }
01434 
01435 bool TV::StateIsRecording(TVState state)
01436 {
01437     return (state == kState_RecordingOnly || 
01438             state == kState_WatchingRecording);
01439 }
01440 
01441 bool TV::StateIsPlaying(TVState state)
01442 {
01443     return (state == kState_WatchingPreRecorded || 
01444             state == kState_WatchingRecording);
01445 }
01446 
01447 bool TV::StateIsLiveTV(TVState state)
01448 {
01449     return (state == kState_WatchingLiveTV);
01450 }
01451 
01452 TVState TV::RemoveRecording(TVState state)
01453 {
01454     if (StateIsRecording(state))
01455     {
01456         if (state == kState_RecordingOnly)
01457             return kState_None;
01458         return kState_WatchingPreRecorded;
01459     }
01460     return kState_Error;
01461 }
01462 
01463 TVState TV::RemovePlaying(TVState state)
01464 {
01465     if (StateIsPlaying(state))
01466         return kState_None;
01467     return kState_Error;
01468 }
01469 
01470 #define TRANSITION(ASTATE,BSTATE) \
01471    ((internalState == ASTATE) && (desiredNextState == BSTATE))
01472 
01473 #define SET_NEXT() do { nextState = desiredNextState; changed = true; } while(0);
01474 #define SET_LAST() do { nextState = internalState; changed = true; } while(0);
01475 
01484 void TV::HandleStateChange(void)
01485 {
01486     if (IsErrored())
01487     {
01488         VERBOSE(VB_IMPORTANT, LOC_ERR + "HandleStateChange(): "
01489                 "Called after fatal error detected.");
01490         return;
01491     }
01492 
01493     bool changed = false;
01494 
01495     stateLock.lock();
01496     TVState nextState = internalState;
01497     if (!nextStates.size())
01498     {
01499         VERBOSE(VB_IMPORTANT, LOC + "HandleStateChange() Warning, "
01500                 "called with no state to change to.");
01501         stateLock.unlock();
01502         return;
01503     }
01504     TVState desiredNextState = nextStates.dequeue();    
01505     VERBOSE(VB_GENERAL, LOC + QString("Attempting to change from %1 to %2")
01506             .arg(StateToString(nextState))
01507             .arg(StateToString(desiredNextState)));
01508 
01509     if (desiredNextState == kState_Error)
01510     {
01511         VERBOSE(VB_IMPORTANT, LOC_ERR + "HandleStateChange(): "
01512                 "Attempting to set to an error state!");
01513         errored = true;
01514         stateLock.unlock();
01515         return;
01516     }
01517 
01518     if (TRANSITION(kState_None, kState_WatchingLiveTV))
01519     {
01520         QString name = "";
01521 
01522         lastSignalUIInfo.clear();
01523         activerecorder = recorder;
01524         recorder->Setup();
01525         
01526         QDateTime timerOffTime = QDateTime::currentDateTime();
01527         lockTimerOn = false;
01528 
01529         SET_NEXT();
01530         recorder->SpawnLiveTV(tvchain->GetID(), false, "");
01531 
01532         tvchain->ReloadAll();
01533 
01534         playbackinfo = tvchain->GetProgramAt(-1);
01535         if (!playbackinfo)
01536         {
01537             VERBOSE(VB_IMPORTANT, LOC_ERR +
01538                     "LiveTV not successfully started");
01539             gContext->RestoreScreensaver();
01540             DeleteRecorder();
01541 
01542             SET_LAST();
01543         }
01544         else
01545         {
01546             QString playbackURL = playbackinfo->GetPlaybackURL();
01547 
01548             tvchain->SetProgram(playbackinfo);
01549 
01550             bool opennow = (tvchain->GetCardType(-1) != "DUMMY");
01551             prbuffer = new RingBuffer(playbackURL, false, true,
01552                                       opennow ? 12 : (uint)-1);
01553             prbuffer->SetLiveMode(tvchain);
01554         }
01555 
01556         gContext->DisableScreensaver();
01557 
01558         bool ok = false;
01559         if (playbackinfo && StartRecorder(recorder,-1))
01560         {
01561             if (StartPlayer(false))
01562                 ok = true;
01563             else
01564                 StopStuff(true, true, true);
01565         }
01566         if (!ok)
01567         {
01568             VERBOSE(VB_IMPORTANT, LOC_ERR +
01569                     "LiveTV not successfully started");
01570             gContext->RestoreScreensaver();
01571             DeleteRecorder();
01572 
01573             SET_LAST();
01574         }
01575         else if (!lastLockSeenTime.isValid() ||
01576                  (lastLockSeenTime < timerOffTime))
01577         {
01578             lockTimer.start();
01579             lockTimerOn = true;
01580         }
01581     }
01582     else if (TRANSITION(kState_WatchingLiveTV, kState_None))
01583     {
01584         SET_NEXT();
01585 
01586         StopStuff(true, true, true);
01587         pbinfoLock.lock();
01588         if (playbackinfo)
01589             delete playbackinfo;
01590         playbackinfo = NULL;
01591         pbinfoLock.unlock();
01592 
01593         gContext->RestoreScreensaver();
01594     }
01595     else if (TRANSITION(kState_WatchingRecording, kState_WatchingPreRecorded))
01596     {
01597         SET_NEXT();
01598     }
01599     else if (TRANSITION(kState_None, kState_WatchingPreRecorded) ||
01600              TRANSITION(kState_None, kState_WatchingRecording))
01601     {
01602         QString playbackURL;
01603         if ((playbackinfo->pathname.left(4) == "dvd:") ||
01604             (playbackinfo->isVideo))
01605             playbackURL = playbackinfo->pathname;
01606         else
01607             playbackURL = playbackinfo->GetPlaybackURL(
01608                               desiredNextState != kState_WatchingRecording);
01609 
01610         prbuffer = new RingBuffer(playbackURL, false);
01611         if (prbuffer->IsOpen())
01612         {
01613             gContext->DisableScreensaver();
01614     
01615             if (desiredNextState == kState_WatchingRecording)
01616             {
01617                 activerecorder = recorder =
01618                     RemoteGetExistingRecorder(playbackinfo);
01619                 if (!recorder || !recorder->IsValidRecorder())
01620                 {
01621                     VERBOSE(VB_IMPORTANT, LOC_ERR + "Couldn't find "
01622                             "recorder for in-progress recording");
01623                     desiredNextState = kState_WatchingPreRecorded;
01624                     DeleteRecorder();
01625                 }
01626                 else
01627                 {
01628                     recorder->Setup();
01629                 }
01630             }
01631 
01632             StartPlayer(desiredNextState == kState_WatchingRecording);
01633             
01634             SET_NEXT();
01635             if (!playbackinfo->isVideo)
01636             {
01637                 QString message = "COMMFLAG_REQUEST ";
01638                 message += playbackinfo->chanid + " " +
01639                            playbackinfo->recstartts.toString(Qt::ISODate);
01640                 RemoteSendMessage(message);
01641             }                
01642         }
01643         else
01644         {
01645             SET_LAST();
01646             wantsToQuit   = true;
01647         }
01648     }
01649     else if (TRANSITION(kState_WatchingPreRecorded, kState_None) ||
01650              TRANSITION(kState_WatchingRecording, kState_None))
01651     {
01652         SET_NEXT();
01653 
01654         StopStuff(true, true, false);
01655         gContext->RestoreScreensaver();
01656     }
01657     else if (TRANSITION(kState_None, kState_None))
01658     {
01659         SET_NEXT();
01660     }
01661 
01662     // Check that new state is valid
01663     if (kState_None != nextState &&
01664         activenvp && !activenvp->IsDecoderThreadAlive())
01665     {
01666         VERBOSE(VB_IMPORTANT, LOC_ERR +
01667                 "Decoder not alive, and trying to play..");
01668         if (nextState == kState_WatchingLiveTV)
01669         {
01670             StopStuff(true, true, true);
01671         }
01672 
01673         nextState = kState_None;
01674         changed = true;
01675     }
01676 
01677     // Print state changed message...
01678     if (!changed)
01679     {
01680         VERBOSE(VB_IMPORTANT, LOC_ERR +
01681                 QString("Unknown state transition: %1 to %2")
01682                 .arg(StateToString(internalState))
01683                 .arg(StateToString(desiredNextState)));
01684     }
01685     else if (internalState != nextState)
01686     {
01687         VERBOSE(VB_GENERAL, LOC + QString("Changing from %1 to %2")
01688                 .arg(StateToString(internalState))
01689                 .arg(StateToString(nextState)));
01690     }
01691 
01692     // update internal state variable
01693     TVState lastState = internalState;
01694     internalState = nextState;
01695     stateLock.unlock();
01696 
01697     if (StateIsLiveTV(internalState))
01698     {
01699         UpdateOSDInput();
01700         UpdateLCD();
01701         ITVRestart(true);
01702     }
01703     else if (StateIsPlaying(internalState) && lastState == kState_None)
01704     {
01705         if (GetOSD() && (PlayGroup::GetCount() > 0))
01706             GetOSD()->SetSettingsText(tr("%1 Settings")
01707                                       .arg(tr(playbackinfo->playgroup)), 3);
01708         ITVRestart(false);
01709     }
01710     if (prbuffer && prbuffer->isDVD()) {
01711         UpdateLCD();
01712     }
01713     if (recorder)
01714         recorder->FrontendReady();
01715 }
01716 #undef TRANSITION
01717 #undef SET_NEXT
01718 #undef SET_LAST
01719 
01723 bool TV::InStateChange() const
01724 {
01725     if (!stateLock.tryLock())
01726         return true;
01727     bool inStateChange = nextStates.size() > 0;
01728     stateLock.unlock();
01729     return inStateChange;
01730 }
01731 
01735 void TV::ChangeState(TVState nextState)
01736 {
01737     stateLock.lock();
01738     nextStates.enqueue(nextState);
01739     stateLock.unlock();
01740 }
01741 
01745 void TV::ForceNextStateNone()
01746 {
01747     stateLock.lock();
01748     nextStates.clear();
01749     nextStates.push_back(kState_None);
01750     stateLock.unlock();
01751 }
01752 
01758 bool TV::StartRecorder(RemoteEncoder *rec, int maxWait)
01759 {
01760     maxWait = (maxWait <= 0) ? 40000 : maxWait;
01761     MythTimer t;
01762     t.start();
01763     while (!rec->IsRecording() && !exitPlayer && t.elapsed() < maxWait)
01764         usleep(5000);
01765     if (!rec->IsRecording() || exitPlayer)
01766     {
01767         if (!exitPlayer)
01768             VERBOSE(VB_IMPORTANT, LOC_ERR + "StartRecorder() -- "
01769                     "timed out waiting for recorder to start");
01770         return false;
01771     }
01772 
01773     VERBOSE(VB_PLAYBACK, LOC + "StartRecorder(): took "<<t.elapsed()
01774             <<" ms to start recorder.");
01775 
01776     // Cache starting frame rate for this recorder
01777     if (rec == recorder)
01778         frameRate = recorder->GetFrameRate();
01779     return true;
01780 }
01781 
01787 bool TV::StartPlayer(bool isWatchingRecording, int maxWait)
01788 { 
01789     SetupPlayer(isWatchingRecording);
01790     pthread_create(&decode, NULL, SpawnDecode, nvp);
01791 
01792     maxWait = (maxWait <= 0) ? 20000 : maxWait;
01793 #ifdef USING_VALGRIND
01794     maxWait = (1<<30);
01795 #endif // USING_VALGRIND
01796     MythTimer t;
01797     t.start();
01798     while (!nvp->IsPlaying() && nvp->IsDecoderThreadAlive() &&
01799            (t.elapsed() < maxWait))
01800         usleep(50);
01801 
01802     VERBOSE(VB_PLAYBACK, LOC + "StartPlayer(): took "<<t.elapsed()
01803             <<" ms to start player.");
01804 
01805     if (nvp->IsPlaying())
01806     {
01807         nvp->ResetCaptions();
01808         nvp->ResetTeletext();
01809 
01810         activenvp = nvp;
01811         activerbuffer = prbuffer;
01812         StartOSD();
01813         return true;
01814     }
01815     VERBOSE(VB_IMPORTANT, LOC_ERR +
01816             QString("StartPlayer(): NVP is not playing after %1 msec")
01817             .arg(maxWait));
01818     return false;
01819 }
01820 
01827 void TV::StartOSD()
01828 {
01829     if (nvp)
01830     {
01831         frameRate = nvp->GetFrameRate();
01832         if (nvp->GetOSD())
01833             GetOSD()->SetUpOSDClosedHandler(this);
01834     }
01835 }
01836 
01850 void TV::StopStuff(bool stopRingBuffers, bool stopPlayers, bool stopRecorders)
01851 {
01852     VERBOSE(VB_PLAYBACK, LOC + "StopStuff() -- begin");
01853 
01854     if (prbuffer && prbuffer->isDVD())
01855     {
01856         VERBOSE(VB_PLAYBACK,LOC + " StopStuff() -- get dvd player out of still frame or wait status");
01857         prbuffer->DVD()->IgnoreStillOrWait(true);
01858     }
01859 
01860     if (stopRingBuffers)
01861     {
01862         VERBOSE(VB_PLAYBACK, LOC + "StopStuff(): stopping ring buffer[s]");
01863         if (prbuffer)
01864         {
01865             prbuffer->StopReads();
01866             prbuffer->Pause();
01867             prbuffer->WaitForPause();
01868         }
01869 
01870         if (piprbuffer)
01871         {
01872             piprbuffer->StopReads();
01873             piprbuffer->Pause();
01874             piprbuffer->WaitForPause();
01875         }
01876     }
01877 
01878     if (stopPlayers)
01879     {
01880         VERBOSE(VB_PLAYBACK, LOC + "StopStuff(): stopping player[s] (1/2)");
01881         if (nvp)
01882             nvp->StopPlaying();
01883 
01884         if (pipnvp)
01885             pipnvp->StopPlaying();
01886     }
01887 
01888     if (stopRecorders)
01889     {
01890         VERBOSE(VB_PLAYBACK, LOC + "StopStuff(): stopping recorder[s]");
01891         if (recorder)
01892             recorder->StopLiveTV();
01893 
01894         if (piprecorder)
01895             piprecorder->StopLiveTV();
01896     }
01897 
01898     if (stopPlayers)
01899     {
01900         VERBOSE(VB_PLAYBACK, LOC + "StopStuff(): stopping player[s] (2/2)");
01901         if (nvp)
01902             TeardownPlayer();
01903 
01904         if (pipnvp)
01905             TeardownPipPlayer();
01906     }
01907     VERBOSE(VB_PLAYBACK, LOC + "StopStuff() -- end");
01908 }
01909 
01910 void TV::SetupPlayer(bool isWatchingRecording)
01911 {
01912     if (nvp)
01913     {
01914         VERBOSE(VB_IMPORTANT, LOC_ERR +
01915                 "Attempting to setup a player, but it already exists.");
01916         return;
01917     }
01918 
01919     nvp = new NuppelVideoPlayer("player", playbackinfo);
01920     nvp->SetParentWidget(myWindow);
01921     nvp->SetParentPlayer(this);
01922     nvp->SetRingBuffer(prbuffer);
01923     nvp->SetRecorder(recorder);
01924     nvp->SetAudioInfo(gContext->GetSetting("AudioOutputDevice"),
01925                       gContext->GetSetting("PassThruOutputDevice"),
01926                       gContext->GetNumSetting("AudioSampleRate", 44100));
01927     nvp->SetLength(playbackLen);
01928     nvp->SetExactSeeks(gContext->GetNumSetting("ExactSeeking", 0));
01929     nvp->SetAutoCommercialSkip(autoCommercialSkip);
01930     LoadExternalSubtitles(nvp, prbuffer->GetFilename());
01931     nvp->SetLiveTVChain(tvchain);
01932 
01933     nvp->SetAudioStretchFactor(normal_speed);
01934 
01935     if (embedWinID > 0)
01936         nvp->EmbedInWidget(embedWinID, embedBounds.x(), embedBounds.y(),
01937                            embedBounds.width(), embedBounds.height());
01938 
01939     if (isWatchingRecording)
01940         nvp->SetWatchingRecording(true);
01941 
01942     int udp_port = gContext->GetNumSetting("UDPNotifyPort");
01943     if (udp_port > 0)
01944     {
01945         if (udpnotify == NULL)
01946             udpnotify = new UDPNotify(this, udp_port);
01947     }
01948     else
01949         udpnotify = NULL;
01950 }
01951 
01952 void TV::SetupPipPlayer(void)
01953 {
01954     if (pipnvp)
01955     {
01956         VERBOSE(VB_IMPORTANT, LOC_ERR +
01957                 "Attempting to setup a PiP player, but it already exists.");
01958         return;
01959     }
01960 
01961     pipnvp = new NuppelVideoPlayer("PIP player");
01962     pipnvp->SetAsPIP();
01963     pipnvp->SetRingBuffer(piprbuffer);
01964     pipnvp->SetRecorder(piprecorder);
01965     pipnvp->SetAudioInfo(gContext->GetSetting("AudioOutputDevice"),
01966                          gContext->GetSetting("PassThruOutputDevice"),
01967                          gContext->GetNumSetting("AudioSampleRate", 44100));
01968     pipnvp->SetExactSeeks(gContext->GetNumSetting("ExactSeeking", 0));
01969     pipnvp->SetLiveTVChain(piptvchain);
01970 
01971     pipnvp->SetLength(playbackLen);
01972 }
01973 
01974 void TV::TeardownPlayer(void)
01975 {
01976     if (nvp)
01977     {
01978         osdlock.lock(); // prevent UpdateOSDSignal from using osd...
01979 
01980         NuppelVideoPlayer *xnvp = nvp;
01981         pthread_t          xdec = decode;
01982 
01983         nvp            = NULL;
01984         activenvp      = NULL;
01985         activerecorder = NULL;
01986         activerbuffer  = NULL;
01987 
01988         osdlock.unlock();
01989 
01990         // NVP may try to get qapp lock if there is an error,
01991         // so we need to do this outside of the osdlock.
01992         pthread_join(xdec, NULL);
01993         delete xnvp;
01994     }
01995 
01996     if (udpnotify)
01997     {
01998         delete udpnotify;
01999         udpnotify = NULL;
02000     }
02001 
02002     paused = false;
02003     doing_ff_rew = 0;
02004     ff_rew_index = kInitFFRWSpeed;
02005     speed_index = 0;
02006     sleep_index = 0;
02007     normal_speed = 1.0f;
02008 
02009     pbinfoLock.lock();
02010     if (playbackinfo)
02011         delete playbackinfo;
02012     playbackinfo = NULL;
02013     pbinfoLock.unlock(); 
02014 
02015     DeleteRecorder();
02016 
02017     if (prbuffer)
02018     {
02019         delete prbuffer;
02020         prbuffer = activerbuffer = NULL;
02021     }
02022 
02023     if (piprbuffer)
02024     {
02025         delete piprbuffer;
02026         piprbuffer = NULL;
02027     }
02028 
02029     if (tvchain)
02030     {
02031         tvchain->DestroyChain();
02032         delete tvchain;
02033         tvchain = NULL;
02034     }
02035 }
02036 
02037 void TV::TeardownPipPlayer(void)
02038 {
02039     if (pipnvp)
02040     {
02041         if (activerecorder == piprecorder)
02042             ToggleActiveWindow();
02043 
02044         osdlock.lock(); // prevent UpdateOSDSignal from using osd...
02045         NuppelVideoPlayer *xnvp = pipnvp;
02046         pthread_t          xdec = pipdecode;
02047         pipnvp = NULL;
02048         osdlock.unlock();
02049 
02050         // NVP may try to get qapp lock if there is an error,
02051         // so we need to do this outside of the osdlock.
02052         pthread_join(xdec, NULL);
02053         delete xnvp;
02054     }
02055 
02056     if (piprecorder)
02057     {
02058         delete piprecorder;
02059         piprecorder = NULL;
02060     }
02061 
02062     if (piprbuffer)
02063     {
02064         delete piprbuffer;
02065         piprbuffer = NULL;
02066     }
02067 
02068     if (piptvchain)
02069     {
02070         piptvchain->DestroyChain();
02071         delete piptvchain;
02072         piptvchain = NULL;
02073     }
02074 }
02075 
02076 void *TV::EventThread(void *param)
02077 {
02078     TV *thetv = (TV *)param;
02079     thetv->RunTV();
02080 
02081     return NULL;
02082 }
02083 
02084 void TV::RunTV(void)
02085 { 
02086     paused = false;
02087 
02088     doing_ff_rew = 0;
02089     ff_rew_index = kInitFFRWSpeed;
02090     speed_index = 0;
02091     normal_speed = 1.0f;
02092     sleep_index = 0;
02093 
02094     int updatecheck = 0;
02095     update_osd_pos = false;
02096 
02097     lastLcdUpdate = QDateTime::currentDateTime();
02098     UpdateLCD();
02099     
02100     ClearInputQueues();
02101 
02102     switchToRec = NULL;
02103     runMainLoop = true;
02104     exitPlayer = false;
02105 
02106     mainLoopCondLock.lock();
02107     mainLoopCond.wakeAll();
02108     mainLoopCondLock.unlock();
02109 
02110     while (runMainLoop)
02111     {
02112         stateLock.lock();
02113         bool doHandle = nextStates.size() > 0;
02114         stateLock.unlock();
02115         if (doHandle)
02116             HandleStateChange();
02117 
02118         if (osdlock.tryLock())
02119         {
02120             if (lastSignalMsg.size())
02121             {   // set last signal msg, so we get some feedback...
02122                 UpdateOSDSignal(lastSignalMsg);
02123                 lastSignalMsg.clear();
02124             }
02125             UpdateOSDTimeoutMessage();
02126 
02127             if (!tvchainUpdate.isEmpty())
02128             {
02129                 tvchainUpdateLock.lock();
02130                 for (QStringList::Iterator it = tvchainUpdate.begin();
02131                      it != tvchainUpdate.end(); ++it)
02132                 {
02133                     if (tvchain && nvp && *it == tvchain->GetID())
02134                     {
02135                         tvchain->ReloadAll();
02136                         if (nvp->GetTVChain())
02137                             nvp->CheckTVChain();
02138                     }
02139                     if (piptvchain && pipnvp && *it == piptvchain->GetID())
02140                     {
02141                         piptvchain->ReloadAll();
02142                         if (pipnvp->GetTVChain())
02143                             pipnvp->CheckTVChain();
02144                     }
02145                 }
02146                 tvchainUpdate.clear();
02147                 tvchainUpdateLock.unlock();
02148             }
02149 
02150             osdlock.unlock();
02151         }
02152 
02153         usleep(1000);
02154 
02155         if (getRecorderPlaybackInfo)
02156         {
02157             if (recorderPlaybackInfo)
02158             {
02159                 delete recorderPlaybackInfo;
02160                 recorderPlaybackInfo = NULL;
02161             }
02162 
02163             recorder->Setup();
02164 
02165             if (recorder->IsRecording())
02166             {
02167                 recorderPlaybackInfo = recorder->GetRecording();
02168                 RemoteFillProginfo(recorderPlaybackInfo, 
02169                                    gContext->GetHostName());
02170             }
02171 
02172             getRecorderPlaybackInfo = false;
02173         }
02174 
02175         bool had_key = false;
02176         if (nvp)
02177         {
02178             QKeyEvent *keypressed = NULL;
02179 
02180             keyListLock.lock();
02181             if (keyList.count() > 0)
02182             {
02183                 keypressed = keyList.first();
02184                 keyList.removeFirst();
02185             }
02186             keyListLock.unlock();
02187 
02188             if (keypressed)
02189             {
02190                 had_key = true;
02191                 ProcessKeypress(keypressed);
02192                 delete keypressed;
02193             }
02194         }
02195 
02196         if (nvp && !had_key && !ignoreKeys)
02197         {
02198             QString netCmd = QString::null;
02199             ncLock.lock();
02200             if (networkControlCommands.size())
02201             {
02202                 netCmd = networkControlCommands.front();
02203                 networkControlCommands.pop_front();
02204             }
02205             ncLock.unlock();
02206 
02207             if (!netCmd.isEmpty())
02208                 ProcessNetworkControlCommand(netCmd);
02209         }
02210 
02211         if ((recorder && recorder->GetErrorStatus()) ||
02212             (nvp && nvp->IsErrored()) || IsErrored())
02213         {
02214             exitPlayer = true;
02215             wantsToQuit = true;
02216         }
02217 
02218         if (StateIsPlaying(internalState))
02219         {
02220 #ifdef USING_VALGRIND
02221             while (!nvp->IsPlaying())
02222             {
02223                 VERBOSE(VB_IMPORTANT, LOC + "Waiting for Valgrind...");
02224                 sleep(1);
02225             }
02226 #endif // USING_VALGRIND
02227 
02228             if (!nvp->IsPlaying())
02229             {
02230                 ChangeState(RemovePlaying(internalState));
02231                 endOfRecording = true;
02232                 wantsToQuit = true;
02233                 if (nvp && gContext->GetNumSetting("AutomaticSetWatched", 0))
02234                     nvp->SetWatched();
02235                 VERBOSE(VB_PLAYBACK, LOC_ERR + "nvp->IsPlaying() timed out");
02236             }
02237 
02238             if (nvp->IsNearEnd())
02239                 isnearend = true;
02240             else
02241                 isnearend = false;
02242             
02243             if (isnearend && IsEmbedding() && !paused)
02244                 DoPause();
02245         }
02246 
02247         if (!endOfRecording)
02248         {
02249             if (jumped_back && !isnearend)
02250                 jumped_back = false;
02251             
02252             if (internalState == kState_WatchingPreRecorded && !inPlaylist &&
02253                 dialogname == "" && isnearend && !exitPlayer 
02254                 && !underNetworkControl &&
02255                 (gContext->GetNumSetting("EndOfRecordingExitPrompt") == 1) &&
02256                 !jumped_back && !editmode && !IsEmbedding() && !paused)
02257             {
02258                 PromptDeleteRecording(tr("End Of Recording"));
02259             }
02260             
02261                 
02262             // disable dialog and enable playback after 2 minutes
02263             if (IsVideoExitDialog() &&
02264                 dialogboxTimer.elapsed() > (2 * 60 * 1000))
02265             {
02266                 GetOSD()->TurnDialogOff(dialogname);
02267                 dialogname = "";
02268                 DoPause();
02269                 ClearOSD();
02270 
02271                 requestDelete = false;
02272                 exitPlayer  = true;
02273                 wantsToQuit = true;
02274             }
02275         }
02276         
02277         if (exitPlayer)
02278         {
02279             while (GetOSD() && GetOSD()->DialogShowing(dialogname))
02280             {
02281                 GetOSD()->DialogAbort(dialogname);
02282                 GetOSD()->TurnDialogOff(dialogname);
02283                 usleep(1000);
02284             }
02285             
02286             
02287             if (jumpToProgram && lastProgram)
02288             {
02289                 bool fileExists = lastProgram->PathnameExists();
02290                 if (!fileExists)
02291                 {
02292                     if (GetOSD())
02293                     {
02294                         QString msg = tr("Last Program: %1 Doesn't Exist")
02295                             .arg(lastProgram->title);
02296                         GetOSD()->SetSettingsText(msg, 3);
02297                     }
02298                     lastProgramStringList.clear();
02299                     setLastProgram(NULL);
02300                     VERBOSE(VB_PLAYBACK, LOC_ERR + "Last Program File does not exist");
02301                     jumpToProgram = false;
02302                 }
02303                 else
02304                     ChangeState(kState_None);
02305             }
02306             else
02307                 ChangeState(kState_None);
02308 
02309             exitPlayer = false;
02310         }
02311 
02312         for (uint i = InStateChange() ? 2 : 0; i < 2; i++)
02313         {
02314             if (kPseudoChangeChannel != pseudoLiveTVState[i])
02315                 continue;
02316 
02317             VERBOSE(VB_PLAYBACK, "REC_PROGRAM -- channel change "<<i);
02318             if (activerecorder != recorder)
02319                 ToggleActiveWindow();
02320 
02321             uint    chanid  = pseudoLiveTVRec[i]->chanid.toUInt();
02322             QString channum = pseudoLiveTVRec[i]->chanstr;
02323             str_vec_t tmp = prevChan;
02324             prevChan.clear();
02325             if (i && activenvp == nvp)
02326                 ToggleActiveWindow();
02327             ChangeChannel(chanid, channum);
02328             prevChan = tmp;
02329             pseudoLiveTVState[i] = kPseudoRecording;
02330 
02331             if (i && pipnvp)
02332                 TogglePIPView();
02333         }
02334 
02335         if ((doing_ff_rew || speed_index) && 
02336             activenvp && activenvp->AtNormalSpeed())
02337         {
02338             speed_index = 0;
02339             doing_ff_rew = 0;
02340             ff_rew_index = kInitFFRWSpeed;
02341             UpdateOSDSeekMessage(PlayMesg(), osd_general_timeout);
02342         }
02343 
02344         if (activenvp && (activenvp->GetNextPlaySpeed() != normal_speed) &&
02345             activenvp->AtNormalSpeed() &&
02346             !activenvp->PlayingSlowForPrebuffer())
02347         {
02348             // got changed in nvp due to close to end of file
02349             normal_speed = 1.0f;
02350             UpdateOSDSeekMessage(PlayMesg(), osd_general_timeout);
02351         }
02352 
02353         if (++updatecheck >= 100)
02354         {
02355             OSDSet *oset;
02356             if (GetOSD() && (oset = GetOSD()->GetSet("status")) &&
02357                 oset->Displaying() && update_osd_pos &&
02358                 (StateIsLiveTV(internalState) ||
02359                  StateIsPlaying(internalState)))
02360             {
02361                 struct StatusPosInfo posInfo;
02362                 nvp->calcSliderPos(posInfo);
02363                 GetOSD()->UpdateStatus(posInfo);
02364             }
02365 
02366             updatecheck = 0;
02367         }
02368 
02369         // Commit input when the OSD fades away
02370         if (HasQueuedChannel() && GetOSD())
02371         {
02372             OSDSet *set = GetOSD()->GetSet("channel_number");
02373             if ((set && !set->Displaying()) || !set)
02374                 CommitQueuedInput();
02375         }
02376         // Clear closed caption input mode when timer expires
02377         if (ccInputMode && (ccInputModeExpires < QTime::currentTime()))
02378         {
02379             ccInputMode = false;
02380             ClearInputQueues(true);
02381         }
02382         // Clear arbitrary seek input mode when timer expires
02383         if (asInputMode && (asInputModeExpires < QTime::currentTime()))
02384         {
02385             asInputMode = false;
02386             ClearInputQueues(true);
02387         }   
02388 
02389         if (switchToInputId)
02390         {
02391             uint tmp = switchToInputId;
02392             switchToInputId = 0;
02393             SwitchInputs(tmp);
02394         }
02395 
02396         if (class LCD * lcd = LCD::Get())
02397         {
02398             QDateTime curTime = QDateTime::currentDateTime();
02399 
02400             if (lastLcdUpdate.secsTo(curTime) < kLCDTimeout)
02401                 continue;
02402 
02403             float progress = 0.0;
02404             bool  showProgress = true;
02405 
02406             if (StateIsLiveTV(GetState()))
02407                 ShowLCDChannelInfo();
02408 
02409             if (activerbuffer && activerbuffer->isDVD())
02410             {
02411                 ShowLCDDVDInfo();
02412                 showProgress = !activerbuffer->InDVDMenuOrStillFrame();
02413             }
02414 
02415             if (activenvp && showProgress)
02416             {
02417                 struct StatusPosInfo posInfo;
02418                 nvp->calcSliderPos(posInfo);
02419                 progress = (float)posInfo.position / 1000;
02420             }
02421             lcd->setChannelProgress(progress);
02422 
02423             lastLcdUpdate = QDateTime::currentDateTime();
02424         }
02425 
02426         if (needToSwapPIP)
02427         {
02428             ClearOSD();
02429             SwapPIP();
02430             needToSwapPIP = false;
02431         }
02432 
02433         if (needToJumpMenu)
02434         {
02435             DoDisplayJumpMenu();
02436             needToJumpMenu = false;
02437         }
02438     }
02439   
02440     if (!IsErrored() && (GetState() != kState_None))
02441     {
02442         ForceNextStateNone();
02443         HandleStateChange();
02444     }
02445 }
02446 
02447 bool TV::eventFilter(QObject *o, QEvent *e)
02448 {
02449     (void)o;
02450 
02451     switch (e->type())
02452     {
02453         case QEvent::KeyPress:
02454         {
02455             QKeyEvent *k = new QKeyEvent(*(QKeyEvent *)e);
02456   
02457             // can't process these events in the Qt event loop. 
02458             keyListLock.lock();
02459             keyList.append(k);
02460             keyListLock.unlock();
02461 
02462             return true;
02463         }
02464         case QEvent::Paint:
02465         {
02466             if (nvp)
02467                 nvp->ExposeEvent();
02468             return true;
02469         }
02470         case MythEvent::MythEventMessage:
02471         {
02472             customEvent((QCustomEvent *)e);
02473             return true;
02474         }
02475         default:
02476             return false;
02477     }
02478 }
02479 
02480 bool TV::HandleTrackAction(const QString &action)
02481 {
02482     bool handled = false;
02483     if (!activenvp)
02484         return false;
02485 
02486     if (action == "TOGGLECC" && !browsemode)
02487     {
02488         handled = true;
02489         if (ccInputMode)
02490         {
02491             bool valid = false;
02492             int page = GetQueuedInputAsInt(&valid, 16);
02493             if (vbimode == VBIMode::PAL_TT && valid)
02494                 activenvp->SetTeletextPage(page);
02495             else if (vbimode == VBIMode::NTSC_CC)
02496                 activenvp->SetTrack(kTrackTypeCC608, max(min(page - 1, 1), 0));
02497             ccInputModeExpires.start(); // expire ccInputMode now...
02498             ClearInputQueues(true);
02499         }
02500         else if (activenvp->GetCaptionMode() & kDisplayNUVTeletextCaptions)
02501         {
02502             ccInputMode        = true;
02503             ccInputModeExpires = QTime::currentTime()
02504                 .addMSecs(kInputModeTimeout);
02505             asInputModeExpires = QTime::currentTime();
02506             ClearInputQueues(false);
02507             AddKeyToInputQueue(0);
02508         }
02509         else
02510         {
02511             activenvp->ToggleCaptions();
02512         }
02513     }
02514     else if (action.left(6) == "TOGGLE")
02515     {
02516         int type = type_string_to_track_type(action.mid(6));
02517         if (type == kTrackTypeTeletextMenu)
02518         {
02519             handled = true;
02520             activenvp->EnableTeletext();
02521         }
02522         else if (type >= kTrackTypeSubtitle)
02523         {
02524             handled = true;
02525             activenvp->ToggleCaptions(type);
02526         }
02527     }
02528     else if (action.left(6) == "SELECT")
02529     {
02530         int type = type_string_to_track_type(action.mid(6));
02531         int mid  = (kTrackTypeSubtitle == type) ? 15 : 12;
02532         if (type >= kTrackTypeAudio)
02533         {
02534             handled = true;
02535             activenvp->SetTrack(type, action.mid(mid).toInt());
02536         }
02537     }
02538     else if (action.left(4) == "NEXT" || action.left(4) == "PREV")
02539     {
02540         int dir = (action.left(4) == "NEXT") ? +1 : -1;
02541         int type = type_string_to_track_type(action.mid(4));
02542         if (type >= kTrackTypeAudio)
02543         {
02544             handled = true;
02545             activenvp->ChangeTrack(type, dir);
02546         }
02547         else if (action.right(2) == "CC")
02548         {
02549             handled = true;
02550             activenvp->ChangeCaptionTrack(dir);
02551         }
02552     }
02553 
02554     return handled;
02555 }
02556 
02557 static bool has_action(QString action, const QStringList &actions)
02558 {
02559     QStringList::const_iterator it;
02560     for (it = actions.begin(); it != actions.end(); ++it)
02561     {
02562         if (action == *it)
02563             return true;
02564     }
02565     return false;
02566 }
02567 
02568 void TV::ProcessKeypress(QKeyEvent *e)
02569 {
02570 #if DEBUG_ACTIONS
02571     VERBOSE(VB_IMPORTANT, LOC + "ProcessKeypress() ignoreKeys: "<<ignoreKeys);
02572 #endif // DEBUG_ACTIONS
02573 
02574     if (!GetOSD() || !GetOSD()->DialogShowing("idletimeout")
02575             && StateIsLiveTV(GetState()) && idleTimer->isActive())
02576         idleTimer->changeInterval(gContext->GetNumSetting("LiveTVIdleTimeout",
02577                                                           0) * 60 * 1000);
02578 
02579     bool was_doing_ff_rew = false;
02580     bool redisplayBrowseInfo = false;
02581     QStringList actions;
02582 
02583     if (ignoreKeys)
02584     {
02585         if (!gContext->GetMainWindow()->TranslateKeyPress(
02586                 "TV Playback", e, actions))
02587         {
02588             return;
02589         }
02590 
02591         bool esc   = has_action("ESCAPE", actions);
02592         bool pause = has_action("PAUSE",  actions);
02593         bool play  = has_action("PLAY",   actions);
02594 
02595         if ((!esc || browsemode) && !pause && !play)
02596             return;
02597     }
02598 
02599     if (editmode)
02600     {   
02601         if (!nvp->DoKeypress(e))
02602             editmode = nvp->GetEditMode();
02603         if (!editmode)
02604         {
02605             paused = !paused;
02606             DoPause();
02607         }
02608         return;
02609     }
02610 
02611     if (GetOSD() && GetOSD()->IsRunningTreeMenu())
02612     {
02613         GetOSD()->TreeMenuHandleKeypress(e);
02614         return;
02615     }
02616 
02617     // If text is already queued up, be more lax on what is ok.
02618     // This allows hex teletext entry and minor channel entry.
02619     const QString txt = e->text();
02620     if (HasQueuedInput() && (1 == txt.length()))
02621     {
02622         bool ok = false;
02623         txt.toInt(&ok, 16);
02624         if (ok || txt=="_" || txt=="-" || txt=="#" || txt==".")
02625         {
02626             AddKeyToInputQueue(txt.at(0).latin1());
02627             return;
02628         }
02629     }
02630 
02631     if (GetOSD() && dialogname == "channel_editor")
02632     {
02633         ChannelEditKey(e);
02634         return;
02635     }
02636 
02637     // Teletext menu
02638     if (activenvp && (activenvp->GetCaptionMode() == kDisplayTeletextMenu))
02639     {
02640         QStringList tt_actions;
02641         if (gContext->GetMainWindow()->TranslateKeyPress(
02642                 "Teletext Menu", e, tt_actions))
02643         {
02644             for (uint i = 0; i < tt_actions.size(); i++)
02645                 if (activenvp->HandleTeletextAction(tt_actions[i]))
02646                     return;
02647         }
02648     }
02649 
02650     // Interactive television
02651     if (activenvp && activenvp->GetInteractiveTV())
02652     {
02653         QStringList itv_actions;
02654         if (gContext->GetMainWindow()->TranslateKeyPress(
02655                 "TV Playback", e, itv_actions))
02656         for (uint i = 0; i < itv_actions.size(); i++)
02657         {
02658             if (activenvp->ITVHandleAction(itv_actions[i]))
02659                 return;
02660         }
02661     }
02662 
02663     if (!gContext->GetMainWindow()->TranslateKeyPress(
02664             "TV Playback", e, actions))
02665     {
02666         return;
02667     }
02668 
02669     bool handled = false;
02670 
02671     if (browsemode)
02672     {
02673         int passThru = 0;
02674 
02675         for (unsigned int i = 0; i < actions.size() && !handled; i++)
02676         {
02677             QString action = actions[i];
02678             handled = true;
02679 
02680             if (action == "UP" || action == "CHANNELUP")
02681                 BrowseDispInfo(BROWSE_UP);
02682             else if (action == "DOWN" || action == "CHANNELDOWN")
02683                 BrowseDispInfo(BROWSE_DOWN);
02684             else if (action == "LEFT")
02685                 BrowseDispInfo(BROWSE_LEFT);
02686             else if (action == "RIGHT")
02687                 BrowseDispInfo(BROWSE_RIGHT);
02688             else if (action == "NEXTFAV")
02689                 BrowseDispInfo(BROWSE_FAVORITE);
02690             else if (action == "0" || action == "1" || action == "2" ||
02691                      action == "3" || action == "4" || action == "5" ||
02692                      action == "6" || action == "7" || action == "8" ||
02693                      action == "9")
02694             {
02695                 AddKeyToInputQueue('0' + action.toInt());
02696             }
02697             else if (action == "TOGGLEBROWSE" || action == "ESCAPE" ||
02698                      action == "CLEAROSD")
02699             {
02700                 CommitQueuedInput(); 
02701                 BrowseEnd(false);
02702             }
02703             else if (action == "SELECT")
02704             {
02705                 CommitQueuedInput(); 
02706                 BrowseEnd(true);
02707             }
02708             else if (action == "TOGGLERECORD")
02709                 ToggleRecord();
02710             else if (action == "VOLUMEDOWN" || action == "VOLUMEUP" ||
02711                      action == "STRETCHINC" || action == "STRETCHDEC" ||
02712                      action == "MUTE"       || action == "TOGGLEASPECT" ||
02713                      action == "TOGGLEFILL" )
02714             {
02715                 passThru = 1;
02716                 handled = false;
02717             }
02718             else if (action == "TOGGLEPIPWINDOW" || action == "TOGGLEPIPMODE" ||
02719                      action == "SWAPPIP")
02720             {
02721                 passThru = 1;
02722                 handled = false;
02723                 redisplayBrowseInfo = true;
02724             }
02725             else
02726                 handled = false;
02727         }
02728 
02729         if (!passThru)
02730             return;
02731     }
02732 
02733     if (zoomMode)
02734     {
02735         int passThru = 0;
02736 
02737         for (unsigned int i = 0; i < actions.size() && !handled; i++)
02738         {
02739             QString action = actions[i];
02740             handled = true;
02741 
02742             if (action == "UP" || action == "CHANNELUP")
02743                 nvp->Zoom(kZoomUp);
02744             else if (action == "DOWN" || action == "CHANNELDOWN")
02745                 nvp->Zoom(kZoomDown);
02746             else if (action == "LEFT")
02747                 nvp->Zoom(kZoomLeft);
02748             else if (action == "RIGHT")
02749                 nvp->Zoom(kZoomRight);
02750             else if (action == "VOLUMEUP")
02751                 nvp->Zoom(kZoomAspectUp);
02752             else if (action == "VOLUMEDOWN")
02753                 nvp->Zoom(kZoomAspectDown);
02754             else if (action == "ESCAPE")
02755             {
02756                 nvp->Zoom(kZoomHome);
02757                 SetManualZoom(false);
02758             }
02759             else if (action == "SELECT")
02760                 SetManualZoom(false);
02761             else if (action == "JUMPFFWD")
02762                 nvp->Zoom(kZoomIn);
02763             else if (action == "JUMPRWND")
02764                 nvp->Zoom(kZoomOut);
02765             else if (action == "VOLUMEDOWN" || action == "VOLUMEUP" ||
02766                      action == "STRETCHINC" || action == "STRETCHDEC" ||
02767                      action == "MUTE" || action == "PAUSE" ||
02768                      action == "CLEAROSD")
02769             {
02770                 passThru = 1;
02771                 handled = false;
02772             }
02773             else
02774                 handled = false;
02775         }
02776 
02777         if (!passThru)
02778             return;
02779     }
02780 
02781     if (dialogname != "" && GetOSD() && GetOSD()->DialogShowing(dialogname))
02782     {
02783         for (unsigned int i = 0; i < actions.size() && !handled; i++)
02784         {
02785             QString action = actions[i];
02786             handled = true;
02787 
02788             if (action == "UP")
02789                 GetOSD()->DialogUp(dialogname); 
02790             else if (action == "DOWN")
02791                 GetOSD()->DialogDown(dialogname);
02792             else if (((action == "RWNDSTICKY" || action == "SEEKRWND" ||
02793                         action == "JUMPRWND"))
02794                     && IsVideoExitDialog() &&
02795                     !has_action("UP", actions) &&
02796                     !has_action("DOWN", actions) &&
02797                     !(has_action("LEFT", actions) && arrowAccel))
02798             {
02799                 DoPause(false);
02800                 GetOSD()->TurnDialogOff(dialogname);
02801                 dialogname = "";
02802                 ClearInputQueues(true);
02803                 requestDelete = false;
02804                 jumped_back = true;
02805                 if (action == "RWNDSTICKY")
02806                     ChangeFFRew(-3);
02807                 else if (action == "JUMPRWND")
02808                     DoSeek(-jumptime * 60, tr("Jump Back"));
02809                 else
02810                     DoSeek(-rewtime, tr("Skip Back"));
02811             }
02812             else if (action == "ESCAPE" && isnearend)
02813             {
02814                 requestDelete = false;
02815                 exitPlayer    = true;
02816                 wantsToQuit   = true;
02817             }
02818             else if (action == "SELECT" || action == "ESCAPE" || 
02819                      action == "CLEAROSD" ||
02820                      ((arrowAccel) && (action == "LEFT" || action == "RIGHT")))
02821             {
02822                 if (action == "ESCAPE" || action == "CLEAROSD" ||
02823                     (arrowAccel && action == "LEFT"))
02824                     GetOSD()->DialogAbort(dialogname);
02825                 GetOSD()->TurnDialogOff(dialogname);
02826                 if (dialogname == "alreadybeingedited")
02827                 {
02828                     int result = GetOSD()->GetDialogResponse(dialogname);
02829                     if (result == 1) 
02830                     {
02831                        playbackinfo->SetEditing(false);
02832                        editmode = nvp->EnableEdit();
02833                     }
02834                     else
02835                     {
02836                         paused = !paused;
02837                         DoPause();
02838                     }
02839                 }
02840                 else if (dialogname == "exitplayoptions") 
02841                 {
02842                     int result = GetOSD()->GetDialogResponse(dialogname);
02843 
02844                     if (result > 0)
02845                     {
02846                         if (!BookmarkAllowed())
02847                             result++;
02848                         if (result > 2 && !DeleteAllowed())
02849                             result++;
02850                     }
02851 
02852                     switch (result)
02853                     {
02854                         case 0: case 4:
02855                             DoPause();
02856                             break;
02857                         case 1:
02858                             nvp->SetBookmark();
02859                             exitPlayer = true;
02860                             wantsToQuit = true;
02861                             break;
02862                         case 3:
02863                             dialogname = "";
02864                             DoPause();
02865                             PromptDeleteRecording(
02866                                 tr("Delete this recording?")); 
02867                             return;
02868                         default:
02869                             exitPlayer = true;
02870                             wantsToQuit = true;
02871                             break;
02872                     }
02873                 }
02874                 else if (dialogname == "askdeleterecording")
02875                 {
02876                     int result = GetOSD()->GetDialogResponse(dialogname);
02877 
02878                     switch (result)
02879                     {
02880                         case 1:
02881                             exitPlayer = true;
02882                             wantsToQuit = true;
02883                             allowRerecord = true;
02884                             requestDelete = true;
02885                             break;
02886                         case 2:
02887                             exitPlayer = true;
02888                             wantsToQuit = true;
02889                             requestDelete = true;
02890                             break;
02891                         case 3:
02892                             exitPlayer = true;
02893                             wantsToQuit = true;
02894                             break;
02895                         default:
02896                             if (isnearend)
02897                             {
02898                                 exitPlayer = true;
02899                                 wantsToQuit = true;
02900                             }
02901                             else
02902                                 DoPause();
02903                             break;
02904                     }
02905                 }
02906                 else if (dialogname == "allowrecordingbox")
02907                 {
02908                     HandleOSDAskAllowResponse();
02909                 }
02910                 else if (dialogname == "idletimeout")
02911                 {
02912                     int result = GetOSD()->GetDialogResponse(dialogname);
02913 
02914                     if (result == 1)
02915                         idleTimer->changeInterval(
02916                                    gContext->GetNumSetting("LiveTVIdleTimeout",
02917                                                            0) * 60 * 1000);
02918                 }
02919                 else if (dialogname == "channel_timed_out")
02920                 {
02921                     lockTimerOn = false;
02922                 }
02923 
02924                 while (GetOSD()->DialogShowing(dialogname))
02925                 {
02926                     usleep(1000);
02927                 }
02928 
02929                 dialogname = "";
02930             }
02931             else
02932                 handled = false;
02933         }
02934         return;
02935     }
02936 
02937     if (adjustingPicture)
02938     {
02939         for (unsigned int i = 0; i < actions.size(); i++)
02940         {
02941             QString action = actions[i];
02942             handled = true;
02943 
02944             if (action == "LEFT")
02945                 DoChangePictureAttribute(adjustingPicture,
02946                                          adjustingPictureAttribute, false);
02947             else if (action == "RIGHT")
02948                 DoChangePictureAttribute(adjustingPicture,
02949                                          adjustingPictureAttribute, true);
02950             else
02951                 handled = false;
02952         }
02953     }
02954    
02955     if (stretchAdjustment)
02956     {
02957         for (unsigned int i = 0; i < actions.size(); i++)
02958         {
02959             QString action = actions[i];
02960             handled = true;
02961 
02962             if (action == "LEFT")
02963                 ChangeTimeStretch(-1);
02964             else if (action == "RIGHT")
02965                 ChangeTimeStretch(1);
02966             else if (action == "DOWN")
02967                 ChangeTimeStretch(-5);
02968             else if (action == "UP")
02969                 ChangeTimeStretch(5);
02970             else if (action == "ADJUSTSTRETCH")
02971                 ClearOSD();
02972             else
02973                 handled = false;
02974         }
02975     }
02976    
02977     if (audiosyncAdjustment)
02978     {
02979         for (unsigned int i = 0; i < actions.size(); i++)
02980         {
02981             QString action = actions[i];
02982             handled = true;
02983 
02984             if (action == "LEFT")
02985                 ChangeAudioSync(-1);
02986             else if (action == "RIGHT")
02987                 ChangeAudioSync(1);
02988             else if (action == "UP")
02989                 ChangeAudioSync(-10);
02990             else if (action == "DOWN")
02991                 ChangeAudioSync(10);
02992             else if (action == "1")
02993                 ChangeAudioSync(1000000);
02994             else if (action == "0")
02995                 ChangeAudioSync(-1000000);
02996             else if (action == "TOGGLEAUDIOSYNC")
02997                 ClearOSD();
02998             else
02999                 handled = false;
03000         }
03001     }
03002 
03003 #if DEBUG_ACTIONS
03004     for (uint i = 0; i < actions.size(); ++i)
03005         VERBOSE(VB_IMPORTANT, LOC + QString("handled(%1) actions[%2](%3)")
03006                 .arg(handled).arg(i).arg(actions[i]));
03007 #endif // DEBUG_ACTIONS
03008 
03009     if (handled)
03010         return;
03011 
03012     if (activerbuffer &&
03013         activerbuffer->isDVD())
03014     {
03015         for (unsigned int i = 0; i < actions.size(); i++)
03016         {
03017             QString action = actions[i];
03018             int nb_buttons = activerbuffer->DVD()->NumMenuButtons();
03019             if (nb_buttons > 0)
03020             {
03021                 handled = true;
03022                 if (action == "UP" || action == "CHANNELUP")
03023                     activerbuffer->DVD()->MoveButtonUp();
03024                 else if (action == "DOWN" || action == "CHANNELDOWN")
03025                     activerbuffer->DVD()->MoveButtonDown();
03026                 else if (action == "LEFT" || action == "SEEKRWND")
03027                     activerbuffer->DVD()->MoveButtonLeft();
03028                 else if (action == "RIGHT" || action == "SEEKFFWD")
03029                     activerbuffer->DVD()->MoveButtonRight();
03030                 else if (action == "SELECT")
03031                     activenvp->ActivateDVDButton();
03032                 else
03033                     handled = false;
03034             }
03035             if (handled)
03036                 return;
03037        }
03038     }
03039              
03040     for (unsigned int i = 0; i < actions.size() && !handled; i++)
03041     {
03042         QString action = actions[i];
03043         handled = true;
03044 
03045         if (action == "SKIPCOMMERCIAL" && !activerbuffer->isDVD())
03046             DoSkipCommercials(1);
03047         else if (action == "SKIPCOMMBACK" && !activerbuffer->isDVD())
03048             DoSkipCommercials(-1);
03049         else if (action == "QUEUETRANSCODE" && !activerbuffer->isDVD())
03050             DoQueueTranscode("Default");
03051         else if (action == "QUEUETRANSCODE_AUTO")
03052             DoQueueTranscode("Autodetect");
03053         else if (action == "QUEUETRANSCODE_HIGH")
03054             DoQueueTranscode("High Quality");
03055         else if (action == "QUEUETRANSCODE_MEDIUM")
03056             DoQueueTranscode("Medium Quality");
03057         else if (action == "QUEUETRANSCODE_LOW")
03058             DoQueueTranscode("Low Quality");
03059         else if (action == "PLAY")
03060             DoPlay();
03061         else if (action == "PAUSE") 
03062             DoPause();
03063         else if (action == "SPEEDINC" &&
03064                  activerbuffer && !activerbuffer->InDVDMenuOrStillFrame())
03065             ChangeSpeed(1);
03066         else if (action == "SPEEDDEC" &&
03067                  activerbuffer && !activerbuffer->InDVDMenuOrStillFrame())
03068             ChangeSpeed(-1);
03069         else if (action == "ADJUSTSTRETCH")
03070             ChangeTimeStretch(0);   // just display
03071         else if (action == "TOGGLESTRETCH")
03072             ToggleTimeStretch();
03073         else if (action == "CYCLECOMMSKIPMODE") {
03074             SetAutoCommercialSkip((enum commSkipMode)
03075                                   ((autoCommercialSkip + 1) % CommSkipModes));
03076         }
03077         else if (action == "TOGGLEAUDIOSYNC")
03078             ChangeAudioSync(0);   // just display
03079         else if (action == "TOGGLEPICCONTROLS")
03080         {
03081             DoTogglePictureAttribute(kAdjustingPicture_Playback);
03082         }
03083         else if (action == "NEXTSCAN")
03084         {
03085             activenvp->NextScanType();
03086             if (GetOSD())
03087             {
03088                 QString msg = toString(activenvp->GetScanType());
03089                 GetOSD()->SetSettingsText(msg, 3);
03090             }
03091         }
03092         else if (action == "ARBSEEK")
03093         {
03094             if (asInputMode)
03095             {
03096                 asInputModeExpires.start();
03097                 ClearInputQueues(true);
03098                 UpdateOSDTextEntry(tr("Seek:"));
03099             }
03100             else
03101             {
03102                 asInputMode        = true;
03103                 asInputModeExpires = QTime::currentTime()
03104                     .addMSecs(kInputModeTimeout);
03105                 ccInputModeExpires = QTime::currentTime();
03106                 ClearInputQueues(false);
03107                 AddKeyToInputQueue(0);
03108             }            
03109         }
03110         else if (action == "SEEKFFWD" &&
03111                  activerbuffer && !activerbuffer->InDVDMenuOrStillFrame())
03112         {
03113             if (HasQueuedInput())
03114                 DoArbSeek(ARBSEEK_FORWARD);
03115             else if (paused)
03116             {
03117                 if (!activerbuffer->isDVD())
03118                     DoSeek(1.001 / frameRate, tr("Forward"));
03119             }
03120             else if (!stickykeys)
03121             {
03122                 if (smartForward && doSmartForward)
03123                     DoSeek(rewtime, tr("Skip Ahead"));
03124                 else
03125                     DoSeek(fftime, tr("Skip Ahead"));
03126             }
03127             else
03128                 ChangeFFRew(1);
03129         }
03130         else if (action == "FFWDSTICKY" &&
03131                  activerbuffer && !activerbuffer->InDVDMenuOrStillFrame())
03132         {
03133             if (HasQueuedInput())
03134                 DoArbSeek(ARBSEEK_END);
03135             else if (paused)
03136             {
03137                 if (!activerbuffer->isDVD())
03138                     DoSeek(1.0, tr("Forward"));
03139             }
03140             else
03141                 ChangeFFRew(1);
03142         }
03143         else if (action == "SEEKRWND" &&
03144                  activerbuffer && !activerbuffer->InDVDMenuOrStillFrame())
03145         {
03146             if (HasQueuedInput())
03147                 DoArbSeek(ARBSEEK_REWIND);
03148             else if (paused)
03149             {
03150                 if (!activerbuffer->isDVD())
03151                     DoSeek(-1.001 / frameRate, tr("Rewind"));
03152             }
03153             else if (!stickykeys)
03154             {
03155                 if (smartForward)
03156                     doSmartForward = true;
03157                 DoSeek(-rewtime, tr("Skip Back"));
03158             }
03159             else
03160                 ChangeFFRew(-1);
03161         }
03162         else if (action == "RWNDSTICKY" &&
03163                  activerbuffer && !activerbuffer->InDVDMenuOrStillFrame())
03164         {
03165             if (HasQueuedInput())
03166                 DoArbSeek(ARBSEEK_SET);
03167             else if (paused)
03168             {
03169                 if (!activerbuffer->isDVD())
03170                     DoSeek(-1.0, tr("Rewind"));
03171             }
03172             else
03173                 ChangeFFRew(-1);
03174         }
03175         else if (action == "JUMPRWND")
03176         {
03177             if (activerbuffer->isDVD())
03178                 DVDJumpBack();       
03179             else
03180                 DoSeek(-jumptime * 60, tr("Jump Back"));
03181         }
03182         else if (action == "JUMPFFWD")
03183         {
03184             if (activerbuffer->isDVD())
03185                 DVDJumpForward();
03186             else
03187                 DoSeek(jumptime * 60, tr("Jump Ahead"));
03188         }
03189         else if (action == "JUMPBKMRK")
03190         {
03191             int bookmark = activenvp->GetBookmark();
03192             if (bookmark > frameRate)
03193                 DoSeek((bookmark - activenvp->GetFramesPlayed()) / frameRate,
03194                        tr("Jump to Bookmark"));
03195         }
03196         else if (action == "JUMPSTART" && activenvp)
03197         {
03198             DoSeek(-activenvp->GetFramesPlayed() / frameRate,
03199                    tr("Jump to Beginning"));
03200         }
03201         else if (action == "CLEAROSD")
03202         {
03203             ClearOSD();
03204         }
03205         else if ((action == "JUMPPREV") ||
03206                  ((action == "PREVCHAN") && (!StateIsLiveTV(GetState()))))
03207         {
03208             if (PromptRecGroupPassword())
03209             {
03210                 nvp->SetBookmark();
03211                 exitPlayer = true;
03212                 jumpToProgram = true;
03213             }
03214         }
03215         else if (action == "VIEWSCHEDULED")
03216             EmbedWithNewThread(kViewSchedule);
03217         else if (action == "JUMPREC")
03218         {
03219             if (gContext->GetNumSetting("JumpToProgramOSD", 1) 
03220                     && StateIsPlaying(internalState))
03221             {
03222                 DisplayJumpMenuSoon();
03223             }
03224             else if (RunPlaybackBoxPtr)
03225                 EmbedWithNewThread(kPlaybackBox);
03226         }
03227         else if (action == "SIGNALMON")
03228         {
03229             if ((GetState() == kState_WatchingLiveTV) && activerecorder)
03230             {
03231                 QString input = activerecorder->GetInput();
03232                 uint timeout  = activerecorder->GetSignalLockTimeout(input);
03233 
03234                 if (timeout == 0xffffffff)
03235                 {
03236                     if (GetOSD())
03237                         GetOSD()->SetSettingsText("No Signal Monitor", 2);
03238                     return;
03239                 }
03240 
03241                 int rate   = sigMonMode ? 0 : 100;
03242                 int notify = sigMonMode ? 0 : 1;
03243 
03244                 PauseLiveTV();
03245                 activerecorder->SetSignalMonitoringRate(rate,notify);
03246                 UnpauseLiveTV();
03247 
03248                 lockTimerOn = false;
03249                 sigMonMode  = !sigMonMode;
03250             }
03251         }
03252         else if (action == "SCREENSHOT")
03253         {
03254             if (activenvp && activerbuffer && !activerbuffer->isDVD())
03255                 ScreenShot(activenvp->GetFramesPlayed());
03256         }
03257         else if (action == "EXITSHOWNOPROMPTS")
03258         {
03259             if (nvp)
03260                 nvp->SetBookmark();
03261             requestDelete = false;
03262             exitPlayer = true;
03263             wantsToQuit = true;
03264         }
03265         else if (action == "ESCAPE")
03266         {
03267             if (StateIsLiveTV(internalState) &&
03268                 (lastSignalMsgTime.elapsed() < kSMExitTimeout))
03269                 ClearOSD();
03270             else if (GetOSD() && ClearOSD())
03271                 return;
03272 
03273             NormalSpeed();
03274             StopFFRew();
03275 
03276             if (StateIsLiveTV(GetState()))
03277             {
03278                 if (nvp && 12 & gContext->GetNumSetting("PlaybackExitPrompt"))
03279                     PromptStopWatchingRecording();
03280                 else
03281                 {
03282                     exitPlayer = true;
03283                     wantsToQuit = true;
03284                 }
03285             }
03286             else 
03287             {
03288                 if (nvp &&
03289                     (5 & gContext->GetNumSetting("PlaybackExitPrompt")) &&
03290                     !underNetworkControl &&
03291                     prbuffer && !prbuffer->InDVDMenuOrStillFrame())
03292                 {
03293                     PromptStopWatchingRecording();
03294                     break;
03295                 }
03296                 else if (nvp && gContext->GetNumSetting("PlaybackExitPrompt") == 2)
03297                     nvp->SetBookmark();
03298                 if (nvp && gContext->GetNumSetting("AutomaticSetWatched", 0))
03299                     nvp->SetWatched();
03300                 exitPlayer = true;
03301                 wantsToQuit = true;
03302                 requestDelete = false;
03303             }
03304             break;
03305         }
03306         else if (action == "VOLUMEDOWN")
03307             ChangeVolume(false);
03308         else if (action == "VOLUMEUP")
03309             ChangeVolume(true);
03310         else if (action == "MUTE")
03311             ToggleMute();
03312         else if (action == "STRETCHINC")
03313             ChangeTimeStretch(1);
03314         else if (action == "STRETCHDEC")
03315             ChangeTimeStretch(-1);
03316         else if (action == "TOGGLEASPECT")
03317             ToggleAspectOverride();
03318         else if (action == "TOGGLEFILL")
03319             ToggleAdjustFill();
03320         else if (action == "MENU")
03321             ShowOSDTreeMenu();
03322         else 
03323             handled = HandleTrackAction(action);
03324     }
03325 
03326     if (!handled)
03327     {
03328         if (doing_ff_rew)
03329         {
03330             for (unsigned int i = 0; i < actions.size() && !handled; i++)
03331             {
03332                 QString action = actions[i];
03333                 bool ok = false;
03334                 int val = action.toInt(&ok);
03335 
03336                 if (ok && val < (int)ff_rew_speeds.size())
03337                 {
03338                     SetFFRew(val);
03339                     handled = true;
03340                 }
03341             }
03342 
03343             if (!handled)
03344             {
03345                 DoNVPSeek(StopFFRew());
03346                 UpdateOSDSeekMessage(PlayMesg(), osd_general_timeout);
03347                 handled = true;
03348             }
03349         }
03350 
03351         if (speed_index)
03352         {
03353             NormalSpeed();
03354             UpdateOSDSeekMessage(PlayMesg(), osd_general_timeout);
03355             handled = true;
03356         }
03357     }
03358 
03359     if (!handled)
03360     {
03361         for (unsigned int i = 0; i < actions.size() && !handled; i++)
03362         {
03363             QString action = actions[i];
03364             bool ok = false;
03365             int val = action.toInt(&ok);
03366 
03367             if (ok)
03368             {
03369                 AddKeyToInputQueue('0' + val);
03370                 handled = true;
03371             }
03372         }
03373     }
03374 
03375     if (StateIsLiveTV(GetState()) || StateIsPlaying(internalState))
03376     {
03377         for (unsigned int i = 0; i < actions.size() && !handled; i++)
03378         {
03379             QString action = actions[i];
03380             handled = true;
03381 
03382             if (action == "INFO")
03383             {
03384                 if (HasQueuedInput())
03385                     DoArbSeek(ARBSEEK_SET);
03386                 else
03387                     ToggleOSD(true);
03388             }
03389             else if (action == "TOGGLESLEEP")
03390                 ToggleSleepTimer();
03391             else
03392                 handled = false;
03393         }
03394     }
03395 
03396     uint aindx = (activenvp == nvp) ? 0 : 1;
03397     if (StateIsLiveTV(GetState()))
03398     {
03399         for (unsigned int i = 0; i < actions.size() && !handled; i++)
03400         {
03401             QString action = actions[i];
03402             handled = true;
03403 
03404             if (action == "TOGGLERECORD")
03405                 ToggleRecord();
03406             else if (action == "TOGGLEFAV")
03407                 ToggleChannelFavorite();
03408             else if (action == "SELECT")
03409             {
03410                 if (!CommitQueuedInput())
03411                     handled = false;
03412             }
03413             else if (action == "TOGGLECHANCONTROLS")
03414                 DoTogglePictureAttribute(kAdjustingPicture_Channel);
03415             else if (action == "TOGGLERECCONTROLS")
03416                 DoTogglePictureAttribute(kAdjustingPicture_Recording);
03417             else if (action == "TOGGLEBROWSE" && pseudoLiveTVState[aindx])
03418                 ShowOSDTreeMenu();
03419             else
03420                 handled = false;
03421         }
03422 
03423         if (redisplayBrowseInfo)
03424             BrowseStart();
03425     }
03426 
03427     if ((StateIsLiveTV(GetState()) || StateIsPlaying(internalState)) &&
03428         (activerbuffer && !activerbuffer->InDVDMenuOrStillFrame()))
03429     {
03430         for (unsigned int i = 0; i < actions.size() && !handled; i++)
03431         {
03432             QString action = actions[i];
03433             handled = true;
03434     
03435             if (action == "SELECT")
03436             {
03437                 if (!was_doing_ff_rew)
03438                 {
03439                     if (prbuffer->isDVD())
03440                         prbuffer->DVD()->JumpToTitle(false);
03441 
03442                     if (gContext->GetNumSetting("AltClearSavedPosition", 1)
03443                         && nvp->GetBookmark())
03444                         nvp->ClearBookmark(); 
03445                     else
03446                         nvp->SetBookmark(); 
03447                 }
03448                 else
03449                     handled = false;
03450             }
03451             else
03452                 handled = false;
03453         }
03454     }
03455     
03456     if (StateIsLiveTV(GetState()) && !pseudoLiveTVState[aindx])
03457     {
03458         for (unsigned int i = 0; i < actions.size() && !handled; i++)
03459         {
03460             QString action = actions[i];
03461             handled = true;
03462 
03463             if (action == "NEXTFAV")
03464                 ChangeChannel(CHANNEL_DIRECTION_FAVORITE);
03465             else if (action == "NEXTSOURCE")
03466                 SwitchSource(kNextSource);
03467             else if (action == "PREVSOURCE")
03468                 SwitchSource(kPreviousSource);
03469             else if (action == "NEXTINPUT")
03470                 ToggleInputs();
03471             else if (action == "NEXTCARD")
03472                 SwitchCards();
03473             else if (action == "GUIDE")
03474                 EditSchedule(kScheduleProgramGuide);
03475             else if (action == "TOGGLEPIPMODE")
03476                 TogglePIPView();
03477             else if (action == "TOGGLEPIPWINDOW")
03478                 ToggleActiveWindow();
03479             else if (action == "SWAPPIP")
03480                 SwapPIPSoon();
03481             else if (action == "TOGGLEBROWSE")
03482                 BrowseStart();
03483             else if (action == "PREVCHAN")
03484                 PreviousChannel();
03485             else if (action == "CHANNELUP")
03486             {
03487                 if (persistentbrowsemode)
03488                     BrowseDispInfo(BROWSE_UP);
03489                 else
03490                     ChangeChannel(CHANNEL_DIRECTION_UP);
03491             }
03492             else if (action == "CHANNELDOWN")
03493             {
03494                 if (persistentbrowsemode)
03495                     BrowseDispInfo(BROWSE_DOWN);
03496                 else
03497                     ChangeChannel(CHANNEL_DIRECTION_DOWN);
03498             }
03499             else if (action == "TOGGLEEDIT")
03500                 StartChannelEditMode();
03501             else
03502                 handled = false;
03503         }
03504 
03505         if (redisplayBrowseInfo)
03506             BrowseStart();
03507     }
03508     else if (StateIsPlaying(internalState))
03509     {
03510         for (unsigned int i = 0; i < actions.size() && !handled; i++)
03511         {
03512             QString action = actions[i];
03513             handled = true;
03514 
03515             if (action == "DELETE" && !activerbuffer->isDVD())
03516             {
03517                 NormalSpeed();
03518                 StopFFRew();
03519                 nvp->SetBookmark(); 
03520                 PromptDeleteRecording(tr("Delete this recording?"));
03521             }
03522             else if (action == "JUMPTODVDROOTMENU")
03523                 activenvp->GoToDVDMenu("menu");
03524             else if (action == "GUIDE")
03525                 EditSchedule(kScheduleProgramGuide);
03526             else if (action == "FINDER")
03527                 EditSchedule(kScheduleProgramFinder);
03528             else if (action == "TOGGLEEDIT" && !activerbuffer->isDVD())
03529                 StartProgramEditMode();
03530             else if (action == "TOGGLEBROWSE")
03531                 ShowOSDTreeMenu();
03532             else if (action == "CHANNELUP")
03533             {
03534                 if (activerbuffer->isDVD()) 
03535                     DVDJumpBack();
03536                 else
03537                     DoSeek(-jumptime * 60, tr("Jump Back"));
03538             }    
03539             else if (action == "CHANNELDOWN")
03540             {
03541                 if (activerbuffer->isDVD())
03542                     DVDJumpForward();
03543                 else
03544                     DoSeek(jumptime * 60, tr("Jump Ahead"));
03545             }
03546             else
03547                 handled = false;
03548         }
03549     }
03550 }
03551 
03552 void TV::ProcessNetworkControlCommand(const QString &command)
03553 {
03554 #ifdef DEBUG_ACTIONS
03555     VERBOSE(VB_IMPORTANT, LOC + "ProcessNetworkControlCommand(" +
03556             QString("%1)").arg(command));
03557 #endif
03558 
03559     QStringList tokens = QStringList::split(" ", command);
03560     if (tokens.size() < 2)
03561     {
03562         VERBOSE(VB_IMPORTANT, LOC_ERR + "Not enough tokens"
03563                 "in network control command" + "\n\t\t\t" +
03564                 QString("'%1'").arg(command));
03565         return;
03566     }
03567 
03568     if (!dialogname.isEmpty())
03569     {
03570         VERBOSE(VB_IMPORTANT, LOC_WARN + "Ignoring network "
03571                 "control command\n\t\t\t" +
03572                 QString("because dialog '%1'").arg(dialogname) +
03573                 "is waiting for a response");
03574         return;
03575     }
03576 
03577     if (tokens[1] != "QUERY")
03578         ClearOSD();
03579 
03580     if (tokens.size() == 3 && tokens[1] == "CHANID")
03581     {
03582         queuedChanID = tokens[2].toUInt();
03583         queuedChanNum = QString::null;
03584         CommitQueuedInput();
03585     }
03586     else if (tokens.size() == 3 && tokens[1] == "CHANNEL")
03587     {
03588         uint aindx = (activenvp == nvp) ? 0 : 1;
03589         if (StateIsLiveTV(GetState()) && !pseudoLiveTVState[aindx])
03590         {
03591             if (tokens[2] == "UP")
03592                 ChangeChannel(CHANNEL_DIRECTION_UP);
03593             else if (tokens[2] == "DOWN")
03594                 ChangeChannel(CHANNEL_DIRECTION_DOWN);
03595             else if (tokens[2].contains(QRegExp("^[-\\.\\d_#]+$")))
03596                 ChangeChannel(0, tokens[2]);
03597         }
03598     }
03599     else if (tokens.size() == 3 && tokens[1] == "SPEED")
03600     {
03601         if (tokens[2] == "0x")
03602         {
03603             NormalSpeed();
03604             StopFFRew();
03605 
03606             if (!paused)
03607                 DoPause();
03608         }
03609         else if (tokens[2].contains(QRegExp("^\\-*\\d+x$")))
03610         {
03611             QString speed = tokens[2].left(tokens[2].length()-1);
03612             bool ok = false;
03613             int tmpSpeed = speed.toInt(&ok);
03614             int searchSpeed = abs(tmpSpeed);
03615             unsigned int index;
03616 
03617             if (paused)
03618                 DoPause();
03619 
03620             if (tmpSpeed == 1)
03621             {
03622                 StopFFRew();
03623                 normal_speed = 1.0f;
03624                 ChangeTimeStretch(0, false);
03625 
03626                 return;