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;
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
00072
00073
00074 QStringList TV::lastProgramStringList = QStringList();
00075
00076
00077
00078
00079 RUNPLAYBACKBOX TV::RunPlaybackBoxPtr = NULL;
00080
00083 RUNVIEWSCHEDULED TV::RunViewScheduledPtr = NULL;
00084
00085
00086
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
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
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
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
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
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
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
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485 }
00486
00487 void *SpawnDecode(void *param)
00488 {
00489
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
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
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
00535 chanEditMapLock(true), ddMapSourceId(0), ddMapLoaderRunning(false),
00536
00537 sleep_index(0), sleepTimer(new QTimer(this)),
00538
00539 idleTimer(new QTimer(this)),
00540
00541 keyRepeat(true), keyrepeatTimer(new QTimer(this)),
00542
00543 doing_ff_rew(0), ff_rew_index(0), speed_index(0),
00544
00545 normal_speed(1.0f), prev_speed(1.5f),
00546
00547 frameRate(30.0f),
00548
00549 ccInputMode(false), ccInputModeExpires(QTime::currentTime()),
00550
00551 asInputMode(false), asInputModeExpires(QTime::currentTime()),
00552
00553 queuedChanNum(""),
00554 muteTimer(new QTimer(this)),
00555 lockTimerOn(false),
00556
00557 prevChanKeyCnt(0), prevChanTimer(new QTimer(this)),
00558
00559 browsemode(false), persistentbrowsemode(false),
00560 browseTimer(new QTimer(this)),
00561 browsechannum(""), browsechanid(""), browsestarttime(""),
00562
00563 recorderPlaybackInfo(NULL),
00564 playbackinfo(NULL), playbackLen(0),
00565 lastProgram(NULL), jumpToProgram(false),
00566 inPlaylist(false), underNetworkControl(false),
00567 isnearend(false),
00568
00569 nvp(NULL), pipnvp(NULL), activenvp(NULL),
00570
00571 recorder(NULL), piprecorder(NULL), activerecorder(NULL),
00572 switchToRec(NULL), lastrecordernum(-1),
00573
00574 tvchain(NULL), piptvchain(NULL),
00575
00576 prbuffer(NULL), piprbuffer(NULL), activerbuffer(NULL),
00577
00578 dialogname(""), treeMenu(NULL), udpnotify(NULL), osdlock(true),
00579
00580 lcdTitle(""), lcdSubtitle(""), lcdCallsign(""),
00581
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);
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
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
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
00693 DisplayRes *display_res = DisplayRes::GetDisplayRes();
00694 int maxWidth = 1920, maxHeight = 1440;
00695 if (switchMode && display_res)
00696 {
00697
00698
00699
00700 maxWidth = display_res->GetMaxWidth();
00701 maxHeight = display_res->GetMaxHeight();
00702
00703
00704
00705 if (fullscreen)
00706 {
00707 player_bounds.setSize(QSize(maxWidth, maxHeight));
00708
00709
00710 mainWindow->resize(player_bounds.size());
00711 }
00712 }
00713
00714
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
00724 mainWindow->setGeometry(player_bounds);
00725 mainWindow->setFixedSize(player_bounds.size());
00726
00727
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);
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
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
00957 testrec = switchToRec;
00958 switchToRec = NULL;
00959 }
00960 else
00961 {
00962
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
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
01007
01008
01009 QDateTime expiry = QDateTime::currentDateTime().addSecs(timeuntil);
01010 askAllowPrograms[key] = AskProgramInfo(expiry, hasrec, haslater, info);
01011 }
01012 else
01013 {
01014
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
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
01056
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
01079 bool busy_input_grps_loaded = false;
01080 vector<uint> busy_input_grps;
01081 TunedInputInfo busy_input;
01082 RemoteIsBusy(cardid, busy_input);
01083
01084
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
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
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
01144
01145 }
01146 else if (conflict_count == 1 && ((uint)(*it).info->cardid == cardid))
01147 {
01148
01149
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
01252
01253 while (!GetOSD())
01254 {
01255
01256 qApp->unlock();
01257 qApp->processEvents();
01258 usleep(1000);
01259 qApp->lock();
01260 }
01261
01262
01263
01264 if ((dialogname == "allowrecordingbox") &&
01265 GetOSD()->DialogShowing(dialogname))
01266 {
01267
01268 askAllowType = kAskAllowCancel;
01269 GetOSD()->HideSet("allowrecordingbox");
01270 while (GetOSD()->DialogShowing(dialogname))
01271 {
01272
01273 usleep(1000);
01274 }
01275 }
01276
01277 askAllowType = type;
01278
01279 if (kAskAllowCancel != askAllowType)
01280 {
01281
01282 dialogname = "allowrecordingbox";
01283 GetOSD()->NewDialogBox(dialogname, message, options, timeuntil, sel);
01284 }
01285
01286
01287 }
01288
01289 void TV::HandleOSDAskAllowResponse(void)
01290 {
01291 if (!askAllowLock.tryLock())
01292 {
01293
01294 return;
01295 }
01296
01297 int result = GetOSD()->GetDialogResponse(dialogname);
01298 if (kAskAllowOneRec == askAllowType)
01299 {
01300
01301 switch (result)
01302 {
01303 default:
01304 case 1:
01305
01306 recorder->CancelNextRecording(false);
01307 break;
01308 case 2:
01309
01310 StopLiveTV();
01311 break;
01312 case 3:
01313
01314 recorder->CancelNextRecording(true);
01315 break;
01316 }
01317 }
01318 else if (kAskAllowMultiRec == askAllowType)
01319 {
01320
01321 switch (result)
01322 {
01323 default:
01324 case 1:
01325
01326 StopLiveTV();
01327 break;
01328 case 2:
01329 {
01330
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
01345
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
01401
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
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
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
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
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();
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
01991
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();
02045 NuppelVideoPlayer *xnvp = pipnvp;
02046 pthread_t xdec = pipdecode;
02047 pipnvp = NULL;
02048 osdlock.unlock();
02049
02050
02051
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 {
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
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
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
02370 if (HasQueuedChannel() && GetOSD())
02371 {
02372 OSDSet *set = GetOSD()->GetSet("channel_number");
02373 if ((set && !set->Displaying()) || !set)
02374 CommitQueuedInput();
02375 }
02376
02377 if (ccInputMode && (ccInputModeExpires < QTime::currentTime()))
02378 {
02379 ccInputMode = false;
02380 ClearInputQueues(true);
02381 }
02382
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
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();
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
02618
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
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
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);
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);
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;