|
MythTV
0.26-pre
|
00001 #include <cstdlib> 00002 #include <cstdarg> 00003 #include <cstring> 00004 #include <cmath> 00005 #include <unistd.h> 00006 #include <stdint.h> 00007 00008 #include <algorithm> 00009 using namespace std; 00010 00011 #include <QCoreApplication> 00012 #include <QKeyEvent> 00013 #include <QRunnable> 00014 #include <QRegExp> 00015 #include <QTimer> 00016 #include <QEvent> 00017 #include <QFile> 00018 #include <QDir> 00019 00020 #include "mythdb.h" 00021 #include "tv_play.h" 00022 #include "tv_rec.h" 00023 #include "mythcorecontext.h" 00024 #include "remoteencoder.h" 00025 #include "remoteutil.h" 00026 #include "tvremoteutil.h" 00027 #include "mythplayer.h" 00028 #include "subtitlescreen.h" 00029 #include "DetectLetterbox.h" 00030 #include "programinfo.h" 00031 #include "vsync.h" 00032 #include "lcddevice.h" 00033 #include "jobqueue.h" 00034 #include "audiooutput.h" 00035 #include "DisplayRes.h" 00036 #include "signalmonitorvalue.h" 00037 #include "scheduledrecording.h" 00038 #include "recordingrule.h" 00039 #include "previewgenerator.h" 00040 #include "mythconfig.h" 00041 #include "livetvchain.h" 00042 #include "playgroup.h" 00043 #include "datadirect.h" 00044 #include "sourceutil.h" 00045 #include "cardutil.h" 00046 #include "channelutil.h" 00047 #include "compat.h" 00048 #include "mythuihelper.h" 00049 #include "mythdialogbox.h" 00050 #include "mythmainwindow.h" 00051 #include "mythscreenstack.h" 00052 #include "mythscreentype.h" 00053 #include "tv_play_win.h" 00054 #include "recordinginfo.h" 00055 #include "mythsystemevent.h" 00056 #include "videometadatautil.h" 00057 #include "mythdirs.h" 00058 #include "tvbrowsehelper.h" 00059 #include "mythlogging.h" 00060 #include "mythuistatetracker.h" 00061 #include "DVD/dvdringbuffer.h" 00062 #include "Bluray/bdringbuffer.h" 00063 00064 #if ! HAVE_ROUND 00065 #define round(x) ((int) ((x) + 0.5)) 00066 #endif 00067 00068 #define DEBUG_CHANNEL_PREFIX 0 00069 #define DEBUG_ACTIONS 0 00071 #define LOC QString("TV: ") 00072 00073 #define GetPlayer(X,Y) GetPlayerHaveLock(X, Y, __FILE__ , __LINE__) 00074 #define GetOSDLock(X) GetOSDL(X, __FILE__, __LINE__) 00075 00076 #define SetOSDText(CTX, GROUP, FIELD, TEXT, TIMEOUT) { \ 00077 OSD *osd = GetOSDLock(CTX); \ 00078 if (osd) \ 00079 { \ 00080 QHash<QString,QString> map; \ 00081 map.insert(FIELD,TEXT); \ 00082 osd->SetText(GROUP, map, TIMEOUT); \ 00083 } \ 00084 ReturnOSDLock(CTX, osd); } 00085 00086 #define SetOSDMessage(CTX, MESSAGE) \ 00087 SetOSDText(CTX, "osd_message", "message_text", MESSAGE, kOSDTimeout_Med) 00088 00089 #define HideOSDWindow(CTX, WINDOW) { \ 00090 OSD *osd = GetOSDLock(CTX); \ 00091 if (osd) \ 00092 osd->HideWindow(WINDOW); \ 00093 ReturnOSDLock(CTX, osd); } 00094 00095 const int TV::kInitFFRWSpeed = 0; 00096 const uint TV::kInputKeysMax = 6; 00097 const uint TV::kNextSource = 1; 00098 const uint TV::kPreviousSource = 2; 00099 const uint TV::kMaxPIPCount = 4; 00100 const uint TV::kMaxPBPCount = 2; 00101 00102 00103 const uint TV::kInputModeTimeout = 5000; 00104 const uint TV::kLCDTimeout = 1000; 00105 const uint TV::kBrowseTimeout = 30000; 00106 const uint TV::kKeyRepeatTimeout = 300; 00107 const uint TV::kPrevChanTimeout = 750; 00108 const uint TV::kSleepTimerDialogTimeout = 45000; 00109 const uint TV::kIdleTimerDialogTimeout = 45000; 00110 const uint TV::kVideoExitDialogTimeout = 120000; 00111 00112 const uint TV::kEndOfPlaybackCheckFrequency = 250; 00113 const uint TV::kEndOfRecPromptCheckFrequency = 250; 00114 const uint TV::kEmbedCheckFrequency = 250; 00115 const uint TV::kSpeedChangeCheckFrequency = 250; 00116 const uint TV::kErrorRecoveryCheckFrequency = 250; 00117 #ifdef USING_VALGRIND 00118 const uint TV::kEndOfPlaybackFirstCheckTimer = 60000; 00119 #else 00120 const uint TV::kEndOfPlaybackFirstCheckTimer = 5000; 00121 #endif 00122 00127 QStringList TV::lastProgramStringList = QStringList(); 00128 00132 EMBEDRETURNVOID TV::RunPlaybackBoxPtr = NULL; 00133 00137 EMBEDRETURNVOID TV::RunViewScheduledPtr = NULL; 00138 00142 EMBEDRETURNVOIDSCHEDIT TV::RunScheduleEditorPtr = NULL; 00143 00147 EMBEDRETURNVOIDEPG TV::RunProgramGuidePtr = NULL; 00148 00152 EMBEDRETURNVOIDFINDER TV::RunProgramFinderPtr = NULL; 00153 00155 class DDLoader : public QRunnable 00156 { 00157 public: 00158 DDLoader(TV *parent) : m_parent(parent), m_sourceid(0) 00159 { 00160 setAutoDelete(false); 00161 } 00162 00163 void SetParent(TV *parent) { m_parent = parent; } 00164 void SetSourceID(uint sourceid) { m_sourceid = sourceid; } 00165 00166 virtual void run(void) 00167 { 00168 if (m_parent) 00169 m_parent->RunLoadDDMap(m_sourceid); 00170 else 00171 SourceUtil::UpdateChannelsFromListings(m_sourceid); 00172 00173 QMutexLocker locker(&m_lock); 00174 m_sourceid = 0; 00175 m_wait.wakeAll(); 00176 } 00177 00178 void wait(void) 00179 { 00180 QMutexLocker locker(&m_lock); 00181 while (m_sourceid) 00182 m_wait.wait(locker.mutex()); 00183 } 00184 00185 private: 00186 TV *m_parent; 00187 uint m_sourceid; 00188 QMutex m_lock; 00189 QWaitCondition m_wait; 00190 }; 00191 00195 int TV::ConfiguredTunerCards(void) 00196 { 00197 int count = 0; 00198 00199 MSqlQuery query(MSqlQuery::InitCon()); 00200 query.prepare("SELECT COUNT(cardid) FROM capturecard;"); 00201 if (query.exec() && query.isActive() && query.size() && query.next()) 00202 count = query.value(0).toInt(); 00203 00204 LOG(VB_RECORD, LOG_INFO, 00205 "ConfiguredTunerCards() = " + QString::number(count)); 00206 00207 return count; 00208 } 00209 00210 static void multi_lock(QMutex *mutex0, ...) 00211 { 00212 vector<QMutex*> mutex; 00213 mutex.push_back(mutex0); 00214 00215 va_list argp; 00216 va_start(argp, mutex0); 00217 QMutex *cur = va_arg(argp, QMutex*); 00218 while (cur) 00219 { 00220 mutex.push_back(cur); 00221 cur = va_arg(argp, QMutex*); 00222 } 00223 va_end(argp); 00224 00225 for (bool success = false; !success;) 00226 { 00227 success = true; 00228 for (uint i = 0; success && (i < mutex.size()); i++) 00229 { 00230 if (!(success = mutex[i]->tryLock())) 00231 { 00232 for (uint j = 0; j < i; j++) 00233 mutex[j]->unlock(); 00234 usleep(25 * 1000); 00235 } 00236 } 00237 } 00238 } 00239 00240 QMutex* TV::gTVLock = new QMutex(); 00241 TV* TV::gTV = NULL; 00242 00243 bool TV::IsTVRunning(void) 00244 { 00245 QMutexLocker locker(gTVLock); 00246 return gTV; 00247 } 00248 00249 TV* TV::GetTV(void) 00250 { 00251 QMutexLocker locker(gTVLock); 00252 if (gTV) 00253 { 00254 LOG(VB_GENERAL, LOG_WARNING, LOC + "Already have a TV object."); 00255 return NULL; 00256 } 00257 gTV = new TV(); 00258 return gTV; 00259 } 00260 00261 void TV::ReleaseTV(TV* tv) 00262 { 00263 QMutexLocker locker(gTVLock); 00264 if (!tv || !gTV || (gTV != tv)) 00265 { 00266 LOG(VB_GENERAL, LOG_ERR, LOC + "ReleaseTV - programmer error."); 00267 return; 00268 } 00269 00270 delete gTV; 00271 gTV = NULL; 00272 } 00273 00277 bool TV::StartTV(ProgramInfo *tvrec, uint flags) 00278 { 00279 TV *tv = GetTV(); 00280 if (!tv) 00281 return false; 00282 00283 LOG(VB_PLAYBACK, LOG_INFO, LOC + "StartTV() -- begin"); 00284 bool startInGuide = flags & kStartTVInGuide; 00285 bool inPlaylist = flags & kStartTVInPlayList; 00286 bool initByNetworkCommand = flags & kStartTVByNetworkCommand; 00287 bool quitAll = false; 00288 bool showDialogs = true; 00289 bool playCompleted = false; 00290 ProgramInfo *curProgram = NULL; 00291 bool startSysEventSent = false; 00292 00293 00294 if (tvrec) 00295 { 00296 curProgram = new ProgramInfo(*tvrec); 00297 curProgram->SetIgnoreBookmark(flags & kStartTVIgnoreBookmark); 00298 } 00299 00300 // Must be before Init() otherwise we swallow the PLAYBACK_START event 00301 // with the event filter 00302 sendPlaybackStart(); 00303 GetMythMainWindow()->PauseIdleTimer(true); 00304 00305 // Initialize TV 00306 if (!tv->Init()) 00307 { 00308 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed initializing TV"); 00309 ReleaseTV(tv); 00310 sendPlaybackEnd(); 00311 GetMythMainWindow()->PauseIdleTimer(false); 00312 delete curProgram; 00313 return false; 00314 } 00315 00316 if (!lastProgramStringList.empty()) 00317 { 00318 ProgramInfo pginfo(lastProgramStringList); 00319 if (pginfo.HasPathname() || pginfo.GetChanID()) 00320 tv->SetLastProgram(&pginfo); 00321 } 00322 00323 if (curProgram) 00324 { 00325 startSysEventSent = true; 00326 SendMythSystemPlayEvent("PLAY_STARTED", curProgram); 00327 } 00328 00329 QString playerError = QString::null; 00330 while (!quitAll) 00331 { 00332 if (curProgram) 00333 { 00334 LOG(VB_PLAYBACK, LOG_INFO, LOC + "tv->Playback() -- begin"); 00335 if (!tv->Playback(*curProgram)) 00336 { 00337 quitAll = true; 00338 } 00339 else if (!startSysEventSent) 00340 { 00341 startSysEventSent = true; 00342 SendMythSystemPlayEvent("PLAY_STARTED", curProgram); 00343 } 00344 00345 LOG(VB_PLAYBACK, LOG_INFO, LOC + "tv->Playback() -- end"); 00346 } 00347 else if (RemoteGetFreeRecorderCount()) 00348 { 00349 LOG(VB_PLAYBACK, LOG_INFO, LOC + "tv->LiveTV() -- begin"); 00350 if (!tv->LiveTV(showDialogs)) 00351 { 00352 tv->SetExitPlayer(true, true); 00353 quitAll = true; 00354 } 00355 else if (!startSysEventSent) 00356 { 00357 startSysEventSent = true; 00358 gCoreContext->SendSystemEvent("LIVETV_STARTED"); 00359 } 00360 00361 if (!quitAll && (startInGuide || tv->StartLiveTVInGuide())) 00362 tv->DoEditSchedule(); 00363 00364 LOG(VB_PLAYBACK, LOG_INFO, LOC + "tv->LiveTV() -- end"); 00365 } 00366 else 00367 { 00368 if (!ConfiguredTunerCards()) 00369 LOG(VB_GENERAL, LOG_ERR, LOC + "No tuners configured"); 00370 else 00371 LOG(VB_GENERAL, LOG_ERR, LOC + "No tuners free for live tv"); 00372 quitAll = true; 00373 continue; 00374 } 00375 00376 tv->setInPlayList(inPlaylist); 00377 tv->setUnderNetworkControl(initByNetworkCommand); 00378 00379 // Process Events 00380 LOG(VB_GENERAL, LOG_INFO, LOC + "Entering main playback loop."); 00381 tv->PlaybackLoop(); 00382 LOG(VB_GENERAL, LOG_INFO, LOC + "Exiting main playback loop."); 00383 00384 if (tv->getJumpToProgram()) 00385 { 00386 ProgramInfo *nextProgram = tv->GetLastProgram(); 00387 00388 tv->SetLastProgram(curProgram); 00389 if (curProgram) 00390 delete curProgram; 00391 00392 curProgram = nextProgram; 00393 00394 SendMythSystemPlayEvent("PLAY_CHANGED", curProgram); 00395 continue; 00396 } 00397 00398 const PlayerContext *mctx = 00399 tv->GetPlayerReadLock(0, __FILE__, __LINE__); 00400 quitAll = tv->wantsToQuit || (mctx && mctx->errored); 00401 if (mctx) 00402 { 00403 mctx->LockDeletePlayer(__FILE__, __LINE__); 00404 if (mctx->player && mctx->player->IsErrored()) 00405 playerError = mctx->player->GetError(); 00406 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 00407 } 00408 tv->ReturnPlayerLock(mctx); 00409 } 00410 00411 LOG(VB_PLAYBACK, LOG_INFO, LOC + "StartTV -- process events 2 begin"); 00412 qApp->processEvents(); 00413 LOG(VB_PLAYBACK, LOG_INFO, LOC + "StartTV -- process events 2 end"); 00414 00415 // check if the show has reached the end. 00416 if (tvrec && tv->getEndOfRecording()) 00417 playCompleted = true; 00418 00419 bool allowrerecord = tv->getAllowRerecord(); 00420 bool deleterecording = tv->requestDelete; 00421 00422 ReleaseTV(tv); 00423 00424 if (curProgram) 00425 { 00426 SendMythSystemPlayEvent("PLAY_STOPPED", curProgram); 00427 00428 if (deleterecording) 00429 { 00430 QStringList list; 00431 list.push_back(QString::number(curProgram->GetChanID())); 00432 list.push_back(curProgram->GetRecordingStartTime(ISODate)); 00433 list.push_back("0"); // do not force delete 00434 list.push_back(allowrerecord ? "1" : "0"); 00435 MythEvent me("LOCAL_PBB_DELETE_RECORDINGS", list); 00436 gCoreContext->dispatch(me); 00437 } 00438 else if (curProgram->IsRecording()) 00439 { 00440 lastProgramStringList.clear(); 00441 curProgram->ToStringList(lastProgramStringList); 00442 } 00443 00444 delete curProgram; 00445 } 00446 else 00447 gCoreContext->SendSystemEvent("PLAY_STOPPED"); 00448 00449 if (!playerError.isEmpty()) 00450 { 00451 MythScreenStack *ss = GetMythMainWindow()->GetStack("popup stack"); 00452 MythConfirmationDialog *dlg = new MythConfirmationDialog( 00453 ss, playerError, false); 00454 if (!dlg->Create()) 00455 delete dlg; 00456 else 00457 ss->AddScreen(dlg); 00458 } 00459 00460 sendPlaybackEnd(); 00461 GetMythMainWindow()->PauseIdleTimer(false); 00462 00463 LOG(VB_PLAYBACK, LOG_INFO, LOC + "StartTV -- end"); 00464 00465 return playCompleted; 00466 } 00467 00472 void TV::SetFuncPtr(const char *string, void *lptr) 00473 { 00474 QString name(string); 00475 if (name == "playbackbox") 00476 RunPlaybackBoxPtr = (EMBEDRETURNVOID)lptr; 00477 else if (name == "viewscheduled") 00478 RunViewScheduledPtr = (EMBEDRETURNVOID)lptr; 00479 else if (name == "programguide") 00480 RunProgramGuidePtr = (EMBEDRETURNVOIDEPG)lptr; 00481 else if (name == "programfinder") 00482 RunProgramFinderPtr = (EMBEDRETURNVOIDFINDER)lptr; 00483 else if (name == "scheduleeditor") 00484 RunScheduleEditorPtr = (EMBEDRETURNVOIDSCHEDIT)lptr; 00485 } 00486 00487 void TV::InitKeys(void) 00488 { 00489 REG_KEY("TV Frontend", ACTION_PLAYBACK, QT_TRANSLATE_NOOP("MythControls", 00490 "Play Program"), "P"); 00491 REG_KEY("TV Frontend", ACTION_STOP, QT_TRANSLATE_NOOP("MythControls", 00492 "Stop Program"), ""); 00493 REG_KEY("TV Frontend", ACTION_TOGGLERECORD, QT_TRANSLATE_NOOP("MythControls", 00494 "Toggle recording status of current program"), "R"); 00495 REG_KEY("TV Frontend", ACTION_DAYLEFT, QT_TRANSLATE_NOOP("MythControls", 00496 "Page the program guide back one day"), "Home"); 00497 REG_KEY("TV Frontend", ACTION_DAYRIGHT, QT_TRANSLATE_NOOP("MythControls", 00498 "Page the program guide forward one day"), "End"); 00499 REG_KEY("TV Frontend", ACTION_PAGELEFT, QT_TRANSLATE_NOOP("MythControls", 00500 "Page the program guide left"), ",,<"); 00501 REG_KEY("TV Frontend", ACTION_PAGERIGHT, QT_TRANSLATE_NOOP("MythControls", 00502 "Page the program guide right"), ">,."); 00503 REG_KEY("TV Frontend", ACTION_TOGGLEFAV, QT_TRANSLATE_NOOP("MythControls", 00504 "Toggle the current channel as a favorite"), "?"); 00505 REG_KEY("TV Frontend", ACTION_TOGGLEPGORDER, QT_TRANSLATE_NOOP("MythControls", 00506 "Reverse the channel order in the program guide"), ""); 00507 REG_KEY("TV Frontend", ACTION_GUIDE, QT_TRANSLATE_NOOP("MythControls", 00508 "Show the Program Guide"), "S"); 00509 REG_KEY("TV Frontend", ACTION_FINDER, QT_TRANSLATE_NOOP("MythControls", 00510 "Show the Program Finder"), "#"); 00511 REG_KEY("TV Frontend", "NEXTFAV", QT_TRANSLATE_NOOP("MythControls", 00512 "Cycle through channel groups and all channels in the " 00513 "program guide."), "/"); 00514 REG_KEY("TV Frontend", "CHANUPDATE", QT_TRANSLATE_NOOP("MythControls", 00515 "Switch channels without exiting guide in Live TV mode."), "X"); 00516 REG_KEY("TV Frontend", ACTION_VOLUMEDOWN, QT_TRANSLATE_NOOP("MythControls", 00517 "Volume down"), "[,{,F10,Volume Down"); 00518 REG_KEY("TV Frontend", ACTION_VOLUMEUP, QT_TRANSLATE_NOOP("MythControls", 00519 "Volume up"), "],},F11,Volume Up"); 00520 REG_KEY("TV Frontend", ACTION_MUTEAUDIO, QT_TRANSLATE_NOOP("MythControls", 00521 "Mute"), "|,\\,F9,Volume Mute"); 00522 REG_KEY("TV Frontend", "CYCLEAUDIOCHAN", QT_TRANSLATE_NOOP("MythControls", 00523 "Cycle audio channels"), ""); 00524 REG_KEY("TV Frontend", "RANKINC", QT_TRANSLATE_NOOP("MythControls", 00525 "Increase program or channel rank"), "Right"); 00526 REG_KEY("TV Frontend", "RANKDEC", QT_TRANSLATE_NOOP("MythControls", 00527 "Decrease program or channel rank"), "Left"); 00528 REG_KEY("TV Frontend", "UPCOMING", QT_TRANSLATE_NOOP("MythControls", 00529 "List upcoming episodes"), "O"); 00530 REG_KEY("TV Frontend", ACTION_VIEWSCHEDULED, QT_TRANSLATE_NOOP("MythControls", 00531 "List scheduled upcoming episodes"), ""); 00532 REG_KEY("TV Frontend", "DETAILS", QT_TRANSLATE_NOOP("MythControls", 00533 "Show details"), "U"); 00534 REG_KEY("TV Frontend", "VIEWCARD", QT_TRANSLATE_NOOP("MythControls", 00535 "Switch Capture Card view"), "Y"); 00536 REG_KEY("TV Frontend", "VIEWINPUT", QT_TRANSLATE_NOOP("MythControls", 00537 "Switch Capture Card view"), "C"); 00538 REG_KEY("TV Frontend", "CUSTOMEDIT", QT_TRANSLATE_NOOP("MythControls", 00539 "Edit Custom Record Rule"), ""); 00540 REG_KEY("TV Frontend", "CHANGERECGROUP", QT_TRANSLATE_NOOP("MythControls", 00541 "Change Recording Group"), ""); 00542 REG_KEY("TV Frontend", "CHANGEGROUPVIEW", QT_TRANSLATE_NOOP("MythControls", 00543 "Change Group View"), ""); 00544 00545 REG_KEY("TV Playback", "BACK", QT_TRANSLATE_NOOP("MythControls", 00546 "Exit or return to DVD menu"), ""); 00547 REG_KEY("TV Playback", ACTION_CLEAROSD, QT_TRANSLATE_NOOP("MythControls", 00548 "Clear OSD"), "Backspace"); 00549 REG_KEY("TV Playback", ACTION_PAUSE, QT_TRANSLATE_NOOP("MythControls", 00550 "Pause"), "P"); 00551 REG_KEY("TV Playback", ACTION_SEEKFFWD, QT_TRANSLATE_NOOP("MythControls", 00552 "Fast Forward"), "Right"); 00553 REG_KEY("TV Playback", ACTION_SEEKRWND, QT_TRANSLATE_NOOP("MythControls", 00554 "Rewind"), "Left"); 00555 REG_KEY("TV Playback", ACTION_SEEKARB, QT_TRANSLATE_NOOP("MythControls", 00556 "Arbitrary Seek"), "*"); 00557 REG_KEY("TV Playback", ACTION_SEEKABSOLUTE, QT_TRANSLATE_NOOP("MythControls", 00558 "Seek to a position in seconds"), ""); 00559 REG_KEY("TV Playback", ACTION_CHANNELUP, QT_TRANSLATE_NOOP("MythControls", 00560 "Channel up"), "Up"); 00561 REG_KEY("TV Playback", ACTION_CHANNELDOWN, QT_TRANSLATE_NOOP("MythControls", 00562 "Channel down"), "Down"); 00563 REG_KEY("TV Playback", "NEXTFAV", QT_TRANSLATE_NOOP("MythControls", 00564 "Switch to the next favorite channel"), "/"); 00565 REG_KEY("TV Playback", "PREVCHAN", QT_TRANSLATE_NOOP("MythControls", 00566 "Switch to the previous channel"), "H"); 00567 REG_KEY("TV Playback", ACTION_JUMPFFWD, QT_TRANSLATE_NOOP("MythControls", 00568 "Jump ahead"), "PgDown"); 00569 REG_KEY("TV Playback", ACTION_JUMPRWND, QT_TRANSLATE_NOOP("MythControls", 00570 "Jump back"), "PgUp"); 00571 REG_KEY("TV Playback", "INFOWITHCUTLIST", QT_TRANSLATE_NOOP("MythControls", 00572 "Info utilizing cutlist"), ""); 00573 REG_KEY("TV Playback", ACTION_JUMPBKMRK, QT_TRANSLATE_NOOP("MythControls", 00574 "Jump to bookmark"), "K"); 00575 REG_KEY("TV Playback", "FFWDSTICKY", QT_TRANSLATE_NOOP("MythControls", 00576 "Fast Forward (Sticky) or Forward one frame while paused"), ">,."); 00577 REG_KEY("TV Playback", "RWNDSTICKY", QT_TRANSLATE_NOOP("MythControls", 00578 "Rewind (Sticky) or Rewind one frame while paused"), ",,<"); 00579 REG_KEY("TV Playback", "NEXTSOURCE", QT_TRANSLATE_NOOP("MythControls", 00580 "Next Video Source"), "Y"); 00581 REG_KEY("TV Playback", "PREVSOURCE", QT_TRANSLATE_NOOP("MythControls", 00582 "Previous Video Source"), ""); 00583 REG_KEY("TV Playback", "NEXTINPUT", QT_TRANSLATE_NOOP("MythControls", 00584 "Next Input"), "C"); 00585 REG_KEY("TV Playback", "NEXTCARD", QT_TRANSLATE_NOOP("MythControls", 00586 "Next Card"), ""); 00587 REG_KEY("TV Playback", "SKIPCOMMERCIAL", QT_TRANSLATE_NOOP("MythControls", 00588 "Skip Commercial"), "Z,End"); 00589 REG_KEY("TV Playback", "SKIPCOMMBACK", QT_TRANSLATE_NOOP("MythControls", 00590 "Skip Commercial (Reverse)"), "Q,Home"); 00591 REG_KEY("TV Playback", ACTION_JUMPSTART, QT_TRANSLATE_NOOP("MythControls", 00592 "Jump to the start of the recording."), "Ctrl+B"); 00593 REG_KEY("TV Playback", "TOGGLEBROWSE", QT_TRANSLATE_NOOP("MythControls", 00594 "Toggle channel browse mode"), "O"); 00595 REG_KEY("TV Playback", ACTION_TOGGLERECORD, QT_TRANSLATE_NOOP("MythControls", 00596 "Toggle recording status of current program"), "R"); 00597 REG_KEY("TV Playback", ACTION_TOGGLEFAV, QT_TRANSLATE_NOOP("MythControls", 00598 "Toggle the current channel as a favorite"), "?"); 00599 REG_KEY("TV Playback", ACTION_VOLUMEDOWN, QT_TRANSLATE_NOOP("MythControls", 00600 "Volume down"), "[,{,F10,Volume Down"); 00601 REG_KEY("TV Playback", ACTION_VOLUMEUP, QT_TRANSLATE_NOOP("MythControls", 00602 "Volume up"), "],},F11,Volume Up"); 00603 REG_KEY("TV Playback", ACTION_MUTEAUDIO, QT_TRANSLATE_NOOP("MythControls", 00604 "Mute"), "|,\\,F9,Volume Mute"); 00605 REG_KEY("TV Playback", ACTION_SETVOLUME, QT_TRANSLATE_NOOP("MythControls", 00606 "Set the volume"), ""); 00607 REG_KEY("TV Playback", "CYCLEAUDIOCHAN", QT_TRANSLATE_NOOP("MythControls", 00608 "Cycle audio channels"), ""); 00609 REG_KEY("TV Playback", ACTION_TOGGLEUPMIX, QT_TRANSLATE_NOOP("MythControls", 00610 "Toggle audio upmixer"), "Ctrl+U"); 00611 REG_KEY("TV Playback", "TOGGLEPIPMODE", QT_TRANSLATE_NOOP("MythControls", 00612 "Toggle Picture-in-Picture view"), "V"); 00613 REG_KEY("TV Playback", "TOGGLEPBPMODE", QT_TRANSLATE_NOOP("MythControls", 00614 "Toggle Picture-by-Picture view"), "Ctrl+V"); 00615 REG_KEY("TV Playback", "CREATEPIPVIEW", QT_TRANSLATE_NOOP("MythControls", 00616 "Create Picture-in-Picture view"), ""); 00617 REG_KEY("TV Playback", "CREATEPBPVIEW", QT_TRANSLATE_NOOP("MythControls", 00618 "Create Picture-by-Picture view"), ""); 00619 REG_KEY("TV Playback", "NEXTPIPWINDOW", QT_TRANSLATE_NOOP("MythControls", 00620 "Toggle active PIP/PBP window"), "B"); 00621 REG_KEY("TV Playback", "SWAPPIP", QT_TRANSLATE_NOOP("MythControls", 00622 "Swap PBP/PIP Windows"), "N"); 00623 REG_KEY("TV Playback", "TOGGLEPIPSTATE", QT_TRANSLATE_NOOP("MythControls", 00624 "Change PxP view"), ""); 00625 REG_KEY("TV Playback", "TOGGLEASPECT", QT_TRANSLATE_NOOP("MythControls", 00626 "Toggle the video aspect ratio"), "Ctrl+W"); 00627 REG_KEY("TV Playback", "TOGGLEFILL", QT_TRANSLATE_NOOP("MythControls", 00628 "Next Preconfigured Zoom mode"), "W"); 00629 REG_KEY("TV Playback", ACTION_TOGGLESUBS, QT_TRANSLATE_NOOP("MythControls", 00630 "Toggle any captions"), "T"); 00631 REG_KEY("TV Playback", ACTION_ENABLESUBS, QT_TRANSLATE_NOOP("MythControls", 00632 "Enable any captions"), ""); 00633 REG_KEY("TV Playback", ACTION_DISABLESUBS, QT_TRANSLATE_NOOP("MythControls", 00634 "Disable any captions"), ""); 00635 REG_KEY("TV Playback", "TOGGLETTC", QT_TRANSLATE_NOOP("MythControls", 00636 "Toggle Teletext Captions"),""); 00637 REG_KEY("TV Playback", "TOGGLESUBTITLE", QT_TRANSLATE_NOOP("MythControls", 00638 "Toggle Subtitles"), ""); 00639 REG_KEY("TV Playback", "TOGGLECC608", QT_TRANSLATE_NOOP("MythControls", 00640 "Toggle VBI CC"), ""); 00641 REG_KEY("TV Playback", "TOGGLECC708", QT_TRANSLATE_NOOP("MythControls", 00642 "Toggle ATSC CC"), ""); 00643 REG_KEY("TV Playback", "TOGGLETTM", QT_TRANSLATE_NOOP("MythControls", 00644 "Toggle Teletext Menu"), ""); 00645 REG_KEY("TV Playback", ACTION_TOGGLEEXTTEXT, QT_TRANSLATE_NOOP("MythControls", 00646 "Toggle External Subtitles"), ""); 00647 REG_KEY("TV Playback", ACTION_ENABLEEXTTEXT, QT_TRANSLATE_NOOP("MythControls", 00648 "Enable External Subtitles"), ""); 00649 REG_KEY("TV Playback", ACTION_DISABLEEXTTEXT, QT_TRANSLATE_NOOP("MythControls", 00650 "Disable External Subtitles"), ""); 00651 REG_KEY("TV Playback", "TOGGLERAWTEXT", QT_TRANSLATE_NOOP("MythControls", 00652 "Toggle Text Subtitles"), ""); 00653 00654 REG_KEY("TV Playback", "SELECTAUDIO_0", QT_TRANSLATE_NOOP("MythControls", 00655 "Play audio track 1"), ""); 00656 REG_KEY("TV Playback", "SELECTAUDIO_1", QT_TRANSLATE_NOOP("MythControls", 00657 "Play audio track 2"), ""); 00658 REG_KEY("TV Playback", "SELECTSUBTITLE_0",QT_TRANSLATE_NOOP("MythControls", 00659 "Display subtitle 1"), ""); 00660 REG_KEY("TV Playback", "SELECTSUBTITLE_1",QT_TRANSLATE_NOOP("MythControls", 00661 "Display subtitle 2"), ""); 00662 REG_KEY("TV Playback", "SELECTRAWTEXT_0",QT_TRANSLATE_NOOP("MythControls", 00663 "Display Text Subtitle 1"), ""); 00664 REG_KEY("TV Playback", "SELECTCC608_0", QT_TRANSLATE_NOOP("MythControls", 00665 "Display VBI CC1"), ""); 00666 REG_KEY("TV Playback", "SELECTCC608_1", QT_TRANSLATE_NOOP("MythControls", 00667 "Display VBI CC2"), ""); 00668 REG_KEY("TV Playback", "SELECTCC608_2", QT_TRANSLATE_NOOP("MythControls", 00669 "Display VBI CC3"), ""); 00670 REG_KEY("TV Playback", "SELECTCC608_3", QT_TRANSLATE_NOOP("MythControls", 00671 "Display VBI CC4"), ""); 00672 REG_KEY("TV Playback", "SELECTCC708_0", QT_TRANSLATE_NOOP("MythControls", 00673 "Display ATSC CC1"), ""); 00674 REG_KEY("TV Playback", "SELECTCC708_1", QT_TRANSLATE_NOOP("MythControls", 00675 "Display ATSC CC2"), ""); 00676 REG_KEY("TV Playback", "SELECTCC708_2", QT_TRANSLATE_NOOP("MythControls", 00677 "Display ATSC CC3"), ""); 00678 REG_KEY("TV Playback", "SELECTCC708_3", QT_TRANSLATE_NOOP("MythControls", 00679 "Display ATSC CC4"), ""); 00680 REG_KEY("TV Playback", ACTION_ENABLEFORCEDSUBS, QT_TRANSLATE_NOOP("MythControls", 00681 "Enable Forced Subtitles"), ""); 00682 REG_KEY("TV Playback", ACTION_DISABLEFORCEDSUBS, QT_TRANSLATE_NOOP("MythControls", 00683 "Disable Forced Subtitles"), ""); 00684 00685 REG_KEY("TV Playback", "NEXTAUDIO", QT_TRANSLATE_NOOP("MythControls", 00686 "Next audio track"), "+"); 00687 REG_KEY("TV Playback", "PREVAUDIO", QT_TRANSLATE_NOOP("MythControls", 00688 "Previous audio track"), "-"); 00689 REG_KEY("TV Playback", "NEXTSUBTITLE", QT_TRANSLATE_NOOP("MythControls", 00690 "Next subtitle track"), ""); 00691 REG_KEY("TV Playback", "PREVSUBTITLE", QT_TRANSLATE_NOOP("MythControls", 00692 "Previous subtitle track"), ""); 00693 REG_KEY("TV Playback", "NEXTRAWTEXT", QT_TRANSLATE_NOOP("MythControls", 00694 "Next Text track"), ""); 00695 REG_KEY("TV Playback", "PREVRAWTEXT", QT_TRANSLATE_NOOP("MythControls", 00696 "Previous Text track"), ""); 00697 REG_KEY("TV Playback", "NEXTCC608", QT_TRANSLATE_NOOP("MythControls", 00698 "Next VBI CC track"), ""); 00699 REG_KEY("TV Playback", "PREVCC608", QT_TRANSLATE_NOOP("MythControls", 00700 "Previous VBI CC track"), ""); 00701 REG_KEY("TV Playback", "NEXTCC708", QT_TRANSLATE_NOOP("MythControls", 00702 "Next ATSC CC track"), ""); 00703 REG_KEY("TV Playback", "PREVCC708", QT_TRANSLATE_NOOP("MythControls", 00704 "Previous ATSC CC track"), ""); 00705 REG_KEY("TV Playback", "NEXTCC", QT_TRANSLATE_NOOP("MythControls", 00706 "Next of any captions"), ""); 00707 00708 REG_KEY("TV Playback", "NEXTSCAN", QT_TRANSLATE_NOOP("MythControls", 00709 "Next video scan overidemode"), ""); 00710 REG_KEY("TV Playback", "QUEUETRANSCODE", QT_TRANSLATE_NOOP("MythControls", 00711 "Queue the current recording for transcoding"), "X"); 00712 REG_KEY("TV Playback", "SPEEDINC", QT_TRANSLATE_NOOP("MythControls", 00713 "Increase the playback speed"), "U"); 00714 REG_KEY("TV Playback", "SPEEDDEC", QT_TRANSLATE_NOOP("MythControls", 00715 "Decrease the playback speed"), "J"); 00716 REG_KEY("TV Playback", "ADJUSTSTRETCH", QT_TRANSLATE_NOOP("MythControls", 00717 "Turn on time stretch control"), "A"); 00718 REG_KEY("TV Playback", "STRETCHINC", QT_TRANSLATE_NOOP("MythControls", 00719 "Increase time stretch speed"), ""); 00720 REG_KEY("TV Playback", "STRETCHDEC", QT_TRANSLATE_NOOP("MythControls", 00721 "Decrease time stretch speed"), ""); 00722 REG_KEY("TV Playback", "TOGGLESTRETCH", QT_TRANSLATE_NOOP("MythControls", 00723 "Toggle time stretch speed"), ""); 00724 REG_KEY("TV Playback", ACTION_TOGGELAUDIOSYNC, 00725 QT_TRANSLATE_NOOP("MythControls", 00726 "Turn on audio sync adjustment controls"), ""); 00727 REG_KEY("TV Playback", ACTION_SETAUDIOSYNC, 00728 QT_TRANSLATE_NOOP("MythControls", 00729 "Set the audio sync adjustment"), ""); 00730 REG_KEY("TV Playback", "TOGGLEPICCONTROLS", 00731 QT_TRANSLATE_NOOP("MythControls", "Playback picture adjustments"), 00732 "F"); 00733 REG_KEY("TV Playback", ACTION_TOGGLENIGHTMODE, 00734 QT_TRANSLATE_NOOP("MythControls", "Toggle night mode"), "Ctrl+F"); 00735 REG_KEY("TV Playback", ACTION_SETBRIGHTNESS, 00736 QT_TRANSLATE_NOOP("MythControls", "Set the picture brightness"), ""); 00737 REG_KEY("TV Playback", ACTION_SETCONTRAST, 00738 QT_TRANSLATE_NOOP("MythControls", "Set the picture contrast"), ""); 00739 REG_KEY("TV Playback", ACTION_SETCOLOUR, 00740 QT_TRANSLATE_NOOP("MythControls", "Set the picture color"), ""); 00741 REG_KEY("TV Playback", ACTION_SETHUE, 00742 QT_TRANSLATE_NOOP("MythControls", "Set the picture hue"), ""); 00743 REG_KEY("TV Playback", ACTION_TOGGLESTUDIOLEVELS, 00744 QT_TRANSLATE_NOOP("MythControls", "Playback picture adjustments"), 00745 ""); 00746 REG_KEY("TV Playback", ACTION_TOGGLECHANCONTROLS, 00747 QT_TRANSLATE_NOOP("MythControls", "Recording picture adjustments " 00748 "for this channel"), "Ctrl+G"); 00749 REG_KEY("TV Playback", ACTION_TOGGLERECCONTROLS, 00750 QT_TRANSLATE_NOOP("MythControls", "Recording picture adjustments " 00751 "for this recorder"), "G"); 00752 REG_KEY("TV Playback", "CYCLECOMMSKIPMODE", 00753 QT_TRANSLATE_NOOP("MythControls", "Cycle Commercial Skip mode"), 00754 ""); 00755 REG_KEY("TV Playback", ACTION_GUIDE, QT_TRANSLATE_NOOP("MythControls", 00756 "Show the Program Guide"), "S"); 00757 REG_KEY("TV Playback", ACTION_FINDER, QT_TRANSLATE_NOOP("MythControls", 00758 "Show the Program Finder"), "#"); 00759 REG_KEY("TV Playback", ACTION_TOGGLESLEEP, QT_TRANSLATE_NOOP("MythControls", 00760 "Toggle the Sleep Timer"), "F8"); 00761 REG_KEY("TV Playback", ACTION_PLAY, QT_TRANSLATE_NOOP("MythControls", "Play"), 00762 "Ctrl+P"); 00763 REG_KEY("TV Playback", ACTION_JUMPPREV, QT_TRANSLATE_NOOP("MythControls", 00764 "Jump to previously played recording"), ""); 00765 REG_KEY("TV Playback", ACTION_JUMPREC, QT_TRANSLATE_NOOP("MythControls", 00766 "Display menu of recorded programs to jump to"), ""); 00767 REG_KEY("TV Playback", ACTION_VIEWSCHEDULED, QT_TRANSLATE_NOOP("MythControls", 00768 "Display scheduled recording list"), ""); 00769 REG_KEY("TV Playback", ACTION_SIGNALMON, QT_TRANSLATE_NOOP("MythControls", 00770 "Monitor Signal Quality"), "Alt+F7"); 00771 REG_KEY("TV Playback", ACTION_JUMPTODVDROOTMENU, 00772 QT_TRANSLATE_NOOP("MythControls", "Jump to the DVD Root Menu"), ""); 00773 REG_KEY("TV Playback", ACTION_JUMPTOPOPUPMENU, 00774 QT_TRANSLATE_NOOP("MythControls", "Jump to the Popup Menu"), ""); 00775 REG_KEY("TV Playback", ACTION_JUMPTODVDCHAPTERMENU, 00776 QT_TRANSLATE_NOOP("MythControls", "Jump to the DVD Chapter Menu"), ""); 00777 REG_KEY("TV Playback", ACTION_JUMPTODVDTITLEMENU, 00778 QT_TRANSLATE_NOOP("MythControls", "Jump to the DVD Title Menu"), ""); 00779 REG_KEY("TV Playback", ACTION_EXITSHOWNOPROMPTS, 00780 QT_TRANSLATE_NOOP("MythControls", "Exit Show without any prompts"), 00781 ""); 00782 REG_KEY("TV Playback", ACTION_JUMPCHAPTER, QT_TRANSLATE_NOOP("MythControls", 00783 "Jump to a chapter"), ""); 00784 REG_KEY("TV Playback", ACTION_SWITCHTITLE, QT_TRANSLATE_NOOP("MythControls", 00785 "Switch title"), ""); 00786 REG_KEY("TV Playback", ACTION_SWITCHANGLE, QT_TRANSLATE_NOOP("MythControls", 00787 "Switch angle"), ""); 00788 00789 /* Interactive Television keys */ 00790 REG_KEY("TV Playback", ACTION_MENURED, QT_TRANSLATE_NOOP("MythControls", 00791 "Menu Red"), "F2"); 00792 REG_KEY("TV Playback", ACTION_MENUGREEN, QT_TRANSLATE_NOOP("MythControls", 00793 "Menu Green"), "F3"); 00794 REG_KEY("TV Playback", ACTION_MENUYELLOW, QT_TRANSLATE_NOOP("MythControls", 00795 "Menu Yellow"), "F4"); 00796 REG_KEY("TV Playback", ACTION_MENUBLUE, QT_TRANSLATE_NOOP("MythControls", 00797 "Menu Blue"), "F5"); 00798 REG_KEY("TV Playback", ACTION_TEXTEXIT, QT_TRANSLATE_NOOP("MythControls", 00799 "Menu Exit"), "F6"); 00800 REG_KEY("TV Playback", ACTION_MENUTEXT, QT_TRANSLATE_NOOP("MythControls", 00801 "Menu Text"), "F7"); 00802 REG_KEY("TV Playback", ACTION_MENUEPG, QT_TRANSLATE_NOOP("MythControls", 00803 "Menu EPG"), "F12"); 00804 00805 /* Editing keys */ 00806 REG_KEY("TV Editing", ACTION_CLEARMAP, QT_TRANSLATE_NOOP("MythControls", 00807 "Clear editing cut points"), "C,Q,Home"); 00808 REG_KEY("TV Editing", ACTION_INVERTMAP, QT_TRANSLATE_NOOP("MythControls", 00809 "Invert Begin/End cut points"),"I"); 00810 REG_KEY("TV Editing", ACTION_SAVEMAP, QT_TRANSLATE_NOOP("MythControls", 00811 "Save cuts"),""); 00812 REG_KEY("TV Editing", ACTION_LOADCOMMSKIP,QT_TRANSLATE_NOOP("MythControls", 00813 "Load cuts from detected commercials"), "Z,End"); 00814 REG_KEY("TV Editing", ACTION_NEXTCUT, QT_TRANSLATE_NOOP("MythControls", 00815 "Jump to the next cut point"), "PgDown"); 00816 REG_KEY("TV Editing", ACTION_PREVCUT, QT_TRANSLATE_NOOP("MythControls", 00817 "Jump to the previous cut point"), "PgUp"); 00818 REG_KEY("TV Editing", ACTION_BIGJUMPREW, QT_TRANSLATE_NOOP("MythControls", 00819 "Jump back 10x the normal amount"), ",,<"); 00820 REG_KEY("TV Editing", ACTION_BIGJUMPFWD, QT_TRANSLATE_NOOP("MythControls", 00821 "Jump forward 10x the normal amount"), ">,."); 00822 00823 /* Teletext keys */ 00824 REG_KEY("Teletext Menu", ACTION_NEXTPAGE, QT_TRANSLATE_NOOP("MythControls", 00825 "Next Page"), "Down"); 00826 REG_KEY("Teletext Menu", ACTION_PREVPAGE, QT_TRANSLATE_NOOP("MythControls", 00827 "Previous Page"), "Up"); 00828 REG_KEY("Teletext Menu", ACTION_NEXTSUBPAGE, QT_TRANSLATE_NOOP("MythControls", 00829 "Next Subpage"), "Right"); 00830 REG_KEY("Teletext Menu", ACTION_PREVSUBPAGE, QT_TRANSLATE_NOOP("MythControls", 00831 "Previous Subpage"), "Left"); 00832 REG_KEY("Teletext Menu", ACTION_TOGGLETT, QT_TRANSLATE_NOOP("MythControls", 00833 "Toggle Teletext"), "T"); 00834 REG_KEY("Teletext Menu", ACTION_MENURED, QT_TRANSLATE_NOOP("MythControls", 00835 "Menu Red"), "F2"); 00836 REG_KEY("Teletext Menu", ACTION_MENUGREEN, QT_TRANSLATE_NOOP("MythControls", 00837 "Menu Green"), "F3"); 00838 REG_KEY("Teletext Menu", ACTION_MENUYELLOW, QT_TRANSLATE_NOOP("MythControls", 00839 "Menu Yellow"), "F4"); 00840 REG_KEY("Teletext Menu", ACTION_MENUBLUE, QT_TRANSLATE_NOOP("MythControls", 00841 "Menu Blue"), "F5"); 00842 REG_KEY("Teletext Menu", ACTION_MENUWHITE, QT_TRANSLATE_NOOP("MythControls", 00843 "Menu White"), "F6"); 00844 REG_KEY("Teletext Menu", ACTION_TOGGLEBACKGROUND, 00845 QT_TRANSLATE_NOOP("MythControls", "Toggle Background"), "F7"); 00846 REG_KEY("Teletext Menu", ACTION_REVEAL, QT_TRANSLATE_NOOP("MythControls", 00847 "Reveal hidden Text"), "F8"); 00848 00849 /* Visualisations */ 00850 REG_KEY("TV Playback", ACTION_TOGGLEVISUALISATION, 00851 QT_TRANSLATE_NOOP("MythControls", "Toggle audio visualisation"), ""); 00852 00853 /* OSD playback information screen */ 00854 REG_KEY("TV Playback", ACTION_TOGGLEOSDDEBUG, 00855 QT_TRANSLATE_NOOP("MythControls", "Toggle OSD playback information"), ""); 00856 00857 /* 3D/Frame compatible/Stereoscopic TV */ 00858 REG_KEY("TV Playback", ACTION_3DNONE, 00859 QT_TRANSLATE_NOOP("MythControls", "No 3D"), ""); 00860 REG_KEY("TV Playback", ACTION_3DSIDEBYSIDE, 00861 QT_TRANSLATE_NOOP("MythControls", "3D Side by Side"), ""); 00862 REG_KEY("TV Playback", ACTION_3DSIDEBYSIDEDISCARD, 00863 QT_TRANSLATE_NOOP("MythControls", "Discard 3D Side by Side"), ""); 00864 REG_KEY("TV Playback", ACTION_3DTOPANDBOTTOM, 00865 QT_TRANSLATE_NOOP("MythControls", "3D Top and Bottom"), ""); 00866 REG_KEY("TV Playback", ACTION_3DTOPANDBOTTOMDISCARD, 00867 QT_TRANSLATE_NOOP("MythControls", "Discard 3D Top and Bottom"), ""); 00868 00869 /* 00870 keys already used: 00871 00872 Global: I M 0123456789 00873 Playback: ABCDEFGH JK NOPQRSTUVWXYZ 00874 Frontend: CD OP R U XY 01 3 7 9 00875 Editing: C E I Q Z 00876 Teletext: T 00877 00878 Playback: <>,.?/|[]{}\+-*#^ 00879 Frontend: <>,.?/ 00880 Editing: <>,. 00881 00882 Global: PgDown, PgUp, Right, Left, Home, End, Up, Down, 00883 Playback: PgDown, PgUp, Right, Left, Home, End, Up, Down, Backspace, 00884 Frontend: Right, Left, Home, End 00885 Editing: PgDown, PgUp, Home, End 00886 Teletext: Right, Left, Up, Down, 00887 00888 Global: Return, Enter, Space, Esc 00889 00890 Global: F1, 00891 Playback: F7,F8,F9,F10,F11 00892 Teletext F2,F3,F4,F5,F6,F7,F8 00893 ITV F2,F3,F4,F5,F6,F7,F12 00894 00895 Playback: Ctrl-B,Ctrl-G,Ctrl-Y,Ctrl-U 00896 */ 00897 } 00898 00899 void TV::ReloadKeys(void) 00900 { 00901 MythMainWindow *mainWindow = GetMythMainWindow(); 00902 mainWindow->ClearKeyContext("TV Frontend"); 00903 mainWindow->ClearKeyContext("TV Playback"); 00904 mainWindow->ClearKeyContext("TV Editing"); 00905 mainWindow->ClearKeyContext("Teletext Menu"); 00906 InitKeys(); 00907 } 00908 00912 TV::TV(void) 00913 : // Configuration variables from database 00914 baseFilters(""), 00915 db_channel_format("<num> <sign>"), 00916 db_idle_timeout(0), 00917 db_playback_exit_prompt(0), db_autoexpire_default(0), 00918 db_auto_set_watched(false), db_end_of_rec_exit_prompt(false), 00919 db_jump_prefer_osd(true), db_use_gui_size_for_tv(false), 00920 db_start_in_guide(false), db_toggle_bookmark(false), 00921 db_run_jobs_on_remote(false), db_continue_embedded(false), 00922 db_use_fixed_size(true), db_browse_always(false), 00923 db_browse_all_tuners(false), 00924 db_use_channel_groups(false), db_remember_last_channel_group(false), 00925 00926 tryUnflaggedSkip(false), 00927 smartForward(false), 00928 ff_rew_repos(1.0f), ff_rew_reverse(false), 00929 jumped_back(false), 00930 vbimode(VBIMode::None), 00931 // State variables 00932 switchToInputId(0), 00933 wantsToQuit(true), 00934 stretchAdjustment(false), 00935 audiosyncAdjustment(false), 00936 subtitleZoomAdjustment(false), 00937 editmode(false), zoomMode(false), 00938 sigMonMode(false), 00939 endOfRecording(false), 00940 requestDelete(false), allowRerecord(false), 00941 doSmartForward(false), 00942 queuedTranscode(false), 00943 adjustingPicture(kAdjustingPicture_None), 00944 adjustingPictureAttribute(kPictureAttribute_None), 00945 askAllowLock(QMutex::Recursive), 00946 // Channel Editing 00947 chanEditMapLock(QMutex::Recursive), 00948 ddMapSourceId(0), ddMapLoader(new DDLoader(this)), 00949 // Sleep Timer 00950 sleep_index(0), sleepTimerId(0), sleepDialogTimerId(0), 00951 // Idle Timer 00952 idleTimerId(0), idleDialogTimerId(0), 00953 // CC/Teletext input state variables 00954 ccInputMode(false), 00955 // Arbritary seek input state variables 00956 asInputMode(false), 00957 // Channel changing state variables 00958 queuedChanNum(""), 00959 lockTimerOn(false), 00960 // channel browsing 00961 browsehelper(NULL), 00962 // Program Info for currently playing video 00963 lastProgram(NULL), 00964 inPlaylist(false), underNetworkControl(false), 00965 // Jump to program stuff 00966 jumpToProgramPIPState(kPIPOff), 00967 jumpToProgram(false), 00968 // Video Player currently receiving UI input 00969 playerActive(-1), 00970 noHardwareDecoders(false), 00971 //Recorder switching info 00972 switchToRec(NULL), 00973 // LCD Info 00974 lcdTitle(""), lcdSubtitle(""), lcdCallsign(""), 00975 // Window info (GUI is optional, transcoding, preview img, etc) 00976 myWindow(NULL), weDisabledGUI(false), 00977 disableDrawUnusedRects(false), 00978 isEmbedded(false), ignoreKeyPresses(false), 00979 // Timers 00980 lcdTimerId(0), lcdVolumeTimerId(0), 00981 networkControlTimerId(0), jumpMenuTimerId(0), 00982 pipChangeTimerId(0), 00983 switchToInputTimerId(0), ccInputTimerId(0), 00984 asInputTimerId(0), queueInputTimerId(0), 00985 browseTimerId(0), updateOSDPosTimerId(0), 00986 updateOSDDebugTimerId(0), 00987 endOfPlaybackTimerId(0), embedCheckTimerId(0), 00988 endOfRecPromptTimerId(0), videoExitDialogTimerId(0), 00989 pseudoChangeChanTimerId(0), speedChangeTimerId(0), 00990 errorRecoveryTimerId(0), exitPlayerTimerId(0) 00991 { 00992 LOG(VB_GENERAL, LOG_INFO, LOC + "Creating TV object"); 00993 ctorTime.start(); 00994 00995 setObjectName("TV"); 00996 keyRepeatTimer.start(); 00997 00998 sleep_times.push_back(SleepTimerInfo(QObject::tr("Off"), 0)); 00999 sleep_times.push_back(SleepTimerInfo(QObject::tr("30m"), 30*60)); 01000 sleep_times.push_back(SleepTimerInfo(QObject::tr("1h"), 60*60)); 01001 sleep_times.push_back(SleepTimerInfo(QObject::tr("1h30m"), 90*60)); 01002 sleep_times.push_back(SleepTimerInfo(QObject::tr("2h"), 120*60)); 01003 01004 playerLock.lockForWrite(); 01005 player.push_back(new PlayerContext(kPlayerInUseID)); 01006 playerActive = 0; 01007 playerLock.unlock(); 01008 01009 InitFromDB(); 01010 01011 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Finished creating TV object"); 01012 } 01013 01014 void TV::InitFromDB(void) 01015 { 01016 QMap<QString,QString> kv; 01017 kv["LiveTVIdleTimeout"] = "0"; 01018 kv["BrowseMaxForward"] = "240"; 01019 kv["PlaybackExitPrompt"] = "0"; 01020 kv["AutomaticSetWatched"] = "0"; 01021 kv["EndOfRecordingExitPrompt"] = "0"; 01022 kv["JumpToProgramOSD"] = "1"; 01023 kv["GuiSizeForTV"] = "0"; 01024 kv["WatchTVGuide"] = "0"; 01025 kv["AltClearSavedPosition"] = "1"; 01026 kv["JobsRunOnRecordHost"] = "0"; 01027 kv["ContinueEmbeddedTVPlay"] = "0"; 01028 kv["UseFixedWindowSize"] = "1"; 01029 kv["PersistentBrowseMode"] = "0"; 01030 kv["BrowseAllTuners"] = "0"; 01031 kv["ChannelOrdering"] = "channum"; 01032 01033 kv["CustomFilters"] = ""; 01034 kv["ChannelFormat"] = "<num> <sign>"; 01035 kv["TimeFormat"] = "h:mm AP"; 01036 kv["ShortDateFormat"] = "M/d"; 01037 01038 kv["TryUnflaggedSkip"] = "0"; 01039 01040 kv["ChannelGroupDefault"] = "-1"; 01041 kv["BrowseChannelGroup"] = "0"; 01042 kv["SmartForward"] = "0"; 01043 kv["FFRewReposTime"] = "100"; 01044 kv["FFRewReverse"] = "1"; 01045 01046 kv["BrowseChannelGroup"] = "0"; 01047 kv["ChannelGroupDefault"] = "-1"; 01048 kv["ChannelGroupRememberLast"] = "0"; 01049 01050 kv["VbiFormat"] = ""; 01051 kv["DecodeVBIFormat"] = ""; 01052 01053 int ff_rew_def[8] = { 3, 5, 10, 20, 30, 60, 120, 180 }; 01054 for (uint i = 0; i < sizeof(ff_rew_def)/sizeof(ff_rew_def[0]); i++) 01055 kv[QString("FFRewSpeed%1").arg(i)] = QString::number(ff_rew_def[i]); 01056 01057 MythDB::getMythDB()->GetSettings(kv); 01058 01059 QString db_time_format; 01060 QString db_short_date_format; 01061 QString db_channel_ordering; 01062 uint db_browse_max_forward; 01063 01064 // convert from minutes to ms. 01065 db_idle_timeout = kv["LiveTVIdleTimeout"].toInt() * 60 * 1000; 01066 db_browse_max_forward = kv["BrowseMaxForward"].toInt() * 60; 01067 db_playback_exit_prompt= kv["PlaybackExitPrompt"].toInt(); 01068 db_auto_set_watched = kv["AutomaticSetWatched"].toInt(); 01069 db_end_of_rec_exit_prompt = kv["EndOfRecordingExitPrompt"].toInt(); 01070 db_jump_prefer_osd = kv["JumpToProgramOSD"].toInt(); 01071 db_use_gui_size_for_tv = kv["GuiSizeForTV"].toInt(); 01072 db_start_in_guide = kv["WatchTVGuide"].toInt(); 01073 db_toggle_bookmark = kv["AltClearSavedPosition"].toInt(); 01074 db_run_jobs_on_remote = kv["JobsRunOnRecordHost"].toInt(); 01075 db_continue_embedded = kv["ContinueEmbeddedTVPlay"].toInt(); 01076 db_use_fixed_size = kv["UseFixedWindowSize"].toInt(); 01077 db_browse_always = kv["PersistentBrowseMode"].toInt(); 01078 db_browse_all_tuners = kv["BrowseAllTuners"].toInt(); 01079 db_channel_ordering = kv["ChannelOrdering"]; 01080 baseFilters += kv["CustomFilters"]; 01081 db_channel_format = kv["ChannelFormat"]; 01082 db_time_format = kv["TimeFormat"]; 01083 db_short_date_format = kv["ShortDateFormat"]; 01084 tryUnflaggedSkip = kv["TryUnflaggedSkip"].toInt(); 01085 smartForward = kv["SmartForward"].toInt(); 01086 ff_rew_repos = kv["FFRewReposTime"].toFloat() * 0.01f; 01087 ff_rew_reverse = kv["FFRewReverse"].toInt(); 01088 01089 db_use_channel_groups = kv["BrowseChannelGroup"].toInt(); 01090 db_remember_last_channel_group = kv["ChannelGroupRememberLast"].toInt(); 01091 channelGroupId = kv["ChannelGroupDefault"].toInt(); 01092 01093 QString beVBI = kv["VbiFormat"]; 01094 QString feVBI = kv["DecodeVBIFormat"]; 01095 01096 RecordingRule record; 01097 record.LoadTemplate("Default"); 01098 db_autoexpire_default = record.m_autoExpire; 01099 01100 if (db_use_channel_groups) 01101 { 01102 db_channel_groups = ChannelGroup::GetChannelGroups(); 01103 if (channelGroupId > -1) 01104 { 01105 channelGroupChannelList = ChannelUtil::GetChannels( 01106 0, true, "channum, callsign", channelGroupId); 01107 ChannelUtil::SortChannels( 01108 channelGroupChannelList, "channum", true); 01109 } 01110 } 01111 01112 for (uint i = 0; i < sizeof(ff_rew_def)/sizeof(ff_rew_def[0]); i++) 01113 ff_rew_speeds.push_back(kv[QString("FFRewSpeed%1").arg(i)].toInt()); 01114 01115 // process it.. 01116 browsehelper = new TVBrowseHelper( 01117 this, 01118 db_time_format, db_short_date_format, 01119 db_browse_max_forward, db_browse_all_tuners, 01120 db_use_channel_groups, db_channel_ordering); 01121 01122 vbimode = VBIMode::Parse(!feVBI.isEmpty() ? feVBI : beVBI); 01123 01124 gCoreContext->addListener(this); 01125 01126 QMutexLocker lock(&initFromDBLock); 01127 initFromDBDone = true; 01128 initFromDBWait.wakeAll(); 01129 } 01130 01137 bool TV::Init(bool createWindow) 01138 { 01139 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Init -- begin"); 01140 01141 if (createWindow) 01142 { 01143 bool fullscreen = !gCoreContext->GetNumSetting("GuiSizeForTV", 0); 01144 bool switchMode = gCoreContext->GetNumSetting("UseVideoModes", 0); 01145 01146 saved_gui_bounds = QRect(GetMythMainWindow()->geometry().topLeft(), 01147 GetMythMainWindow()->size()); 01148 01149 // adjust for window manager wierdness. 01150 { 01151 int xbase, width, ybase, height; 01152 float wmult, hmult; 01153 GetMythUI()->GetScreenSettings(xbase, width, wmult, 01154 ybase, height, hmult); 01155 if ((abs(saved_gui_bounds.x()-xbase) < 3) && 01156 (abs(saved_gui_bounds.y()-ybase) < 3)) 01157 { 01158 saved_gui_bounds = QRect(QPoint(xbase, ybase), 01159 GetMythMainWindow()->size()); 01160 } 01161 } 01162 01163 // if width && height are zero users expect fullscreen playback 01164 if (!fullscreen) 01165 { 01166 int gui_width = 0, gui_height = 0; 01167 gCoreContext->GetResolutionSetting("Gui", gui_width, gui_height); 01168 fullscreen |= (0 == gui_width && 0 == gui_height); 01169 } 01170 01171 player_bounds = saved_gui_bounds; 01172 if (fullscreen) 01173 { 01174 int xbase, width, ybase, height; 01175 GetMythUI()->GetScreenBounds(xbase, ybase, width, height); 01176 player_bounds = QRect(xbase, ybase, width, height); 01177 } 01178 01179 // main window sizing 01180 int maxWidth = 1920, maxHeight = 1440; 01181 if (switchMode) 01182 { 01183 DisplayRes *display_res = DisplayRes::GetDisplayRes(); 01184 if(display_res) 01185 { 01186 // The very first Resize needs to be the maximum possible 01187 // desired res, because X will mask off anything outside 01188 // the initial dimensions 01189 maxWidth = display_res->GetMaxWidth(); 01190 maxHeight = display_res->GetMaxHeight(); 01191 01192 // bit of a hack, but it's ok if the window is too 01193 // big in fullscreen mode 01194 if (fullscreen) 01195 { 01196 player_bounds.setSize(QSize(maxWidth, maxHeight)); 01197 01198 // resize possibly avoids a bug on some systems 01199 GetMythMainWindow()->setGeometry(player_bounds); 01200 GetMythMainWindow()->ResizePainterWindow(player_bounds.size()); 01201 } 01202 } 01203 } 01204 01205 // player window sizing 01206 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack(); 01207 01208 myWindow = new TvPlayWindow(mainStack, "Playback"); 01209 01210 if (myWindow->Create()) 01211 { 01212 mainStack->AddScreen(myWindow, false); 01213 LOG(VB_GENERAL, LOG_INFO, LOC + "Created TvPlayWindow."); 01214 } 01215 else 01216 { 01217 delete myWindow; 01218 myWindow = NULL; 01219 } 01220 01221 MythMainWindow *mainWindow = GetMythMainWindow(); 01222 if (mainWindow->GetPaintWindow()) 01223 mainWindow->GetPaintWindow()->update(); 01224 mainWindow->installEventFilter(this); 01225 qApp->processEvents(); 01226 } 01227 01228 { 01229 QMutexLocker locker(&initFromDBLock); 01230 while (!initFromDBDone) 01231 { 01232 qApp->processEvents(); 01233 initFromDBWait.wait(&initFromDBLock, 50); 01234 } 01235 } 01236 01237 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 01238 mctx->ff_rew_state = 0; 01239 mctx->ff_rew_index = kInitFFRWSpeed; 01240 mctx->ff_rew_speed = 0; 01241 mctx->ts_normal = 1.0f; 01242 ReturnPlayerLock(mctx); 01243 01244 sleep_index = 0; 01245 01246 SetUpdateOSDPosition(false); 01247 01248 const PlayerContext *ctx = GetPlayerReadLock(0, __FILE__, __LINE__); 01249 ClearInputQueues(ctx, false); 01250 ReturnPlayerLock(ctx); 01251 01252 switchToRec = NULL; 01253 SetExitPlayer(false, false); 01254 01255 errorRecoveryTimerId = StartTimer(kErrorRecoveryCheckFrequency, __LINE__); 01256 lcdTimerId = StartTimer(1, __LINE__); 01257 speedChangeTimerId = StartTimer(kSpeedChangeCheckFrequency, __LINE__); 01258 01259 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Init -- end"); 01260 return true; 01261 } 01262 01263 TV::~TV(void) 01264 { 01265 LOG(VB_PLAYBACK, LOG_INFO, "TV::~TV() -- begin"); 01266 01267 if (browsehelper) 01268 browsehelper->Stop(); 01269 01270 gCoreContext->removeListener(this); 01271 01272 if (GetMythMainWindow() && weDisabledGUI) 01273 GetMythMainWindow()->PopDrawDisabled(); 01274 01275 if (myWindow) 01276 { 01277 myWindow->Close(); 01278 myWindow = NULL; 01279 } 01280 01281 LOG(VB_PLAYBACK, LOG_INFO, "TV::~TV() -- lock"); 01282 01283 // restore window to gui size and position 01284 MythMainWindow* mwnd = GetMythMainWindow(); 01285 mwnd->setGeometry(saved_gui_bounds); 01286 mwnd->setFixedSize(saved_gui_bounds.size()); 01287 mwnd->ResizePainterWindow(saved_gui_bounds.size()); 01288 mwnd->show(); 01289 if (!db_use_gui_size_for_tv) 01290 mwnd->move(saved_gui_bounds.topLeft()); 01291 01292 if (lastProgram) 01293 delete lastProgram; 01294 01295 if (LCD *lcd = LCD::Get()) 01296 { 01297 lcd->setFunctionLEDs(FUNC_TV, false); 01298 lcd->setFunctionLEDs(FUNC_MOVIE, false); 01299 lcd->switchToTime(); 01300 } 01301 01302 if (ddMapLoader) 01303 { 01304 ddMapLoader->wait(); 01305 01306 if (ddMapSourceId) 01307 { 01308 ddMapLoader->SetParent(NULL); 01309 ddMapLoader->SetSourceID(ddMapSourceId); 01310 ddMapLoader->setAutoDelete(true); 01311 MThreadPool::globalInstance()->start(ddMapLoader, "DDLoadMapPost"); 01312 } 01313 else 01314 { 01315 delete ddMapLoader; 01316 } 01317 01318 ddMapSourceId = 0; 01319 ddMapLoader = NULL; 01320 } 01321 01322 if (browsehelper) 01323 { 01324 delete browsehelper; 01325 browsehelper = NULL; 01326 } 01327 01328 PlayerContext *mctx = GetPlayerWriteLock(0, __FILE__, __LINE__); 01329 while (!player.empty()) 01330 { 01331 delete player.back(); 01332 player.pop_back(); 01333 } 01334 ReturnPlayerLock(mctx); 01335 01336 if (browsehelper) 01337 { 01338 delete browsehelper; 01339 browsehelper = NULL; 01340 } 01341 01342 LOG(VB_PLAYBACK, LOG_INFO, "TV::~TV() -- end"); 01343 } 01344 01348 void TV::PlaybackLoop(void) 01349 { 01350 while (true) 01351 { 01352 qApp->processEvents(); 01353 01354 TVState state = GetState(0); 01355 if ((kState_Error == state) || (kState_None == state)) 01356 return; 01357 01358 if (kState_ChangingState == state) 01359 continue; 01360 01361 int count = player.size(); 01362 for (int i = 0; i < count; i++) 01363 { 01364 const PlayerContext *mctx = GetPlayerReadLock(i, __FILE__, __LINE__); 01365 if (mctx) 01366 { 01367 mctx->LockDeletePlayer(__FILE__, __LINE__); 01368 if (mctx->player && !mctx->player->IsErrored()) 01369 { 01370 mctx->player->EventLoop(); 01371 mctx->player->VideoLoop(); 01372 } 01373 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 01374 } 01375 ReturnPlayerLock(mctx); 01376 } 01377 } 01378 } 01379 01383 void TV::UpdateChannelList(int groupID) 01384 { 01385 if (!db_use_channel_groups) 01386 return; 01387 01388 QMutexLocker locker(&channelGroupLock); 01389 if (groupID == channelGroupId) 01390 return; 01391 01392 DBChanList list; 01393 if (groupID != -1) 01394 { 01395 list = ChannelUtil::GetChannels( 01396 0, true, "channum, callsign", groupID); 01397 ChannelUtil::SortChannels(list, "channum", true); 01398 } 01399 01400 channelGroupId = groupID; 01401 channelGroupChannelList = list; 01402 01403 if (db_remember_last_channel_group) 01404 gCoreContext->SaveSetting("ChannelGroupDefault", channelGroupId); 01405 } 01406 01410 TVState TV::GetState(int player_idx) const 01411 { 01412 TVState ret = kState_ChangingState; 01413 const PlayerContext *ctx = GetPlayerReadLock(player_idx, __FILE__, __LINE__); 01414 ret = GetState(ctx); 01415 ReturnPlayerLock(ctx); 01416 return ret; 01417 } 01418 01419 // XXX what about subtitlezoom? 01420 void TV::GetStatus(void) 01421 { 01422 QVariantMap status; 01423 01424 const PlayerContext *ctx = GetPlayerReadLock(-1, __FILE__, __LINE__); 01425 01426 status.insert("state", StateToString(GetState(ctx))); 01427 ctx->LockPlayingInfo(__FILE__, __LINE__); 01428 if (ctx->playingInfo) 01429 { 01430 status.insert("title", ctx->playingInfo->GetTitle()); 01431 status.insert("subtitle", ctx->playingInfo->GetSubtitle()); 01432 status.insert("starttime", 01433 ctx->playingInfo->GetRecordingStartTime() 01434 .toUTC().toString("yyyy-MM-ddThh:mm:ssZ")); 01435 status.insert("chanid", 01436 QString::number(ctx->playingInfo->GetChanID())); 01437 status.insert("programid", ctx->playingInfo->GetProgramID()); 01438 } 01439 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 01440 osdInfo info; 01441 ctx->CalcPlayerSliderPosition(info); 01442 ctx->LockDeletePlayer(__FILE__, __LINE__); 01443 if (ctx->player) 01444 { 01445 if (!info.text["totalchapters"].isEmpty()) 01446 { 01447 QList<long long> chapters; 01448 ctx->player->GetChapterTimes(chapters); 01449 QVariantList var; 01450 foreach (long long chapter, chapters) 01451 var << QVariant(chapter); 01452 status.insert("chaptertimes", var); 01453 } 01454 01455 uint capmode = ctx->player->GetCaptionMode(); 01456 QVariantMap tracks; 01457 01458 QStringList list = ctx->player->GetTracks(kTrackTypeSubtitle); 01459 int currenttrack = -1; 01460 if (!list.isEmpty() && (kDisplayAVSubtitle == capmode)) 01461 currenttrack = ctx->player->GetTrack(kTrackTypeSubtitle); 01462 for (int i = 0; i < list.size(); i++) 01463 { 01464 if (i == currenttrack) 01465 status.insert("currentsubtitletrack", list[i]); 01466 tracks.insert("SELECTSUBTITLE_" + QString::number(i), list[i]); 01467 } 01468 01469 list = ctx->player->GetTracks(kTrackTypeTeletextCaptions); 01470 currenttrack = -1; 01471 if (!list.isEmpty() && (kDisplayTeletextCaptions == capmode)) 01472 currenttrack = ctx->player->GetTrack(kTrackTypeTeletextCaptions); 01473 for (int i = 0; i < list.size(); i++) 01474 { 01475 if (i == currenttrack) 01476 status.insert("currentsubtitletrack", list[i]); 01477 tracks.insert("SELECTTTC_" + QString::number(i), list[i]); 01478 } 01479 01480 list = ctx->player->GetTracks(kTrackTypeCC708); 01481 currenttrack = -1; 01482 if (!list.isEmpty() && (kDisplayCC708 == capmode)) 01483 currenttrack = ctx->player->GetTrack(kTrackTypeCC708); 01484 for (int i = 0; i < list.size(); i++) 01485 { 01486 if (i == currenttrack) 01487 status.insert("currentsubtitletrack", list[i]); 01488 tracks.insert("SELECTCC708_" + QString::number(i), list[i]); 01489 } 01490 01491 list = ctx->player->GetTracks(kTrackTypeCC608); 01492 currenttrack = -1; 01493 if (!list.isEmpty() && (kDisplayCC608 == capmode)) 01494 currenttrack = ctx->player->GetTrack(kTrackTypeCC608); 01495 for (int i = 0; i < list.size(); i++) 01496 { 01497 if (i == currenttrack) 01498 status.insert("currentsubtitletrack", list[i]); 01499 tracks.insert("SELECTCC608_" + QString::number(i), list[i]); 01500 } 01501 01502 list = ctx->player->GetTracks(kTrackTypeRawText); 01503 currenttrack = -1; 01504 if (!list.isEmpty() && (kDisplayRawTextSubtitle == capmode)) 01505 currenttrack = ctx->player->GetTrack(kTrackTypeRawText); 01506 for (int i = 0; i < list.size(); i++) 01507 { 01508 if (i == currenttrack) 01509 status.insert("currentsubtitletrack", list[i]); 01510 tracks.insert("SELECTRAWTEXT_" + QString::number(i), list[i]); 01511 } 01512 01513 if (ctx->player->HasTextSubtitles()) 01514 { 01515 if (kDisplayTextSubtitle == capmode) 01516 status.insert("currentsubtitletrack", tr("External Subtitles")); 01517 tracks.insert(ACTION_ENABLEEXTTEXT, tr("External Subtitles")); 01518 } 01519 01520 status.insert("totalsubtitletracks", tracks.size()); 01521 if (!tracks.isEmpty()) 01522 status.insert("subtitletracks", tracks); 01523 01524 tracks.clear(); 01525 list = ctx->player->GetTracks(kTrackTypeAudio); 01526 currenttrack = ctx->player->GetTrack(kTrackTypeAudio); 01527 for (int i = 0; i < list.size(); i++) 01528 { 01529 if (i == currenttrack) 01530 status.insert("currentaudiotrack", list[i]); 01531 tracks.insert("SELECTAUDIO_" + QString::number(i), list[i]); 01532 } 01533 01534 status.insert("totalaudiotracks", tracks.size()); 01535 if (!tracks.isEmpty()) 01536 status.insert("audiotracks", tracks); 01537 01538 status.insert("playspeed", ctx->player->GetPlaySpeed()); 01539 status.insert("audiosyncoffset", (long long)ctx->player->GetAudioTimecodeOffset()); 01540 if (ctx->player->GetAudio()->ControlsVolume()) 01541 { 01542 status.insert("volume", ctx->player->GetVolume()); 01543 status.insert("mute", ctx->player->GetMuteState()); 01544 } 01545 if (ctx->player->GetVideoOutput()) 01546 { 01547 VideoOutput *vo = ctx->player->GetVideoOutput(); 01548 PictureAttributeSupported supp = 01549 vo->GetSupportedPictureAttributes(); 01550 if (supp & kPictureAttributeSupported_Brightness) 01551 { 01552 status.insert("brightness", 01553 vo->GetPictureAttribute(kPictureAttribute_Brightness)); 01554 } 01555 if (supp & kPictureAttributeSupported_Brightness) 01556 { 01557 status.insert("contrast", 01558 vo->GetPictureAttribute(kPictureAttribute_Contrast)); 01559 } 01560 if (supp & kPictureAttributeSupported_Brightness) 01561 { 01562 status.insert("colour", 01563 vo->GetPictureAttribute(kPictureAttribute_Colour)); 01564 } 01565 if (supp & kPictureAttributeSupported_Brightness) 01566 { 01567 status.insert("hue", 01568 vo->GetPictureAttribute(kPictureAttribute_Hue)); 01569 } 01570 if (supp & kPictureAttributeSupported_StudioLevels) 01571 { 01572 status.insert("studiolevels", 01573 vo->GetPictureAttribute(kPictureAttribute_StudioLevels)); 01574 } 01575 } 01576 } 01577 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 01578 01579 ReturnPlayerLock(ctx); 01580 01581 QHashIterator<QString,QString> tit(info.text); 01582 while (tit.hasNext()) 01583 { 01584 tit.next(); 01585 status.insert(tit.key(), tit.value()); 01586 } 01587 01588 QHashIterator<QString,int> vit(info.values); 01589 while (vit.hasNext()) 01590 { 01591 vit.next(); 01592 status.insert(vit.key(), vit.value()); 01593 } 01594 01595 MythUIStateTracker::SetState(status); 01596 } 01597 01601 TVState TV::GetState(const PlayerContext *actx) const 01602 { 01603 TVState ret = kState_ChangingState; 01604 if (!actx->InStateChange()) 01605 ret = actx->GetState(); 01606 return ret; 01607 } 01608 01613 bool TV::LiveTV(bool showDialogs) 01614 { 01615 requestDelete = false; 01616 allowRerecord = false; 01617 jumpToProgram = false; 01618 01619 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 01620 if (actx->GetState() == kState_None && 01621 RequestNextRecorder(actx, showDialogs)) 01622 { 01623 actx->SetInitialTVState(true); 01624 HandleStateChange(actx, actx); 01625 switchToRec = NULL; 01626 01627 // Start Idle Timer 01628 if (db_idle_timeout > 0) 01629 { 01630 idleTimerId = StartTimer(db_idle_timeout, __LINE__); 01631 LOG(VB_GENERAL, LOG_INFO, QString("Using Idle Timer. %1 minutes") 01632 .arg(db_idle_timeout*(1.0f/60000.0f))); 01633 } 01634 01635 ReturnPlayerLock(actx); 01636 return true; 01637 } 01638 ReturnPlayerLock(actx); 01639 return false; 01640 } 01641 01642 int TV::GetLastRecorderNum(int player_idx) const 01643 { 01644 const PlayerContext *ctx = GetPlayerReadLock(player_idx, __FILE__, __LINE__); 01645 int ret = ctx->GetCardID(); 01646 ReturnPlayerLock(ctx); 01647 return ret; 01648 } 01649 01650 bool TV::RequestNextRecorder(PlayerContext *ctx, bool showDialogs) 01651 { 01652 if (!ctx) 01653 return false; 01654 01655 ctx->SetRecorder(NULL); 01656 01657 RemoteEncoder *testrec = NULL; 01658 if (switchToRec) 01659 { 01660 // If this is set we, already got a new recorder in SwitchCards() 01661 testrec = switchToRec; 01662 switchToRec = NULL; 01663 } 01664 else 01665 { 01666 // When starting LiveTV we just get the next free recorder 01667 testrec = RemoteRequestNextFreeRecorder(-1); 01668 } 01669 01670 if (!testrec) 01671 return false; 01672 01673 if (!testrec->IsValidRecorder()) 01674 { 01675 if (showDialogs) 01676 ShowNoRecorderDialog(ctx); 01677 01678 delete testrec; 01679 01680 return false; 01681 } 01682 01683 ctx->SetRecorder(testrec); 01684 01685 return true; 01686 } 01687 01688 void TV::FinishRecording(int player_ctx) 01689 { 01690 PlayerContext *ctx = GetPlayerReadLock(player_ctx, __FILE__, __LINE__); 01691 if (StateIsRecording(GetState(ctx)) && ctx->recorder) 01692 ctx->recorder->FinishRecording(); 01693 ReturnPlayerLock(ctx); 01694 } 01695 01696 void TV::AskAllowRecording(PlayerContext *ctx, 01697 const QStringList &msg, int timeuntil, 01698 bool hasrec, bool haslater) 01699 { 01700 #if 0 01701 LOG(VB_GENERAL, LOG_DEBUG, LOC + "AskAllowRecording"); 01702 #endif 01703 if (!StateIsLiveTV(GetState(ctx))) 01704 return; 01705 01706 ProgramInfo *info = new ProgramInfo(msg); 01707 if (!info->GetChanID()) 01708 { 01709 delete info; 01710 return; 01711 } 01712 01713 QMutexLocker locker(&askAllowLock); 01714 QString key = info->MakeUniqueKey(); 01715 if (timeuntil > 0) 01716 { 01717 // add program to list 01718 #if 0 01719 LOG(VB_GENERAL, LOG_DEBUG, LOC + "AskAllowRecording -- " + 01720 QString("adding '%1'").arg(info->title)); 01721 #endif 01722 QDateTime expiry = QDateTime::currentDateTime().addSecs(timeuntil); 01723 askAllowPrograms[key] = AskProgramInfo(expiry, hasrec, haslater, info); 01724 } 01725 else 01726 { 01727 // remove program from list 01728 LOG(VB_GENERAL, LOG_INFO, LOC + "AskAllowRecording -- " + 01729 QString("removing '%1'").arg(info->GetTitle())); 01730 QMap<QString,AskProgramInfo>::iterator it = askAllowPrograms.find(key); 01731 if (it != askAllowPrograms.end()) 01732 { 01733 delete (*it).info; 01734 askAllowPrograms.erase(it); 01735 } 01736 delete info; 01737 } 01738 01739 ShowOSDAskAllow(ctx); 01740 } 01741 01742 void TV::ShowOSDAskAllow(PlayerContext *ctx) 01743 { 01744 QMutexLocker locker(&askAllowLock); 01745 if (!ctx->recorder) 01746 return; 01747 01748 uint cardid = ctx->GetCardID(); 01749 01750 QString single_rec = 01751 tr("MythTV wants to record \"%1\" on %2 in %d seconds. " 01752 "Do you want to:"); 01753 01754 QString record_watch = tr("Record and watch while it records"); 01755 QString let_record1 = tr("Let it record and go back to the Main Menu"); 01756 QString let_recordm = tr("Let them record and go back to the Main Menu"); 01757 QString record_later1 = tr("Record it later, I want to watch TV"); 01758 QString record_laterm = tr("Record them later, I want to watch TV"); 01759 QString do_not_record1= tr("Don't let it record, I want to watch TV"); 01760 QString do_not_recordm= tr("Don't let them record, I want to watch TV"); 01761 01762 // eliminate timed out programs 01763 QDateTime timeNow = QDateTime::currentDateTime(); 01764 QMap<QString,AskProgramInfo>::iterator it = askAllowPrograms.begin(); 01765 QMap<QString,AskProgramInfo>::iterator next = it; 01766 while (it != askAllowPrograms.end()) 01767 { 01768 next = it; ++next; 01769 if ((*it).expiry <= timeNow) 01770 { 01771 #if 0 01772 LOG(VB_GENERAL, LOG_DEBUG, LOC + "UpdateOSDAskAllowDialog -- " + 01773 QString("removing '%1'").arg((*it).info->title)); 01774 #endif 01775 delete (*it).info; 01776 askAllowPrograms.erase(it); 01777 } 01778 it = next; 01779 } 01780 int timeuntil = 0; 01781 QString message = QString::null; 01782 uint conflict_count = askAllowPrograms.size(); 01783 01784 it = askAllowPrograms.begin(); 01785 if ((1 == askAllowPrograms.size()) && ((*it).info->GetCardID() == cardid)) 01786 { 01787 (*it).is_in_same_input_group = (*it).is_conflicting = true; 01788 } 01789 else if (!askAllowPrograms.empty()) 01790 { 01791 // get the currently used input on our card 01792 bool busy_input_grps_loaded = false; 01793 vector<uint> busy_input_grps; 01794 TunedInputInfo busy_input; 01795 RemoteIsBusy(cardid, busy_input); 01796 01797 // check if current input can conflict 01798 it = askAllowPrograms.begin(); 01799 for (; it != askAllowPrograms.end(); ++it) 01800 { 01801 (*it).is_in_same_input_group = 01802 (cardid == (*it).info->GetCardID()); 01803 01804 if ((*it).is_in_same_input_group) 01805 continue; 01806 01807 // is busy_input in same input group as recording 01808 if (!busy_input_grps_loaded) 01809 { 01810 busy_input_grps = CardUtil::GetInputGroups(busy_input.inputid); 01811 busy_input_grps_loaded = true; 01812 } 01813 01814 vector<uint> input_grps = 01815 CardUtil::GetInputGroups((*it).info->GetInputID()); 01816 01817 for (uint i = 0; i < input_grps.size(); i++) 01818 { 01819 if (find(busy_input_grps.begin(), busy_input_grps.end(), 01820 input_grps[i]) != busy_input_grps.end()) 01821 { 01822 (*it).is_in_same_input_group = true; 01823 break; 01824 } 01825 } 01826 } 01827 01828 // check if inputs that can conflict are ok 01829 conflict_count = 0; 01830 it = askAllowPrograms.begin(); 01831 for (; it != askAllowPrograms.end(); ++it) 01832 { 01833 if (!(*it).is_in_same_input_group) 01834 (*it).is_conflicting = false; 01835 else if ((cardid == (uint)(*it).info->GetCardID())) 01836 (*it).is_conflicting = true; 01837 else if (!CardUtil::IsTunerShared(cardid, (*it).info->GetCardID())) 01838 (*it).is_conflicting = true; 01839 else if ((busy_input.sourceid == (uint)(*it).info->GetSourceID()) && 01840 (busy_input.mplexid == (uint)(*it).info->QueryMplexID())) 01841 (*it).is_conflicting = false; 01842 else 01843 (*it).is_conflicting = true; 01844 01845 conflict_count += (*it).is_conflicting ? 1 : 0; 01846 } 01847 } 01848 01849 it = askAllowPrograms.begin(); 01850 for (; it != askAllowPrograms.end() && !(*it).is_conflicting; ++it); 01851 01852 if (conflict_count == 0) 01853 { 01854 LOG(VB_GENERAL, LOG_INFO, LOC + "The scheduler wants to make " 01855 "a non-conflicting recording."); 01856 // TODO take down mplexid and inform user of problem 01857 // on channel changes. 01858 } 01859 else if (conflict_count == 1 && ((*it).info->GetCardID() == cardid)) 01860 { 01861 #if 0 01862 LOG(VB_GENERAL, LOG_DEBUG, LOC + "UpdateOSDAskAllowDialog -- " + 01863 "kAskAllowOneRec"); 01864 #endif 01865 01866 it = askAllowPrograms.begin(); 01867 01868 QString channel = db_channel_format; 01869 channel 01870 .replace("<num>", (*it).info->GetChanNum()) 01871 .replace("<sign>", (*it).info->GetChannelSchedulingID()) 01872 .replace("<name>", (*it).info->GetChannelName()); 01873 01874 message = single_rec.arg((*it).info->GetTitle()).arg(channel); 01875 01876 OSD *osd = GetOSDLock(ctx); 01877 if (osd) 01878 { 01879 browsehelper->BrowseEnd(ctx, false); 01880 timeuntil = QDateTime::currentDateTime().secsTo((*it).expiry) * 1000; 01881 osd->DialogShow(OSD_DLG_ASKALLOW, message, timeuntil); 01882 osd->DialogAddButton(record_watch, "DIALOG_ASKALLOW_WATCH_0", 01883 false, !((*it).has_rec)); 01884 osd->DialogAddButton(let_record1, "DIALOG_ASKALLOW_EXIT_0"); 01885 osd->DialogAddButton(((*it).has_later) ? record_later1 : do_not_record1, 01886 "DIALOG_ASKALLOW_CANCELRECORDING_0", 01887 false, ((*it).has_rec)); 01888 } 01889 ReturnOSDLock(ctx, osd); 01890 } 01891 else 01892 { 01893 if (conflict_count > 1) 01894 { 01895 message = QObject::tr( 01896 "MythTV wants to record these programs in %d seconds:"); 01897 message += "\n"; 01898 } 01899 01900 bool has_rec = false; 01901 it = askAllowPrograms.begin(); 01902 for (; it != askAllowPrograms.end(); ++it) 01903 { 01904 if (!(*it).is_conflicting) 01905 continue; 01906 01907 QString title = (*it).info->GetTitle(); 01908 if ((title.length() < 10) && !(*it).info->GetSubtitle().isEmpty()) 01909 title += ": " + (*it).info->GetSubtitle(); 01910 if (title.length() > 20) 01911 title = title.left(17) + "..."; 01912 01913 QString channel = db_channel_format; 01914 channel 01915 .replace("<num>", (*it).info->GetChanNum()) 01916 .replace("<sign>", (*it).info->GetChannelSchedulingID()) 01917 .replace("<name>", (*it).info->GetChannelName()); 01918 01919 if (conflict_count > 1) 01920 { 01921 message += QObject::tr("\"%1\" on %2").arg(title).arg(channel); 01922 message += "\n"; 01923 } 01924 else 01925 { 01926 message = single_rec.arg((*it).info->GetTitle()).arg(channel); 01927 has_rec = (*it).has_rec; 01928 } 01929 } 01930 01931 if (conflict_count > 1) 01932 { 01933 message += "\n"; 01934 message += QObject::tr("Do you want to:"); 01935 } 01936 01937 bool all_have_later = true; 01938 timeuntil = 9999999; 01939 it = askAllowPrograms.begin(); 01940 for (; it != askAllowPrograms.end(); ++it) 01941 { 01942 if ((*it).is_conflicting) 01943 { 01944 all_have_later &= (*it).has_later; 01945 int tmp = QDateTime::currentDateTime().secsTo((*it).expiry); 01946 tmp *= 1000; 01947 timeuntil = min(timeuntil, max(tmp, 0)); 01948 } 01949 } 01950 timeuntil = (9999999 == timeuntil) ? 0 : timeuntil; 01951 01952 OSD *osd = GetOSDLock(ctx); 01953 if (osd && conflict_count > 1) 01954 { 01955 browsehelper->BrowseEnd(ctx, false); 01956 osd->DialogShow(OSD_DLG_ASKALLOW, message, timeuntil); 01957 osd->DialogAddButton(let_recordm, "DIALOG_ASKALLOW_EXIT_0", 01958 false, true); 01959 osd->DialogAddButton((all_have_later) ? record_laterm : do_not_recordm, 01960 "DIALOG_ASKALLOW_CANCELCONFLICTING_0"); 01961 } 01962 else if (osd) 01963 { 01964 browsehelper->BrowseEnd(ctx, false); 01965 osd->DialogShow(OSD_DLG_ASKALLOW, message, timeuntil); 01966 osd->DialogAddButton(let_record1, "DIALOG_ASKALLOW_EXIT_0", 01967 false, !has_rec); 01968 osd->DialogAddButton((all_have_later) ? record_later1 : do_not_record1, 01969 "DIALOG_ASKALLOW_CANCELRECORDING_0", 01970 false, has_rec); 01971 } 01972 ReturnOSDLock(ctx, osd); 01973 } 01974 } 01975 01976 void TV::HandleOSDAskAllow(PlayerContext *ctx, QString action) 01977 { 01978 if (!DialogIsVisible(ctx, OSD_DLG_ASKALLOW)) 01979 return; 01980 01981 if (!askAllowLock.tryLock()) 01982 { 01983 LOG(VB_GENERAL, LOG_ERR, "allowrecordingbox : askAllowLock is locked"); 01984 return; 01985 } 01986 01987 if (action == "CANCELRECORDING") 01988 { 01989 if (ctx->recorder) 01990 ctx->recorder->CancelNextRecording(true); 01991 } 01992 else if (action == "CANCELCONFLICTING") 01993 { 01994 QMap<QString,AskProgramInfo>::iterator it = 01995 askAllowPrograms.begin(); 01996 for (; it != askAllowPrograms.end(); ++it) 01997 { 01998 if ((*it).is_conflicting) 01999 RemoteCancelNextRecording((*it).info->GetCardID(), true); 02000 } 02001 } 02002 else if (action == "WATCH") 02003 { 02004 if (ctx->recorder) 02005 ctx->recorder->CancelNextRecording(false); 02006 } 02007 else // if (action == "EXIT") 02008 { 02009 PrepareToExitPlayer(ctx, __LINE__); 02010 SetExitPlayer(true, true); 02011 } 02012 02013 askAllowLock.unlock(); 02014 } 02015 02016 int TV::Playback(const ProgramInfo &rcinfo) 02017 { 02018 wantsToQuit = false; 02019 jumpToProgram = false; 02020 allowRerecord = false; 02021 requestDelete = false; 02022 02023 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 02024 if (mctx->GetState() != kState_None) 02025 { 02026 ReturnPlayerLock(mctx); 02027 return 0; 02028 } 02029 02030 mctx->SetPlayingInfo(&rcinfo); 02031 mctx->SetInitialTVState(false); 02032 HandleStateChange(mctx, mctx); 02033 02034 ReturnPlayerLock(mctx); 02035 02036 if (LCD *lcd = LCD::Get()) 02037 { 02038 lcd->switchToChannel(rcinfo.GetChannelSchedulingID(), 02039 rcinfo.GetTitle(), rcinfo.GetSubtitle()); 02040 lcd->setFunctionLEDs((rcinfo.IsRecording())?FUNC_TV:FUNC_MOVIE, true); 02041 } 02042 02043 return 1; 02044 } 02045 02046 bool TV::StateIsRecording(TVState state) 02047 { 02048 return (state == kState_RecordingOnly || 02049 state == kState_WatchingRecording); 02050 } 02051 02052 bool TV::StateIsPlaying(TVState state) 02053 { 02054 return (state == kState_WatchingPreRecorded || 02055 state == kState_WatchingRecording || 02056 state == kState_WatchingVideo || 02057 state == kState_WatchingDVD || 02058 state == kState_WatchingBD); 02059 } 02060 02061 bool TV::StateIsLiveTV(TVState state) 02062 { 02063 return (state == kState_WatchingLiveTV); 02064 } 02065 02066 TVState TV::RemoveRecording(TVState state) 02067 { 02068 if (StateIsRecording(state)) 02069 { 02070 if (state == kState_RecordingOnly) 02071 return kState_None; 02072 return kState_WatchingPreRecorded; 02073 } 02074 return kState_Error; 02075 } 02076 02077 #define TRANSITION(ASTATE,BSTATE) \ 02078 ((ctxState == ASTATE) && (desiredNextState == BSTATE)) 02079 02080 #define SET_NEXT() do { nextState = desiredNextState; changed = true; } while(0) 02081 #define SET_LAST() do { nextState = ctxState; changed = true; } while(0) 02082 02083 static QString tv_i18n(const QString &msg) 02084 { 02085 QByteArray msg_arr = msg.toLatin1(); 02086 QString msg_i18n = QObject::tr(msg_arr.constData()); 02087 QByteArray msg_i18n_arr = msg_i18n.toLatin1(); 02088 return (msg_arr == msg_i18n_arr) ? msg_i18n : msg; 02089 } 02090 02099 void TV::HandleStateChange(PlayerContext *mctx, PlayerContext *ctx) 02100 { 02101 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("HandleStateChange(%1) -- begin") 02102 .arg(find_player_index(ctx))); 02103 02104 if (ctx->IsErrored()) 02105 { 02106 LOG(VB_GENERAL, LOG_ERR, LOC + 02107 "HandleStateChange(): Called after fatal error detected."); 02108 return; 02109 } 02110 02111 bool changed = false; 02112 02113 ctx->LockState(); 02114 TVState nextState = ctx->GetState(); 02115 if (ctx->nextState.empty()) 02116 { 02117 LOG(VB_GENERAL, LOG_WARNING, LOC + 02118 "HandleStateChange() Warning, called with no state to change to."); 02119 ctx->UnlockState(); 02120 return; 02121 } 02122 02123 TVState ctxState = ctx->GetState(); 02124 TVState desiredNextState = ctx->DequeueNextState(); 02125 02126 LOG(VB_GENERAL, LOG_INFO, LOC + 02127 QString("Attempting to change from %1 to %2") 02128 .arg(StateToString(nextState)) 02129 .arg(StateToString(desiredNextState))); 02130 02131 if (desiredNextState == kState_Error) 02132 { 02133 LOG(VB_GENERAL, LOG_ERR, LOC + "HandleStateChange(): " 02134 "Attempting to set to an error state!"); 02135 SetErrored(ctx); 02136 ctx->UnlockState(); 02137 return; 02138 } 02139 02140 bool ok = false; 02141 if (TRANSITION(kState_None, kState_WatchingLiveTV)) 02142 { 02143 QString name = ""; 02144 02145 ctx->lastSignalUIInfo.clear(); 02146 02147 ctx->recorder->Setup(); 02148 02149 QDateTime timerOffTime = QDateTime::currentDateTime(); 02150 lockTimerOn = false; 02151 02152 SET_NEXT(); 02153 02154 uint chanid = gCoreContext->GetNumSetting("DefaultChanid", 0); 02155 02156 if (chanid && !IsTunable(ctx, chanid)) 02157 chanid = 0; 02158 02159 QString channum = ""; 02160 02161 if (chanid) 02162 { 02163 QStringList reclist; 02164 02165 MSqlQuery query(MSqlQuery::InitCon()); 02166 query.prepare("SELECT channum FROM channel " 02167 "WHERE chanid = :CHANID"); 02168 query.bindValue(":CHANID", chanid); 02169 if (query.exec() && query.isActive() && query.size() > 0 && query.next()) 02170 channum = query.value(0).toString(); 02171 else 02172 channum = QString::number(chanid); 02173 02174 bool getit = ctx->recorder->ShouldSwitchToAnotherCard( 02175 QString::number(chanid)); 02176 02177 if (getit) 02178 reclist = ChannelUtil::GetValidRecorderList(chanid, channum); 02179 02180 if (reclist.size()) 02181 { 02182 RemoteEncoder *testrec = NULL; 02183 vector<uint> excluded_cardids; 02184 testrec = RemoteRequestFreeRecorderFromList(reclist, 02185 excluded_cardids); 02186 if (testrec && testrec->IsValidRecorder()) 02187 { 02188 ctx->SetRecorder(testrec); 02189 ctx->recorder->Setup(); 02190 } 02191 } 02192 else if (getit) 02193 chanid = 0; 02194 } 02195 02196 LOG(VB_GENERAL, LOG_NOTICE, LOC + "Spawning LiveTV Recorder -- begin"); 02197 02198 if (chanid && !channum.isEmpty()) 02199 ctx->recorder->SpawnLiveTV(ctx->tvchain->GetID(), false, channum); 02200 else 02201 ctx->recorder->SpawnLiveTV(ctx->tvchain->GetID(), false, ""); 02202 02203 LOG(VB_GENERAL, LOG_NOTICE, LOC + "Spawning LiveTV Recorder -- end"); 02204 02205 if (!ctx->ReloadTVChain()) 02206 { 02207 LOG(VB_GENERAL, LOG_ERR, LOC + 02208 "HandleStateChange(): LiveTV not successfully started"); 02209 RestoreScreenSaver(ctx); 02210 ctx->SetRecorder(NULL); 02211 SetErrored(ctx); 02212 SET_LAST(); 02213 } 02214 else 02215 { 02216 ctx->LockPlayingInfo(__FILE__, __LINE__); 02217 QString playbackURL = ctx->playingInfo->GetPlaybackURL(true); 02218 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 02219 02220 bool opennow = (ctx->tvchain->GetCardType(-1) != "DUMMY"); 02221 02222 LOG(VB_GENERAL, LOG_INFO, LOC + 02223 QString("playbackURL(%1) cardtype(%2)") 02224 .arg(playbackURL).arg(ctx->tvchain->GetCardType(-1))); 02225 02226 ctx->SetRingBuffer( 02227 RingBuffer::Create( 02228 playbackURL, false, true, 02229 opennow ? RingBuffer::kLiveTVOpenTimeout : -1)); 02230 02231 ctx->buffer->SetLiveMode(ctx->tvchain); 02232 } 02233 02234 02235 if (ctx->playingInfo && StartRecorder(ctx,-1)) 02236 { 02237 ok = StartPlayer(mctx, ctx, desiredNextState); 02238 } 02239 if (!ok) 02240 { 02241 LOG(VB_GENERAL, LOG_ERR, LOC + "LiveTV not successfully started"); 02242 RestoreScreenSaver(ctx); 02243 ctx->SetRecorder(NULL); 02244 SetErrored(ctx); 02245 SET_LAST(); 02246 } 02247 else if (!ctx->IsPIP()) 02248 { 02249 if (!lastLockSeenTime.isValid() || 02250 (lastLockSeenTime < timerOffTime)) 02251 { 02252 lockTimer.start(); 02253 lockTimerOn = true; 02254 } 02255 } 02256 02257 if (mctx != ctx) 02258 SetActive(ctx, find_player_index(ctx), false); 02259 } 02260 else if (TRANSITION(kState_WatchingLiveTV, kState_None)) 02261 { 02262 SET_NEXT(); 02263 RestoreScreenSaver(ctx); 02264 StopStuff(mctx, ctx, true, true, true); 02265 02266 if ((mctx != ctx) && (GetPlayer(ctx,-1) == ctx)) 02267 SetActive(mctx, 0, true); 02268 } 02269 else if (TRANSITION(kState_WatchingRecording, kState_WatchingPreRecorded)) 02270 { 02271 SET_NEXT(); 02272 } 02273 else if (TRANSITION(kState_None, kState_WatchingPreRecorded) || 02274 TRANSITION(kState_None, kState_WatchingVideo) || 02275 TRANSITION(kState_None, kState_WatchingDVD) || 02276 TRANSITION(kState_None, kState_WatchingBD) || 02277 TRANSITION(kState_None, kState_WatchingRecording)) 02278 { 02279 ctx->LockPlayingInfo(__FILE__, __LINE__); 02280 QString playbackURL = ctx->playingInfo->GetPlaybackURL(true); 02281 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 02282 02283 ctx->SetRingBuffer(RingBuffer::Create(playbackURL, false)); 02284 02285 if (ctx->buffer && ctx->buffer->IsOpen()) 02286 { 02287 if (desiredNextState == kState_WatchingRecording) 02288 { 02289 ctx->LockPlayingInfo(__FILE__, __LINE__); 02290 RemoteEncoder *rec = RemoteGetExistingRecorder( 02291 ctx->playingInfo); 02292 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 02293 02294 ctx->SetRecorder(rec); 02295 02296 if (!ctx->recorder || 02297 !ctx->recorder->IsValidRecorder()) 02298 { 02299 LOG(VB_GENERAL, LOG_ERR, LOC + 02300 "Couldn't find recorder for in-progress recording"); 02301 desiredNextState = kState_WatchingPreRecorded; 02302 ctx->SetRecorder(NULL); 02303 } 02304 else 02305 { 02306 ctx->recorder->Setup(); 02307 } 02308 } 02309 02310 ok = StartPlayer(mctx, ctx, desiredNextState); 02311 02312 if (ok) 02313 { 02314 SET_NEXT(); 02315 02316 ctx->LockPlayingInfo(__FILE__, __LINE__); 02317 if (ctx->playingInfo->IsRecording()) 02318 { 02319 QString message = "COMMFLAG_REQUEST "; 02320 message += ctx->playingInfo->MakeUniqueKey(); 02321 gCoreContext->SendMessage(message); 02322 } 02323 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 02324 } 02325 } 02326 02327 if (!ok) 02328 { 02329 SET_LAST(); 02330 SetErrored(ctx); 02331 } 02332 else if (mctx != ctx) 02333 { 02334 SetActive(ctx, find_player_index(ctx), false); 02335 } 02336 } 02337 else if (TRANSITION(kState_WatchingPreRecorded, kState_None) || 02338 TRANSITION(kState_WatchingVideo, kState_None) || 02339 TRANSITION(kState_WatchingDVD, kState_None) || 02340 TRANSITION(kState_WatchingBD, kState_None) || 02341 TRANSITION(kState_WatchingRecording, kState_None)) 02342 { 02343 SET_NEXT(); 02344 02345 RestoreScreenSaver(ctx); 02346 StopStuff(mctx, ctx, true, true, false); 02347 02348 if ((mctx != ctx) && (GetPlayer(ctx,-1) == ctx)) 02349 SetActive(mctx, 0, true); 02350 } 02351 else if (TRANSITION(kState_None, kState_None)) 02352 { 02353 SET_NEXT(); 02354 } 02355 02356 // Print state changed message... 02357 if (!changed) 02358 { 02359 LOG(VB_GENERAL, LOG_ERR, LOC + 02360 QString("Unknown state transition: %1 to %2") 02361 .arg(StateToString(ctx->GetState())) 02362 .arg(StateToString(desiredNextState))); 02363 } 02364 else if (ctx->GetState() != nextState) 02365 { 02366 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Changing from %1 to %2") 02367 .arg(StateToString(ctx->GetState())) 02368 .arg(StateToString(nextState))); 02369 } 02370 02371 // update internal state variable 02372 TVState lastState = ctx->GetState(); 02373 ctx->playingState = nextState; 02374 ctx->UnlockState(); 02375 02376 if (mctx == ctx) 02377 { 02378 if (StateIsLiveTV(ctx->GetState())) 02379 { 02380 LOG(VB_GENERAL, LOG_INFO, LOC + "State is LiveTV & mctx == ctx"); 02381 UpdateOSDInput(ctx); 02382 LOG(VB_GENERAL, LOG_INFO, LOC + "UpdateOSDInput done"); 02383 UpdateLCD(); 02384 LOG(VB_GENERAL, LOG_INFO, LOC + "UpdateLCD done"); 02385 ITVRestart(ctx, true); 02386 LOG(VB_GENERAL, LOG_INFO, LOC + "ITVRestart done"); 02387 } 02388 else if (StateIsPlaying(ctx->GetState()) && lastState == kState_None) 02389 { 02390 ctx->LockPlayingInfo(__FILE__, __LINE__); 02391 int count = PlayGroup::GetCount(); 02392 QString msg = tr("%1 Settings") 02393 .arg(tv_i18n(ctx->playingInfo->GetPlaybackGroup())); 02394 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 02395 if (count > 0) 02396 SetOSDMessage(ctx, msg); 02397 ITVRestart(ctx, false); 02398 } 02399 02400 if (ctx->buffer && ctx->buffer->IsDVD()) 02401 { 02402 UpdateLCD(); 02403 } 02404 02405 if (ctx->recorder) 02406 ctx->recorder->FrontendReady(); 02407 02408 QMutexLocker locker(&timerIdLock); 02409 if (endOfRecPromptTimerId) 02410 KillTimer(endOfRecPromptTimerId); 02411 endOfRecPromptTimerId = 0; 02412 if (db_end_of_rec_exit_prompt && !inPlaylist && !underNetworkControl) 02413 { 02414 endOfRecPromptTimerId = 02415 StartTimer(kEndOfRecPromptCheckFrequency, __LINE__); 02416 } 02417 02418 if (endOfPlaybackTimerId) 02419 KillTimer(endOfPlaybackTimerId); 02420 endOfPlaybackTimerId = 0; 02421 02422 if (StateIsPlaying(ctx->GetState())) 02423 { 02424 endOfPlaybackTimerId = 02425 StartTimer(kEndOfPlaybackFirstCheckTimer, __LINE__); 02426 02427 } 02428 02429 } 02430 02431 if (TRANSITION(kState_None, kState_WatchingPreRecorded) || 02432 TRANSITION(kState_None, kState_WatchingVideo) || 02433 TRANSITION(kState_None, kState_WatchingDVD) || 02434 TRANSITION(kState_None, kState_WatchingBD) || 02435 TRANSITION(kState_None, kState_WatchingRecording) || 02436 TRANSITION(kState_None, kState_WatchingLiveTV)) 02437 { 02438 if (!ctx->IsPIP()) 02439 GetMythUI()->DisableScreensaver(); 02440 MythMainWindow *mainWindow = GetMythMainWindow(); 02441 mainWindow->setBaseSize(player_bounds.size()); 02442 mainWindow->setMinimumSize( 02443 (db_use_fixed_size) ? player_bounds.size() : QSize(16, 16)); 02444 mainWindow->setMaximumSize( 02445 (db_use_fixed_size) ? player_bounds.size() : 02446 QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); 02447 mainWindow->setGeometry(player_bounds); 02448 mainWindow->ResizePainterWindow(player_bounds.size()); 02449 if (!weDisabledGUI) 02450 { 02451 weDisabledGUI = true; 02452 GetMythMainWindow()->PushDrawDisabled(); 02453 } 02454 DrawUnusedRects(); 02455 // we no longer need the contents of myWindow 02456 if (myWindow) 02457 myWindow->DeleteAllChildren(); 02458 02459 LOG(VB_GENERAL, LOG_INFO, LOC + "Main UI disabled."); 02460 } 02461 02462 LOG(VB_PLAYBACK, LOG_INFO, LOC + 02463 QString("HandleStateChange(%1) -- end") 02464 .arg(find_player_index(ctx))); 02465 } 02466 #undef TRANSITION 02467 #undef SET_NEXT 02468 #undef SET_LAST 02469 02475 bool TV::StartRecorder(PlayerContext *ctx, int maxWait) 02476 { 02477 RemoteEncoder *rec = ctx->recorder; 02478 maxWait = (maxWait <= 0) ? 40000 : maxWait; 02479 MythTimer t; 02480 t.start(); 02481 bool recording = false, ok = true; 02482 if (!rec) { 02483 LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid Remote Encoder"); 02484 SetErrored(ctx); 02485 return false; 02486 } 02487 while (!(recording = rec->IsRecording(&ok)) && 02488 !exitPlayerTimerId && t.elapsed() < maxWait) 02489 { 02490 if (!ok) 02491 { 02492 LOG(VB_GENERAL, LOG_ERR, LOC + "StartRecorder() -- " 02493 "lost contact with backend"); 02494 SetErrored(ctx); 02495 return false; 02496 } 02497 usleep(5000); 02498 } 02499 02500 if (!recording || exitPlayerTimerId) 02501 { 02502 if (!exitPlayerTimerId) 02503 LOG(VB_GENERAL, LOG_ERR, LOC + "StartRecorder() -- " 02504 "timed out waiting for recorder to start"); 02505 return false; 02506 } 02507 02508 LOG(VB_PLAYBACK, LOG_INFO, LOC + 02509 QString("StartRecorder(): took %1 ms to start recorder.") 02510 .arg(t.elapsed())); 02511 02512 return true; 02513 } 02514 02528 void TV::StopStuff(PlayerContext *mctx, PlayerContext *ctx, 02529 bool stopRingBuffer, bool stopPlayer, bool stopRecorder) 02530 { 02531 LOG(VB_PLAYBACK, LOG_INFO, 02532 LOC + QString("StopStuff() for player ctx %1 -- begin") 02533 .arg(find_player_index(ctx))); 02534 02535 SetActive(mctx, 0, false); 02536 02537 if (ctx->buffer) 02538 ctx->buffer->IgnoreWaitStates(true); 02539 02540 ctx->LockDeletePlayer(__FILE__, __LINE__); 02541 if (stopPlayer) 02542 ctx->StopPlaying(); 02543 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 02544 02545 if (stopRingBuffer) 02546 { 02547 LOG(VB_PLAYBACK, LOG_INFO, LOC + "StopStuff(): stopping ring buffer"); 02548 if (ctx->buffer) 02549 { 02550 ctx->buffer->StopReads(); 02551 ctx->buffer->Pause(); 02552 ctx->buffer->WaitForPause(); 02553 } 02554 } 02555 02556 if (stopPlayer) 02557 { 02558 LOG(VB_PLAYBACK, LOG_INFO, LOC + "StopStuff(): stopping player"); 02559 if (ctx == mctx) 02560 { 02561 for (uint i = 1; mctx && (i < player.size()); i++) 02562 StopStuff(mctx, GetPlayer(mctx,i), true, true, true); 02563 } 02564 } 02565 02566 if (stopRecorder) 02567 { 02568 LOG(VB_PLAYBACK, LOG_INFO, LOC + "StopStuff(): stopping recorder"); 02569 if (ctx->recorder) 02570 ctx->recorder->StopLiveTV(); 02571 } 02572 02573 LOG(VB_PLAYBACK, LOG_INFO, LOC + "StopStuff() -- end"); 02574 } 02575 02576 void TV::TeardownPlayer(PlayerContext *mctx, PlayerContext *ctx) 02577 { 02578 int ctx_index = find_player_index(ctx); 02579 02580 QString loc = LOC + QString("TeardownPlayer() player ctx %1") 02581 .arg(ctx_index); 02582 02583 if (!mctx || !ctx || ctx_index < 0) 02584 { 02585 LOG(VB_GENERAL, LOG_ERR, loc + "-- error"); 02586 return; 02587 } 02588 02589 LOG(VB_PLAYBACK, LOG_INFO, loc); 02590 02591 if (mctx != ctx) 02592 { 02593 if (ctx->HasPlayer()) 02594 { 02595 PIPRemovePlayer(mctx, ctx); 02596 ctx->SetPlayer(NULL); 02597 } 02598 02599 player.erase(player.begin() + ctx_index); 02600 delete ctx; 02601 if (mctx->IsPBP()) 02602 PBPRestartMainPlayer(mctx); 02603 SetActive(mctx, playerActive, false); 02604 return; 02605 } 02606 02607 ctx->TeardownPlayer(); 02608 } 02609 02610 void TV::timerEvent(QTimerEvent *te) 02611 { 02612 const int timer_id = te->timerId(); 02613 02614 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 02615 if (mctx->IsErrored()) 02616 { 02617 ReturnPlayerLock(mctx); 02618 return; 02619 } 02620 ReturnPlayerLock(mctx); 02621 02622 bool ignore = false; 02623 { 02624 QMutexLocker locker(&timerIdLock); 02625 ignore = 02626 (stateChangeTimerId.size() && 02627 stateChangeTimerId.find(timer_id) == stateChangeTimerId.end()); 02628 } 02629 if (ignore) 02630 return; // Always handle state changes first... 02631 02632 bool handled = true; 02633 if (timer_id == lcdTimerId) 02634 HandleLCDTimerEvent(); 02635 else if (timer_id == lcdVolumeTimerId) 02636 HandleLCDVolumeTimerEvent(); 02637 else if (timer_id == sleepTimerId) 02638 ShowOSDSleep(); 02639 else if (timer_id == sleepDialogTimerId) 02640 SleepDialogTimeout(); 02641 else if (timer_id == idleTimerId) 02642 ShowOSDIdle(); 02643 else if (timer_id == idleDialogTimerId) 02644 IdleDialogTimeout(); 02645 else if (timer_id == endOfPlaybackTimerId) 02646 HandleEndOfPlaybackTimerEvent(); 02647 else if (timer_id == embedCheckTimerId) 02648 HandleIsNearEndWhenEmbeddingTimerEvent(); 02649 else if (timer_id == endOfRecPromptTimerId) 02650 HandleEndOfRecordingExitPromptTimerEvent(); 02651 else if (timer_id == videoExitDialogTimerId) 02652 HandleVideoExitDialogTimerEvent(); 02653 else if (timer_id == pseudoChangeChanTimerId) 02654 HandlePseudoLiveTVTimerEvent(); 02655 else if (timer_id == speedChangeTimerId) 02656 HandleSpeedChangeTimerEvent(); 02657 else if (timer_id == pipChangeTimerId) 02658 HandlePxPTimerEvent(); 02659 else 02660 handled = false; 02661 02662 if (handled) 02663 return; 02664 02665 // Check if it matches a stateChangeTimerId 02666 PlayerContext *ctx = NULL; 02667 { 02668 QMutexLocker locker(&timerIdLock); 02669 TimerContextMap::iterator it = stateChangeTimerId.find(timer_id); 02670 if (it != stateChangeTimerId.end()) 02671 { 02672 KillTimer(timer_id); 02673 ctx = *it; 02674 stateChangeTimerId.erase(it); 02675 } 02676 } 02677 02678 if (ctx) 02679 { 02680 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 02681 bool still_exists = find_player_index(ctx) >= 0; 02682 02683 while (still_exists && !ctx->nextState.empty()) 02684 { 02685 HandleStateChange(mctx, ctx); 02686 if ((kState_None == ctx->GetState() || 02687 kState_Error == ctx->GetState()) && 02688 ((mctx != ctx) || jumpToProgram)) 02689 { 02690 ReturnPlayerLock(mctx); 02691 mctx = GetPlayerWriteLock(0, __FILE__, __LINE__); 02692 TeardownPlayer(mctx, ctx); 02693 still_exists = false; 02694 } 02695 } 02696 ReturnPlayerLock(mctx); 02697 handled = true; 02698 } 02699 02700 if (handled) 02701 return; 02702 02703 // Check if it matches a signalMonitorTimerId 02704 ctx = NULL; 02705 { 02706 QMutexLocker locker(&timerIdLock); 02707 TimerContextMap::iterator it = signalMonitorTimerId.find(timer_id); 02708 if (it != signalMonitorTimerId.end()) 02709 { 02710 KillTimer(timer_id); 02711 ctx = *it; 02712 signalMonitorTimerId.erase(it); 02713 } 02714 } 02715 02716 if (ctx) 02717 { 02718 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 02719 bool still_exists = find_player_index(ctx) >= 0; 02720 02721 if (still_exists && !ctx->lastSignalMsg.empty()) 02722 { // set last signal msg, so we get some feedback... 02723 UpdateOSDSignal(ctx, ctx->lastSignalMsg); 02724 ctx->lastSignalMsg.clear(); 02725 } 02726 UpdateOSDTimeoutMessage(ctx); 02727 02728 ReturnPlayerLock(mctx); 02729 handled = true; 02730 } 02731 02732 if (handled) 02733 return; 02734 02735 // Check if it matches a tvchainUpdateTimerId 02736 ctx = NULL; 02737 { 02738 QMutexLocker locker(&timerIdLock); 02739 TimerContextMap::iterator it = tvchainUpdateTimerId.find(timer_id); 02740 if (it != tvchainUpdateTimerId.end()) 02741 { 02742 KillTimer(timer_id); 02743 ctx = *it; 02744 tvchainUpdateTimerId.erase(it); 02745 } 02746 } 02747 02748 if (ctx) 02749 { 02750 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 02751 bool still_exists = find_player_index(ctx) >= 0; 02752 02753 if (still_exists) 02754 ctx->UpdateTVChain(); 02755 02756 ReturnPlayerLock(mctx); 02757 handled = true; 02758 } 02759 02760 if (handled) 02761 return; 02762 02763 // Check if it matches networkControlTimerId 02764 QString netCmd = QString::null; 02765 { 02766 QMutexLocker locker(&timerIdLock); 02767 if (timer_id == networkControlTimerId) 02768 { 02769 if (networkControlCommands.size()) 02770 netCmd = networkControlCommands.dequeue(); 02771 if (networkControlCommands.empty()) 02772 { 02773 KillTimer(networkControlTimerId); 02774 networkControlTimerId = 0; 02775 } 02776 } 02777 } 02778 02779 if (!netCmd.isEmpty()) 02780 { 02781 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 02782 ProcessNetworkControlCommand(actx, netCmd); 02783 ReturnPlayerLock(actx); 02784 handled = true; 02785 } 02786 02787 if (handled) 02788 return; 02789 02790 // Check if it matches exitPlayerTimerId 02791 if (timer_id == exitPlayerTimerId) 02792 { 02793 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 02794 02795 OSD *osd = GetOSDLock(mctx); 02796 if (osd) 02797 { 02798 osd->DialogQuit(); 02799 osd->HideAll(); 02800 } 02801 ReturnOSDLock(mctx, osd); 02802 02803 if (jumpToProgram && lastProgram) 02804 { 02805 if (!lastProgram->IsFileReadable()) 02806 { 02807 SetOSDMessage(mctx, tr("Last Program: %1 Doesn't Exist") 02808 .arg(lastProgram->GetTitle())); 02809 lastProgramStringList.clear(); 02810 SetLastProgram(NULL); 02811 LOG(VB_PLAYBACK, LOG_ERR, LOC + 02812 "Last Program File does not exist"); 02813 jumpToProgram = false; 02814 } 02815 else 02816 ForceNextStateNone(mctx); 02817 } 02818 else 02819 ForceNextStateNone(mctx); 02820 02821 ReturnPlayerLock(mctx); 02822 02823 QMutexLocker locker(&timerIdLock); 02824 KillTimer(exitPlayerTimerId); 02825 exitPlayerTimerId = 0; 02826 handled = true; 02827 } 02828 02829 if (handled) 02830 return; 02831 02832 if (timer_id == jumpMenuTimerId) 02833 { 02834 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 02835 if (actx) 02836 FillOSDMenuJumpRec(actx); 02837 ReturnPlayerLock(actx); 02838 02839 QMutexLocker locker(&timerIdLock); 02840 KillTimer(jumpMenuTimerId); 02841 jumpMenuTimerId = 0; 02842 handled = true; 02843 } 02844 02845 if (handled) 02846 return; 02847 02848 if (timer_id == switchToInputTimerId) 02849 { 02850 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 02851 if (switchToInputId) 02852 { 02853 uint tmp = switchToInputId; 02854 switchToInputId = 0; 02855 SwitchInputs(actx, tmp); 02856 } 02857 ReturnPlayerLock(actx); 02858 02859 QMutexLocker locker(&timerIdLock); 02860 KillTimer(switchToInputTimerId); 02861 switchToInputTimerId = 0; 02862 handled = true; 02863 } 02864 02865 if (handled) 02866 return; 02867 02868 if (timer_id == ccInputTimerId) 02869 { 02870 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 02871 // Clear closed caption input mode when timer expires 02872 if (ccInputMode) 02873 { 02874 ccInputMode = false; 02875 ClearInputQueues(actx, true); 02876 } 02877 ReturnPlayerLock(actx); 02878 02879 QMutexLocker locker(&timerIdLock); 02880 KillTimer(ccInputTimerId); 02881 ccInputTimerId = 0; 02882 handled = true; 02883 } 02884 02885 if (handled) 02886 return; 02887 02888 if (timer_id == asInputTimerId) 02889 { 02890 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 02891 // Clear closed caption input mode when timer expires 02892 if (asInputMode) 02893 { 02894 asInputMode = false; 02895 ClearInputQueues(actx, true); 02896 } 02897 ReturnPlayerLock(actx); 02898 02899 QMutexLocker locker(&timerIdLock); 02900 KillTimer(asInputTimerId); 02901 asInputTimerId = 0; 02902 handled = true; 02903 } 02904 02905 if (handled) 02906 return; 02907 02908 if (timer_id == queueInputTimerId) 02909 { 02910 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 02911 // Commit input when the OSD fades away 02912 if (HasQueuedChannel()) 02913 { 02914 OSD *osd = GetOSDLock(actx); 02915 if (osd && !osd->IsWindowVisible("osd_input")) 02916 CommitQueuedInput(actx); 02917 ReturnOSDLock(actx, osd); 02918 } 02919 ReturnPlayerLock(actx); 02920 02921 QMutexLocker locker(&timerIdLock); 02922 if (!queuedChanID && queuedChanNum.isEmpty() && queueInputTimerId) 02923 { 02924 KillTimer(queueInputTimerId); 02925 queueInputTimerId = 0; 02926 } 02927 handled = true; 02928 } 02929 02930 if (handled) 02931 return; 02932 02933 if (timer_id == browseTimerId) 02934 { 02935 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 02936 browsehelper->BrowseEnd(actx, false); 02937 ReturnPlayerLock(actx); 02938 handled = true; 02939 } 02940 02941 if (handled) 02942 return; 02943 02944 if (timer_id == updateOSDDebugTimerId) 02945 { 02946 bool update = false; 02947 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 02948 OSD *osd = GetOSDLock(actx); 02949 if (osd && osd->IsWindowVisible("osd_debug") && 02950 (StateIsLiveTV(actx->GetState()) || 02951 StateIsPlaying(actx->GetState()))) 02952 { 02953 update = true; 02954 } 02955 else 02956 { 02957 QMutexLocker locker(&timerIdLock); 02958 KillTimer(updateOSDDebugTimerId); 02959 updateOSDDebugTimerId = 0; 02960 actx->buffer->EnableBitrateMonitor(false); 02961 if (actx->player) 02962 actx->player->EnableFrameRateMonitor(false); 02963 } 02964 ReturnOSDLock(actx, osd); 02965 if (update) 02966 UpdateOSDDebug(actx); 02967 ReturnPlayerLock(actx); 02968 handled = true; 02969 } 02970 if (timer_id == updateOSDPosTimerId) 02971 { 02972 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 02973 OSD *osd = GetOSDLock(actx); 02974 if (osd && osd->IsWindowVisible("osd_status") && 02975 (StateIsLiveTV(actx->GetState()) || 02976 StateIsPlaying(actx->GetState()))) 02977 { 02978 osdInfo info; 02979 if (actx->CalcPlayerSliderPosition(info)) 02980 { 02981 osd->SetText("osd_status", info.text, kOSDTimeout_Ignore); 02982 osd->SetValues("osd_status", info.values, kOSDTimeout_Ignore); 02983 } 02984 } 02985 else 02986 SetUpdateOSDPosition(false); 02987 ReturnOSDLock(actx, osd); 02988 ReturnPlayerLock(actx); 02989 handled = true; 02990 } 02991 02992 if (handled) 02993 return; 02994 02995 if (timer_id == errorRecoveryTimerId) 02996 { 02997 bool error = false; 02998 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 02999 03000 if (mctx->IsPlayerErrored()) 03001 { 03002 if (mctx->IsPlayerRecoverable()) 03003 RestartMainPlayer(mctx); 03004 03005 if (mctx->IsPlayerDecoderErrored()) 03006 { 03007 LOG(VB_GENERAL, LOG_EMERG, LOC + 03008 QString("Serious hardware decoder error detected. " 03009 "Disabling hardware decoders.")); 03010 noHardwareDecoders = true; 03011 for (uint i = 0; i < player.size(); i++) 03012 player[i]->SetNoHardwareDecoders(); 03013 RestartMainPlayer(mctx); 03014 } 03015 } 03016 03017 if (mctx->IsRecorderErrored() || 03018 mctx->IsPlayerErrored() || 03019 mctx->IsErrored()) 03020 { 03021 SetExitPlayer(true, false); 03022 ForceNextStateNone(mctx); 03023 error = true; 03024 } 03025 03026 for (uint i = 0; i < player.size(); i++) 03027 { 03028 PlayerContext *ctx = GetPlayer(mctx, i); 03029 if (error || ctx->IsErrored()) 03030 ForceNextStateNone(ctx); 03031 } 03032 ReturnPlayerLock(mctx); 03033 03034 QMutexLocker locker(&timerIdLock); 03035 if (errorRecoveryTimerId) 03036 KillTimer(errorRecoveryTimerId); 03037 errorRecoveryTimerId = 03038 StartTimer(kErrorRecoveryCheckFrequency, __LINE__); 03039 } 03040 } 03041 03042 bool TV::HandlePxPTimerEvent(void) 03043 { 03044 QString cmd = QString::null; 03045 03046 { 03047 QMutexLocker locker(&timerIdLock); 03048 if (changePxP.empty()) 03049 { 03050 if (pipChangeTimerId) 03051 KillTimer(pipChangeTimerId); 03052 pipChangeTimerId = 0; 03053 return true; 03054 } 03055 cmd = changePxP.dequeue(); 03056 } 03057 03058 PlayerContext *mctx = GetPlayerWriteLock(0, __FILE__, __LINE__); 03059 PlayerContext *actx = GetPlayer(mctx, -1); 03060 03061 if (cmd == "TOGGLEPIPMODE") 03062 PxPToggleView(actx, false); 03063 else if (cmd == "TOGGLEPBPMODE") 03064 PxPToggleView(actx, true); 03065 else if (cmd == "CREATEPIPVIEW") 03066 PxPCreateView(actx, false); 03067 else if (cmd == "CREATEPBPVIEW") 03068 PxPCreateView(actx, true); 03069 else if (cmd == "SWAPPIP") 03070 { 03071 if (mctx != actx) 03072 PxPSwap(mctx, actx); 03073 else if (mctx && player.size() == 2) 03074 PxPSwap(mctx, GetPlayer(mctx,1)); 03075 } 03076 else if (cmd == "TOGGLEPIPSTATE") 03077 PxPToggleType(mctx, !mctx->IsPBP()); 03078 03079 ReturnPlayerLock(mctx); 03080 03081 QMutexLocker locker(&timerIdLock); 03082 03083 if (pipChangeTimerId) 03084 KillTimer(pipChangeTimerId); 03085 03086 if (changePxP.empty()) 03087 pipChangeTimerId = 0; 03088 else 03089 pipChangeTimerId = StartTimer(20, __LINE__); 03090 03091 return true; 03092 } 03093 03094 bool TV::HandleLCDTimerEvent(void) 03095 { 03096 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 03097 LCD *lcd = LCD::Get(); 03098 if (lcd) 03099 { 03100 float progress = 0.0f; 03101 QString lcd_time_string; 03102 bool showProgress = true; 03103 03104 if (StateIsLiveTV(GetState(actx))) 03105 ShowLCDChannelInfo(actx); 03106 03107 if (actx->buffer && actx->buffer->IsDVD()) 03108 { 03109 ShowLCDDVDInfo(actx); 03110 showProgress = !actx->buffer->IsInDiscMenuOrStillFrame(); 03111 } 03112 03113 if (showProgress) 03114 { 03115 osdInfo info; 03116 if (actx->CalcPlayerSliderPosition(info)) { 03117 progress = info.values["position"] * 0.001f; 03118 03119 lcd_time_string = info.text["playedtime"] + " / " + info.text["totaltime"]; 03120 // if the string is longer than the LCD width, remove all spaces 03121 if (lcd_time_string.length() > (int)lcd->getLCDWidth()) 03122 lcd_time_string.remove(' '); 03123 } 03124 } 03125 lcd->setChannelProgress(lcd_time_string, progress); 03126 } 03127 ReturnPlayerLock(actx); 03128 03129 QMutexLocker locker(&timerIdLock); 03130 KillTimer(lcdTimerId); 03131 lcdTimerId = StartTimer(kLCDTimeout, __LINE__); 03132 03133 return true; 03134 } 03135 03136 void TV::HandleLCDVolumeTimerEvent() 03137 { 03138 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 03139 LCD *lcd = LCD::Get(); 03140 if (lcd) 03141 { 03142 ShowLCDChannelInfo(actx); 03143 lcd->switchToChannel(lcdCallsign, lcdTitle, lcdSubtitle); 03144 } 03145 ReturnPlayerLock(actx); 03146 03147 QMutexLocker locker(&timerIdLock); 03148 KillTimer(lcdVolumeTimerId); 03149 } 03150 03151 int TV::StartTimer(int interval, int line) 03152 { 03153 int x = QObject::startTimer(interval); 03154 if (!x) 03155 { 03156 LOG(VB_GENERAL, LOG_ERR, LOC + 03157 QString("Failed to start timer on line %1 of %2") 03158 .arg(line).arg(__FILE__)); 03159 } 03160 return x; 03161 } 03162 03163 void TV::KillTimer(int id) 03164 { 03165 QObject::killTimer(id); 03166 } 03167 03168 void TV::ForceNextStateNone(PlayerContext *ctx) 03169 { 03170 ctx->ForceNextStateNone(); 03171 ScheduleStateChange(ctx); 03172 } 03173 03174 void TV::ScheduleStateChange(PlayerContext *ctx) 03175 { 03176 QMutexLocker locker(&timerIdLock); 03177 stateChangeTimerId[StartTimer(1, __LINE__)] = ctx; 03178 } 03179 03180 void TV::SetErrored(PlayerContext *ctx) 03181 { 03182 if (!ctx) 03183 return; 03184 QMutexLocker locker(&timerIdLock); 03185 ctx->errored = true; 03186 KillTimer(errorRecoveryTimerId); 03187 errorRecoveryTimerId = StartTimer(1, __LINE__); 03188 } 03189 03190 void TV::PrepToSwitchToRecordedProgram(PlayerContext *ctx, 03191 const ProgramInfo &p) 03192 { 03193 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Switching to program: %1") 03194 .arg(p.toString(ProgramInfo::kTitleSubtitle))); 03195 SetLastProgram(&p); 03196 PrepareToExitPlayer(ctx,__LINE__); 03197 jumpToProgram = true; 03198 SetExitPlayer(true, true); 03199 } 03200 03201 void TV::PrepareToExitPlayer(PlayerContext *ctx, int line, BookmarkAction bookmark) 03202 { 03203 bool bm_basic = 03204 (bookmark == kBookmarkAlways || 03205 (bookmark == kBookmarkAuto && db_playback_exit_prompt == 2)); 03206 bool bookmark_it = bm_basic && IsBookmarkAllowed(ctx); 03207 ctx->LockDeletePlayer(__FILE__, line); 03208 if (ctx->player) 03209 { 03210 if (bookmark_it) 03211 SetBookmark(ctx, 03212 (ctx->player->IsNearEnd() || getEndOfRecording()) 03213 && !StateIsRecording(GetState(ctx))); 03214 if (db_auto_set_watched) 03215 ctx->player->SetWatched(); 03216 } 03217 ctx->UnlockDeletePlayer(__FILE__, line); 03218 } 03219 03220 void TV::SetExitPlayer(bool set_it, bool wants_to) 03221 { 03222 QMutexLocker locker(&timerIdLock); 03223 if (set_it) 03224 { 03225 wantsToQuit = wants_to; 03226 if (!exitPlayerTimerId) 03227 exitPlayerTimerId = StartTimer(1, __LINE__); 03228 } 03229 else 03230 { 03231 if (exitPlayerTimerId) 03232 KillTimer(exitPlayerTimerId); 03233 exitPlayerTimerId = 0; 03234 wantsToQuit = wants_to; 03235 } 03236 } 03237 03238 void TV::SetUpdateOSDPosition(bool set_it) 03239 { 03240 QMutexLocker locker(&timerIdLock); 03241 if (set_it) 03242 { 03243 if (!updateOSDPosTimerId) 03244 updateOSDPosTimerId = StartTimer(500, __LINE__); 03245 } 03246 else 03247 { 03248 if (updateOSDPosTimerId) 03249 KillTimer(updateOSDPosTimerId); 03250 updateOSDPosTimerId = 0; 03251 } 03252 } 03253 03254 void TV::HandleEndOfPlaybackTimerEvent(void) 03255 { 03256 { 03257 QMutexLocker locker(&timerIdLock); 03258 if (endOfPlaybackTimerId) 03259 KillTimer(endOfPlaybackTimerId); 03260 endOfPlaybackTimerId = 0; 03261 } 03262 03263 bool is_playing = false; 03264 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 03265 for (uint i = 0; mctx && (i < player.size()); i++) 03266 { 03267 PlayerContext *ctx = GetPlayer(mctx, i); 03268 if (!StateIsPlaying(ctx->GetState())) 03269 continue; 03270 03271 if (ctx->IsPlayerPlaying()) 03272 { 03273 is_playing = true; 03274 continue; 03275 } 03276 03277 ForceNextStateNone(ctx); 03278 if (mctx == ctx) 03279 { 03280 endOfRecording = true; 03281 PrepareToExitPlayer(mctx, __LINE__); 03282 SetExitPlayer(true, true); 03283 } 03284 } 03285 ReturnPlayerLock(mctx); 03286 03287 if (is_playing) 03288 { 03289 QMutexLocker locker(&timerIdLock); 03290 endOfPlaybackTimerId = 03291 StartTimer(kEndOfPlaybackCheckFrequency, __LINE__); 03292 } 03293 } 03294 03295 void TV::HandleIsNearEndWhenEmbeddingTimerEvent(void) 03296 { 03297 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 03298 if (!StateIsLiveTV(GetState(actx))) 03299 { 03300 actx->LockDeletePlayer(__FILE__, __LINE__); 03301 bool toggle = actx->player && actx->player->IsEmbedding() && 03302 actx->player->IsNearEnd() && !actx->player->IsPaused(); 03303 actx->UnlockDeletePlayer(__FILE__, __LINE__); 03304 if (toggle) 03305 DoTogglePause(actx, true); 03306 } 03307 ReturnPlayerLock(actx); 03308 } 03309 03310 void TV::HandleEndOfRecordingExitPromptTimerEvent(void) 03311 { 03312 if (endOfRecording || inPlaylist || editmode || underNetworkControl || 03313 exitPlayerTimerId) 03314 { 03315 return; 03316 } 03317 03318 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 03319 OSD *osd = GetOSDLock(mctx); 03320 if (osd && osd->DialogVisible()) 03321 { 03322 ReturnOSDLock(mctx, osd); 03323 ReturnPlayerLock(mctx); 03324 return; 03325 } 03326 ReturnOSDLock(mctx, osd); 03327 03328 bool do_prompt = false; 03329 mctx->LockDeletePlayer(__FILE__, __LINE__); 03330 if (mctx->GetState() == kState_WatchingPreRecorded && mctx->player) 03331 { 03332 if (!mctx->player->IsNearEnd()) 03333 jumped_back = false; 03334 03335 do_prompt = mctx->player->IsNearEnd() && !jumped_back && 03336 !mctx->player->IsEmbedding() && !mctx->player->IsPaused(); 03337 } 03338 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 03339 03340 if (do_prompt) 03341 ShowOSDPromptDeleteRecording(mctx, tr("End Of Recording")); 03342 03343 ReturnPlayerLock(mctx); 03344 } 03345 03346 void TV::HandleVideoExitDialogTimerEvent(void) 03347 { 03348 { 03349 QMutexLocker locker(&timerIdLock); 03350 if (videoExitDialogTimerId) 03351 KillTimer(videoExitDialogTimerId); 03352 videoExitDialogTimerId = 0; 03353 } 03354 03355 // disable dialog and exit playback after timeout 03356 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 03357 OSD *osd = GetOSDLock(mctx); 03358 if (!osd || !osd->DialogVisible(OSD_DLG_VIDEOEXIT)) 03359 { 03360 ReturnOSDLock(mctx, osd); 03361 ReturnPlayerLock(mctx); 03362 return; 03363 } 03364 if (osd) 03365 osd->DialogQuit(); 03366 ReturnOSDLock(mctx, osd); 03367 DoTogglePause(mctx, true); 03368 ClearOSD(mctx); 03369 PrepareToExitPlayer(mctx, __LINE__); 03370 ReturnPlayerLock(mctx); 03371 03372 requestDelete = false; 03373 SetExitPlayer(true, true); 03374 } 03375 03376 void TV::HandlePseudoLiveTVTimerEvent(void) 03377 { 03378 { 03379 QMutexLocker locker(&timerIdLock); 03380 KillTimer(pseudoChangeChanTimerId); 03381 pseudoChangeChanTimerId = 0; 03382 } 03383 03384 bool restartTimer = false; 03385 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 03386 for (uint i = 0; mctx && (i < player.size()); i++) 03387 { 03388 PlayerContext *ctx = GetPlayer(mctx, i); 03389 if (kPseudoChangeChannel != ctx->pseudoLiveTVState) 03390 continue; 03391 03392 if (ctx->InStateChange()) 03393 { 03394 restartTimer = true; 03395 continue; 03396 } 03397 03398 LOG(VB_CHANNEL, LOG_INFO, 03399 QString("REC_PROGRAM -- channel change %1").arg(i)); 03400 03401 uint chanid = ctx->pseudoLiveTVRec->GetChanID(); 03402 QString channum = ctx->pseudoLiveTVRec->GetChanNum(); 03403 StringDeque tmp = ctx->prevChan; 03404 03405 ctx->prevChan.clear(); 03406 ChangeChannel(ctx, chanid, channum); 03407 ctx->prevChan = tmp; 03408 ctx->pseudoLiveTVState = kPseudoRecording; 03409 } 03410 ReturnPlayerLock(mctx); 03411 03412 if (restartTimer) 03413 { 03414 QMutexLocker locker(&timerIdLock); 03415 if (!pseudoChangeChanTimerId) 03416 pseudoChangeChanTimerId = StartTimer(25, __LINE__); 03417 } 03418 } 03419 03420 void TV::SetSpeedChangeTimer(uint when, int line) 03421 { 03422 QMutexLocker locker(&timerIdLock); 03423 if (speedChangeTimerId) 03424 KillTimer(speedChangeTimerId); 03425 speedChangeTimerId = StartTimer(when, line); 03426 } 03427 03428 void TV::HandleSpeedChangeTimerEvent(void) 03429 { 03430 { 03431 QMutexLocker locker(&timerIdLock); 03432 if (speedChangeTimerId) 03433 KillTimer(speedChangeTimerId); 03434 speedChangeTimerId = StartTimer(kSpeedChangeCheckFrequency, __LINE__); 03435 } 03436 03437 bool update_msg = false; 03438 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 03439 for (uint i = 0; actx && (i < player.size()); i++) 03440 { 03441 PlayerContext *ctx = GetPlayer(actx, i); 03442 update_msg |= ctx->HandlePlayerSpeedChangeFFRew() && (ctx == actx); 03443 } 03444 ReturnPlayerLock(actx); 03445 03446 actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 03447 for (uint i = 0; actx && (i < player.size()); i++) 03448 { 03449 PlayerContext *ctx = GetPlayer(actx, i); 03450 update_msg |= ctx->HandlePlayerSpeedChangeEOF() && (ctx == actx); 03451 } 03452 03453 if (update_msg) 03454 { 03455 UpdateOSDSeekMessage(actx, actx->GetPlayMessage(), kOSDTimeout_Med); 03456 } 03457 ReturnPlayerLock(actx); 03458 } 03459 03461 bool TV::eventFilter(QObject *o, QEvent *e) 03462 { 03463 // We want to intercept all resize events sent to the main window 03464 if ((e->type() == QEvent::Resize)) 03465 return (GetMythMainWindow()!= o) ? false : event(e); 03466 03467 // Intercept keypress events unless they need to be handled by a main UI 03468 // screen (e.g. GuideGrid, ProgramFinder) 03469 if (QEvent::KeyPress == e->type()) 03470 return ignoreKeyPresses ? false : event(e); 03471 03472 if (e->type() == MythEvent::MythEventMessage || 03473 e->type() == MythEvent::MythUserMessage || 03474 e->type() == MythEvent::kUpdateTvProgressEventType) 03475 { 03476 customEvent(e); 03477 return true; 03478 } 03479 03480 switch (e->type()) 03481 { 03482 case QEvent::Paint: 03483 case QEvent::UpdateRequest: 03484 case QEvent::Enter: 03485 { 03486 event(e); 03487 return false; 03488 } 03489 default: 03490 return false; 03491 } 03492 } 03493 03495 bool TV::event(QEvent *e) 03496 { 03497 if (QEvent::Resize == e->type()) 03498 { 03499 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 03500 mctx->LockDeletePlayer(__FILE__, __LINE__); 03501 if (mctx->player) 03502 mctx->player->WindowResized(((const QResizeEvent*) e)->size()); 03503 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 03504 ReturnPlayerLock(mctx); 03505 return true; 03506 } 03507 03508 if (QEvent::KeyPress == e->type()) 03509 { 03510 bool handled = false; 03511 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 03512 if (actx->HasPlayer()) 03513 handled = ProcessKeypress(actx, (QKeyEvent *)e); 03514 ReturnPlayerLock(actx); 03515 if (handled) 03516 return true; 03517 } 03518 03519 switch (e->type()) 03520 { 03521 case QEvent::Paint: 03522 case QEvent::UpdateRequest: 03523 case QEvent::Enter: 03524 DrawUnusedRects(); 03525 return true; 03526 default: 03527 break; 03528 } 03529 03530 return QObject::event(e); 03531 } 03532 03533 bool TV::HandleTrackAction(PlayerContext *ctx, const QString &action) 03534 { 03535 ctx->LockDeletePlayer(__FILE__, __LINE__); 03536 if (!ctx->player) 03537 { 03538 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 03539 return false; 03540 } 03541 03542 bool handled = true; 03543 03544 if (action == ACTION_TOGGLEEXTTEXT) 03545 ctx->player->ToggleCaptions(kTrackTypeTextSubtitle); 03546 else if (ACTION_ENABLEEXTTEXT == action) 03547 ctx->player->EnableCaptions(kDisplayTextSubtitle); 03548 else if (ACTION_DISABLEEXTTEXT == action) 03549 ctx->player->DisableCaptions(kDisplayTextSubtitle); 03550 else if (ACTION_ENABLEFORCEDSUBS == action) 03551 ctx->player->SetAllowForcedSubtitles(true); 03552 else if (ACTION_DISABLEFORCEDSUBS == action) 03553 ctx->player->SetAllowForcedSubtitles(false); 03554 else if (action == ACTION_ENABLESUBS) 03555 ctx->player->SetCaptionsEnabled(true, true); 03556 else if (action == ACTION_DISABLESUBS) 03557 ctx->player->SetCaptionsEnabled(false, true); 03558 else if (action == ACTION_TOGGLESUBS && !browsehelper->IsBrowsing()) 03559 { 03560 if (ccInputMode) 03561 { 03562 bool valid = false; 03563 int page = GetQueuedInputAsInt(&valid, 16); 03564 if (vbimode == VBIMode::PAL_TT && valid) 03565 ctx->player->SetTeletextPage(page); 03566 else if (vbimode == VBIMode::NTSC_CC) 03567 ctx->player->SetTrack(kTrackTypeCC608, 03568 max(min(page - 1, 1), 0)); 03569 03570 ClearInputQueues(ctx, true); 03571 03572 QMutexLocker locker(&timerIdLock); 03573 ccInputMode = false; 03574 if (ccInputTimerId) 03575 { 03576 KillTimer(ccInputTimerId); 03577 ccInputTimerId = 0; 03578 } 03579 } 03580 else if (ctx->player->GetCaptionMode() & kDisplayNUVTeletextCaptions) 03581 { 03582 ClearInputQueues(ctx, false); 03583 AddKeyToInputQueue(ctx, 0); 03584 03585 QMutexLocker locker(&timerIdLock); 03586 ccInputMode = true; 03587 asInputMode = false; 03588 ccInputTimerId = StartTimer(kInputModeTimeout, __LINE__); 03589 if (asInputTimerId) 03590 { 03591 KillTimer(asInputTimerId); 03592 asInputTimerId = 0; 03593 } 03594 } 03595 else 03596 { 03597 ctx->player->ToggleCaptions(); 03598 } 03599 } 03600 else if (action.left(6) == "TOGGLE") 03601 { 03602 int type = to_track_type(action.mid(6)); 03603 if (type == kTrackTypeTeletextMenu) 03604 ctx->player->EnableTeletext(); 03605 else if (type >= kTrackTypeSubtitle) 03606 ctx->player->ToggleCaptions(type); 03607 else 03608 handled = false; 03609 } 03610 else if (action.left(6) == "SELECT") 03611 { 03612 int type = to_track_type(action.mid(6)); 03613 int num = action.section("_", -1).toInt(); 03614 if (type >= kTrackTypeAudio) 03615 ctx->player->SetTrack(type, num); 03616 else 03617 handled = false; 03618 } 03619 else if (action.left(4) == "NEXT" || action.left(4) == "PREV") 03620 { 03621 int dir = (action.left(4) == "NEXT") ? +1 : -1; 03622 int type = to_track_type(action.mid(4)); 03623 if (type >= kTrackTypeAudio) 03624 ctx->player->ChangeTrack(type, dir); 03625 else if (action.right(2) == "CC") 03626 ctx->player->ChangeCaptionTrack(dir); 03627 else 03628 handled = false; 03629 } 03630 else 03631 handled = false; 03632 03633 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 03634 03635 return handled; 03636 } 03637 03638 static bool has_action(QString action, const QStringList &actions) 03639 { 03640 QStringList::const_iterator it; 03641 for (it = actions.begin(); it != actions.end(); ++it) 03642 { 03643 if (action == *it) 03644 return true; 03645 } 03646 return false; 03647 } 03648 03649 bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 03650 { 03651 bool ignoreKeys = actx->IsPlayerChangingBuffers(); 03652 #if DEBUG_ACTIONS 03653 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("ProcessKeypress() ignoreKeys: %1") 03654 .arg(ignoreKeys)); 03655 #endif // DEBUG_ACTIONS 03656 03657 if (idleTimerId) 03658 { 03659 KillTimer(idleTimerId); 03660 idleTimerId = StartTimer(db_idle_timeout, __LINE__); 03661 } 03662 03663 QStringList actions; 03664 bool handled = false; 03665 03666 if (ignoreKeys) 03667 { 03668 handled = GetMythMainWindow()->TranslateKeyPress( 03669 "TV Playback", e, actions); 03670 03671 if (handled || actions.isEmpty()) 03672 return true; 03673 03674 bool esc = has_action("ESCAPE", actions) || 03675 has_action("BACK", actions); 03676 bool pause = has_action(ACTION_PAUSE, actions); 03677 bool play = has_action(ACTION_PLAY, actions); 03678 03679 if ((!esc || browsehelper->IsBrowsing()) && !pause && !play) 03680 return false; 03681 } 03682 03683 OSD *osd = GetOSDLock(actx); 03684 if (osd && osd->DialogVisible()) 03685 { 03686 osd->DialogHandleKeypress(e); 03687 handled = true; 03688 } 03689 ReturnOSDLock(actx, osd); 03690 03691 if (editmode && !handled) 03692 { 03693 handled |= GetMythMainWindow()->TranslateKeyPress( 03694 "TV Editing", e, actions); 03695 03696 if (!handled) 03697 { 03698 if (has_action("MENU", actions)) 03699 { 03700 ShowOSDCutpoint(actx, "EDIT_CUT_POINTS"); 03701 handled = true; 03702 } 03703 if (has_action("ESCAPE", actions)) 03704 { 03705 if (!actx->player->IsCutListSaved()) 03706 ShowOSDCutpoint(actx, "EXIT_EDIT_MODE"); 03707 else 03708 { 03709 actx->LockDeletePlayer(__FILE__, __LINE__); 03710 if (actx->player) 03711 actx->player->DisableEdit(0); 03712 actx->UnlockDeletePlayer(__FILE__, __LINE__); 03713 } 03714 handled = true; 03715 } 03716 else 03717 { 03718 actx->LockDeletePlayer(__FILE__, __LINE__); 03719 int64_t current_frame = actx->player->GetFramesPlayed(); 03720 actx->UnlockDeletePlayer(__FILE__, __LINE__); 03721 if ((has_action(ACTION_SELECT, actions)) && 03722 (actx->player->IsInDelete(current_frame)) && 03723 (!(actx->player->HasTemporaryMark()))) 03724 { 03725 ShowOSDCutpoint(actx, "EDIT_CUT_REGION"); 03726 handled = true; 03727 } 03728 else 03729 handled |= actx->player->HandleProgramEditorActions( 03730 actions, current_frame); 03731 } 03732 } 03733 if (handled) 03734 editmode = actx->player->GetEditMode(); 03735 } 03736 03737 if (handled) 03738 return true; 03739 03740 // If text is already queued up, be more lax on what is ok. 03741 // This allows hex teletext entry and minor channel entry. 03742 const QString txt = e->text(); 03743 if (HasQueuedInput() && (1 == txt.length())) 03744 { 03745 bool ok = false; 03746 txt.toInt(&ok, 16); 03747 if (ok || txt=="_" || txt=="-" || txt=="#" || txt==".") 03748 { 03749 AddKeyToInputQueue(actx, txt.at(0).toLatin1()); 03750 return true; 03751 } 03752 } 03753 03754 // Teletext menu 03755 actx->LockDeletePlayer(__FILE__, __LINE__); 03756 if (actx->player && (actx->player->GetCaptionMode() == kDisplayTeletextMenu)) 03757 { 03758 QStringList tt_actions; 03759 handled = GetMythMainWindow()->TranslateKeyPress( 03760 "Teletext Menu", e, tt_actions); 03761 03762 if (!handled && !tt_actions.isEmpty()) 03763 { 03764 for (int i = 0; i < tt_actions.size(); i++) 03765 { 03766 if (actx->player->HandleTeletextAction(tt_actions[i])) 03767 { 03768 actx->UnlockDeletePlayer(__FILE__, __LINE__); 03769 return true; 03770 } 03771 } 03772 } 03773 } 03774 03775 // Interactive television 03776 if (actx->player && actx->player->GetInteractiveTV()) 03777 { 03778 QStringList itv_actions; 03779 handled = GetMythMainWindow()->TranslateKeyPress( 03780 "TV Playback", e, itv_actions); 03781 03782 if (!handled && !itv_actions.isEmpty()) 03783 { 03784 for (int i = 0; i < itv_actions.size(); i++) 03785 { 03786 if (actx->player->ITVHandleAction(itv_actions[i])) 03787 { 03788 actx->UnlockDeletePlayer(__FILE__, __LINE__); 03789 return true; 03790 } 03791 } 03792 } 03793 } 03794 actx->UnlockDeletePlayer(__FILE__, __LINE__); 03795 03796 handled = GetMythMainWindow()->TranslateKeyPress( 03797 "TV Playback", e, actions); 03798 03799 if (handled || actions.isEmpty()) 03800 return true; 03801 03802 handled = false; 03803 03804 bool isDVD = actx->buffer && actx->buffer->IsDVD(); 03805 bool isMenuOrStill = actx->buffer->IsInDiscMenuOrStillFrame(); 03806 03807 handled = handled || BrowseHandleAction(actx, actions); 03808 handled = handled || ManualZoomHandleAction(actx, actions); 03809 handled = handled || PictureAttributeHandleAction(actx, actions); 03810 handled = handled || TimeStretchHandleAction(actx, actions); 03811 handled = handled || AudioSyncHandleAction(actx, actions); 03812 handled = handled || SubtitleZoomHandleAction(actx, actions); 03813 handled = handled || DiscMenuHandleAction(actx, actions); 03814 handled = handled || ActiveHandleAction( 03815 actx, actions, isDVD, isMenuOrStill); 03816 handled = handled || ToggleHandleAction(actx, actions, isDVD); 03817 handled = handled || PxPHandleAction(actx, actions); 03818 handled = handled || FFRewHandleAction(actx, actions); 03819 handled = handled || ActivePostQHandleAction(actx, actions); 03820 03821 #if DEBUG_ACTIONS 03822 for (uint i = 0; i < actions.size(); ++i) 03823 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("handled(%1) actions[%2](%3)") 03824 .arg(handled).arg(i).arg(actions[i])); 03825 #endif // DEBUG_ACTIONS 03826 03827 if (handled) 03828 return true; 03829 03830 if (!handled) 03831 { 03832 for (int i = 0; i < actions.size() && !handled; i++) 03833 { 03834 QString action = actions[i]; 03835 bool ok = false; 03836 int val = action.toInt(&ok); 03837 03838 if (ok) 03839 { 03840 AddKeyToInputQueue(actx, '0' + val); 03841 handled = true; 03842 } 03843 } 03844 } 03845 03846 return true; 03847 } 03848 03849 bool TV::BrowseHandleAction(PlayerContext *ctx, const QStringList &actions) 03850 { 03851 if (!browsehelper->IsBrowsing()) 03852 return false; 03853 03854 bool handled = true; 03855 03856 if (has_action(ACTION_UP, actions) || has_action(ACTION_CHANNELUP, actions)) 03857 browsehelper->BrowseDispInfo(ctx, BROWSE_UP); 03858 else if (has_action(ACTION_DOWN, actions) || has_action(ACTION_CHANNELDOWN, actions)) 03859 browsehelper->BrowseDispInfo(ctx, BROWSE_DOWN); 03860 else if (has_action(ACTION_LEFT, actions)) 03861 browsehelper->BrowseDispInfo(ctx, BROWSE_LEFT); 03862 else if (has_action(ACTION_RIGHT, actions)) 03863 browsehelper->BrowseDispInfo(ctx, BROWSE_RIGHT); 03864 else if (has_action("NEXTFAV", actions)) 03865 browsehelper->BrowseDispInfo(ctx, BROWSE_FAVORITE); 03866 else if (has_action(ACTION_SELECT, actions)) 03867 { 03868 browsehelper->BrowseEnd(ctx, true); 03869 } 03870 else if (has_action(ACTION_CLEAROSD, actions) || 03871 has_action("ESCAPE", actions) || 03872 has_action("BACK", actions) || 03873 has_action("TOGGLEBROWSE", actions)) 03874 { 03875 browsehelper->BrowseEnd(ctx, false); 03876 } 03877 else if (has_action(ACTION_TOGGLERECORD, actions)) 03878 ToggleRecord(ctx); 03879 else 03880 { 03881 handled = false; 03882 QStringList::const_iterator it = actions.begin(); 03883 for (; it != actions.end(); ++it) 03884 { 03885 if ((*it).length() == 1 && (*it)[0].isDigit()) 03886 { 03887 AddKeyToInputQueue(ctx, (*it)[0].toLatin1()); 03888 handled = true; 03889 } 03890 } 03891 } 03892 03893 // only pass-through actions listed below 03894 return handled || 03895 !(has_action(ACTION_VOLUMEDOWN, actions) || 03896 has_action(ACTION_VOLUMEUP, actions) || 03897 has_action("STRETCHINC", actions) || 03898 has_action("STRETCHDEC", actions) || 03899 has_action(ACTION_MUTEAUDIO, actions) || 03900 has_action("CYCLEAUDIOCHAN", actions) || 03901 has_action("TOGGLEASPECT", actions) || 03902 has_action("TOGGLEPIPMODE", actions) || 03903 has_action("TOGGLEPIPSTATE", actions) || 03904 has_action("NEXTPIPWINDOW", actions) || 03905 has_action("CREATEPIPVIEW", actions) || 03906 has_action("CREATEPBPVIEW", actions) || 03907 has_action("SWAPPIP", actions)); 03908 } 03909 03910 bool TV::ManualZoomHandleAction(PlayerContext *actx, const QStringList &actions) 03911 { 03912 if (!zoomMode) 03913 return false; 03914 03915 actx->LockDeletePlayer(__FILE__, __LINE__); 03916 if (!actx->player) 03917 { 03918 actx->UnlockDeletePlayer(__FILE__, __LINE__); 03919 return false; 03920 } 03921 03922 bool end_manual_zoom = false; 03923 bool handled = true; 03924 if (has_action(ACTION_UP, actions) || 03925 has_action(ACTION_CHANNELUP, actions)) 03926 { 03927 actx->player->Zoom(kZoomUp); 03928 } 03929 else if (has_action(ACTION_DOWN, actions) || 03930 has_action(ACTION_CHANNELDOWN, actions)) 03931 { 03932 actx->player->Zoom(kZoomDown); 03933 } 03934 else if (has_action(ACTION_LEFT, actions)) 03935 actx->player->Zoom(kZoomLeft); 03936 else if (has_action(ACTION_RIGHT, actions)) 03937 actx->player->Zoom(kZoomRight); 03938 else if (has_action(ACTION_VOLUMEUP, actions)) 03939 actx->player->Zoom(kZoomAspectUp); 03940 else if (has_action(ACTION_VOLUMEDOWN, actions)) 03941 actx->player->Zoom(kZoomAspectDown); 03942 else if (has_action("ESCAPE", actions) || 03943 has_action("BACK", actions)) 03944 { 03945 actx->player->Zoom(kZoomHome); 03946 end_manual_zoom = true; 03947 } 03948 else if (has_action(ACTION_SELECT, actions)) 03949 SetManualZoom(actx, false, tr("Zoom Committed")); 03950 else if (has_action(ACTION_JUMPFFWD, actions)) 03951 actx->player->Zoom(kZoomIn); 03952 else if (has_action(ACTION_JUMPRWND, actions)) 03953 actx->player->Zoom(kZoomOut); 03954 else 03955 { 03956 // only pass-through actions listed below 03957 handled = !(has_action("STRETCHINC", actions) || 03958 has_action("STRETCHDEC", actions) || 03959 has_action(ACTION_MUTEAUDIO, actions) || 03960 has_action("CYCLEAUDIOCHAN", actions) || 03961 has_action(ACTION_PAUSE, actions) || 03962 has_action(ACTION_CLEAROSD, actions)); 03963 } 03964 actx->UnlockDeletePlayer(__FILE__, __LINE__); 03965 03966 if (end_manual_zoom) 03967 SetManualZoom(actx, false, tr("Zoom Ignored")); 03968 03969 return handled; 03970 } 03971 03972 bool TV::PictureAttributeHandleAction(PlayerContext *ctx, 03973 const QStringList &actions) 03974 { 03975 if (!adjustingPicture) 03976 return false; 03977 03978 bool handled = true; 03979 if (has_action(ACTION_LEFT, actions)) 03980 { 03981 DoChangePictureAttribute(ctx, adjustingPicture, 03982 adjustingPictureAttribute, false); 03983 } 03984 else if (has_action(ACTION_RIGHT, actions)) 03985 { 03986 DoChangePictureAttribute(ctx, adjustingPicture, 03987 adjustingPictureAttribute, true); 03988 } 03989 else 03990 handled = false; 03991 03992 return handled; 03993 } 03994 03995 bool TV::TimeStretchHandleAction(PlayerContext *ctx, 03996 const QStringList &actions) 03997 { 03998 if (!stretchAdjustment) 03999 return false; 04000 04001 bool handled = true; 04002 04003 if (has_action(ACTION_LEFT, actions)) 04004 ChangeTimeStretch(ctx, -1); 04005 else if (has_action(ACTION_RIGHT, actions)) 04006 ChangeTimeStretch(ctx, 1); 04007 else if (has_action(ACTION_DOWN, actions)) 04008 ChangeTimeStretch(ctx, -5); 04009 else if (has_action(ACTION_UP, actions)) 04010 ChangeTimeStretch(ctx, 5); 04011 else if (has_action("ADJUSTSTRETCH", actions)) 04012 ClearOSD(ctx); 04013 else 04014 handled = false; 04015 04016 return handled; 04017 } 04018 04019 bool TV::AudioSyncHandleAction(PlayerContext *ctx, 04020 const QStringList &actions) 04021 { 04022 if (!audiosyncAdjustment) 04023 return false; 04024 04025 bool handled = true; 04026 04027 if (has_action(ACTION_LEFT, actions)) 04028 ChangeAudioSync(ctx, -1); 04029 else if (has_action(ACTION_RIGHT, actions)) 04030 ChangeAudioSync(ctx, 1); 04031 else if (has_action(ACTION_UP, actions)) 04032 ChangeAudioSync(ctx, -10); 04033 else if (has_action(ACTION_DOWN, actions)) 04034 ChangeAudioSync(ctx, 10); 04035 else if (has_action(ACTION_TOGGELAUDIOSYNC, actions)) 04036 ClearOSD(ctx); 04037 else 04038 handled = false; 04039 04040 return handled; 04041 } 04042 04043 bool TV::SubtitleZoomHandleAction(PlayerContext *ctx, 04044 const QStringList &actions) 04045 { 04046 if (!subtitleZoomAdjustment) 04047 return false; 04048 04049 bool handled = true; 04050 04051 if (has_action(ACTION_LEFT, actions)) 04052 ChangeSubtitleZoom(ctx, -1); 04053 else if (has_action(ACTION_RIGHT, actions)) 04054 ChangeSubtitleZoom(ctx, 1); 04055 else if (has_action(ACTION_UP, actions)) 04056 ChangeSubtitleZoom(ctx, -10); 04057 else if (has_action(ACTION_DOWN, actions)) 04058 ChangeSubtitleZoom(ctx, 10); 04059 else if (has_action(ACTION_TOGGLESUBTITLEZOOM, actions)) 04060 ClearOSD(ctx); 04061 else 04062 handled = false; 04063 04064 return handled; 04065 } 04066 04067 bool TV::DiscMenuHandleAction(PlayerContext *ctx, const QStringList &actions) 04068 { 04069 int64_t pts = 0; 04070 VideoOutput *output = ctx->player->GetVideoOutput(); 04071 if (output) 04072 { 04073 VideoFrame *frame = output->GetLastShownFrame(); 04074 if (frame) 04075 { 04076 // convert timecode (msec) to pts (90kHz) 04077 pts = (int64_t)(frame->timecode * 90); 04078 } 04079 } 04080 return ctx->buffer->HandleAction(actions, pts); 04081 } 04082 04083 bool TV::Handle3D(PlayerContext *ctx, const QString &action) 04084 { 04085 ctx->LockDeletePlayer(__FILE__, __LINE__); 04086 if (ctx->player && ctx->player->GetVideoOutput() && 04087 ctx->player->GetVideoOutput()->StereoscopicModesAllowed()) 04088 { 04089 StereoscopicMode mode = kStereoscopicModeNone; 04090 if (ACTION_3DSIDEBYSIDE == action) 04091 mode = kStereoscopicModeSideBySide; 04092 else if (ACTION_3DSIDEBYSIDEDISCARD == action) 04093 mode = kStereoscopicModeSideBySideDiscard; 04094 else if (ACTION_3DTOPANDBOTTOM == action) 04095 mode = kStereoscopicModeTopAndBottom; 04096 else if (ACTION_3DTOPANDBOTTOMDISCARD == action) 04097 mode = kStereoscopicModeTopAndBottomDiscard; 04098 ctx->player->GetVideoOutput()->SetStereoscopicMode(mode); 04099 SetOSDMessage(ctx, StereoscopictoString(mode)); 04100 } 04101 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04102 return true; 04103 } 04104 04105 bool TV::ActiveHandleAction(PlayerContext *ctx, 04106 const QStringList &actions, 04107 bool isDVD, bool isDVDStill) 04108 { 04109 bool handled = true; 04110 04111 if (has_action("SKIPCOMMERCIAL", actions) && !isDVD) 04112 DoSkipCommercials(ctx, 1); 04113 else if (has_action("SKIPCOMMBACK", actions) && !isDVD) 04114 DoSkipCommercials(ctx, -1); 04115 else if (has_action("QUEUETRANSCODE", actions) && !isDVD) 04116 DoQueueTranscode(ctx, "Default"); 04117 else if (has_action("QUEUETRANSCODE_AUTO", actions) && !isDVD) 04118 DoQueueTranscode(ctx, "Autodetect"); 04119 else if (has_action("QUEUETRANSCODE_HIGH", actions) && !isDVD) 04120 DoQueueTranscode(ctx, "High Quality"); 04121 else if (has_action("QUEUETRANSCODE_MEDIUM", actions) && !isDVD) 04122 DoQueueTranscode(ctx, "Medium Quality"); 04123 else if (has_action("QUEUETRANSCODE_LOW", actions) && !isDVD) 04124 DoQueueTranscode(ctx, "Low Quality"); 04125 else if (has_action(ACTION_PLAY, actions)) 04126 DoPlay(ctx); 04127 else if (has_action(ACTION_PAUSE, actions)) 04128 DoTogglePause(ctx, true); 04129 else if (has_action("SPEEDINC", actions) && !isDVDStill) 04130 ChangeSpeed(ctx, 1); 04131 else if (has_action("SPEEDDEC", actions) && !isDVDStill) 04132 ChangeSpeed(ctx, -1); 04133 else if (has_action("ADJUSTSTRETCH", actions)) 04134 ChangeTimeStretch(ctx, 0); // just display 04135 else if (has_action("CYCLECOMMSKIPMODE",actions) && !isDVD) 04136 SetAutoCommercialSkip(ctx, kCommSkipIncr); 04137 else if (has_action("NEXTSCAN", actions)) 04138 { 04139 QString msg = QString::null; 04140 ctx->LockDeletePlayer(__FILE__, __LINE__); 04141 if (ctx->player) 04142 { 04143 ctx->player->NextScanType(); 04144 msg = toString(ctx->player->GetScanType()); 04145 } 04146 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04147 04148 if (!msg.isEmpty()) 04149 SetOSDMessage(ctx, msg); 04150 } 04151 else if (has_action(ACTION_SEEKARB, actions) && !isDVD) 04152 { 04153 if (asInputMode) 04154 { 04155 ClearInputQueues(ctx, true); 04156 SetOSDText(ctx, "osd_input", "osd_number_entry", tr("Seek:"), 04157 kOSDTimeout_Med); 04158 04159 QMutexLocker locker(&timerIdLock); 04160 asInputMode = false; 04161 if (asInputTimerId) 04162 { 04163 KillTimer(asInputTimerId); 04164 asInputTimerId = 0; 04165 } 04166 } 04167 else 04168 { 04169 ClearInputQueues(ctx, false); 04170 AddKeyToInputQueue(ctx, 0); 04171 04172 QMutexLocker locker(&timerIdLock); 04173 asInputMode = true; 04174 ccInputMode = false; 04175 asInputTimerId = StartTimer(kInputModeTimeout, __LINE__); 04176 if (ccInputTimerId) 04177 { 04178 KillTimer(ccInputTimerId); 04179 ccInputTimerId = 0; 04180 } 04181 } 04182 } 04183 else if (has_action(ACTION_JUMPRWND, actions)) 04184 DoJumpRWND(ctx); 04185 else if (has_action(ACTION_JUMPFFWD, actions)) 04186 DoJumpFFWD(ctx); 04187 else if (has_action(ACTION_JUMPBKMRK, actions)) 04188 { 04189 ctx->LockDeletePlayer(__FILE__, __LINE__); 04190 uint64_t bookmark = ctx->player->GetBookmark(); 04191 float rate = ctx->player->GetFrameRate(); 04192 float seekloc = ctx->player->TranslatePositionAbsToRel(bookmark) / rate; 04193 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04194 04195 if (bookmark > rate) 04196 DoSeek(ctx, seekloc, tr("Jump to Bookmark"), 04197 /*timeIsOffset*/false, 04198 /*honorCutlist*/true); 04199 } 04200 else if (has_action(ACTION_JUMPSTART,actions)) 04201 { 04202 DoSeek(ctx, 0, tr("Jump to Beginning"), 04203 /*timeIsOffset*/false, 04204 /*honorCutlist*/true); 04205 } 04206 else if (has_action(ACTION_CLEAROSD, actions)) 04207 { 04208 ClearOSD(ctx); 04209 } 04210 else if (has_action(ACTION_VIEWSCHEDULED, actions)) 04211 EditSchedule(ctx, kViewSchedule); 04212 else if (HandleJumpToProgramAction(ctx, actions)) 04213 { 04214 } 04215 else if (has_action(ACTION_SIGNALMON, actions)) 04216 { 04217 if ((GetState(ctx) == kState_WatchingLiveTV) && ctx->recorder) 04218 { 04219 QString input = ctx->recorder->GetInput(); 04220 uint timeout = ctx->recorder->GetSignalLockTimeout(input); 04221 04222 if (timeout == 0xffffffff) 04223 { 04224 SetOSDMessage(ctx, "No Signal Monitor"); 04225 return false; 04226 } 04227 04228 int rate = sigMonMode ? 0 : 100; 04229 int notify = sigMonMode ? 0 : 1; 04230 04231 PauseLiveTV(ctx); 04232 ctx->recorder->SetSignalMonitoringRate(rate, notify); 04233 UnpauseLiveTV(ctx); 04234 04235 lockTimerOn = false; 04236 sigMonMode = !sigMonMode; 04237 } 04238 } 04239 else if (has_action(ACTION_SCREENSHOT, actions)) 04240 { 04241 ctx->LockDeletePlayer(__FILE__, __LINE__); 04242 if (ctx->player && ctx->player->GetScreenShot()) 04243 { 04244 // VideoOutput has saved screenshot 04245 } 04246 else 04247 { 04248 GetMythMainWindow()->ScreenShot(); 04249 } 04250 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04251 } 04252 else if (has_action(ACTION_STOP, actions)) 04253 { 04254 PrepareToExitPlayer(ctx, __LINE__); 04255 SetExitPlayer(true, true); 04256 } 04257 else if (has_action(ACTION_EXITSHOWNOPROMPTS, actions)) 04258 { 04259 requestDelete = false; 04260 PrepareToExitPlayer(ctx, __LINE__); 04261 SetExitPlayer(true, true); 04262 } 04263 else if (has_action("ESCAPE", actions) || 04264 has_action("BACK", actions)) 04265 { 04266 if (StateIsLiveTV(ctx->GetState()) && 04267 (ctx->lastSignalMsgTime.elapsed() < 04268 (int)PlayerContext::kSMExitTimeout)) 04269 { 04270 ClearOSD(ctx); 04271 } 04272 else 04273 { 04274 OSD *osd = GetOSDLock(ctx); 04275 if (osd && osd->IsVisible()) 04276 { 04277 ClearOSD(ctx); 04278 ReturnOSDLock(ctx, osd); 04279 return handled; 04280 } 04281 ReturnOSDLock(ctx, osd); 04282 } 04283 04284 NormalSpeed(ctx); 04285 04286 StopFFRew(ctx); 04287 04288 bool do_exit = false; 04289 04290 if (StateIsLiveTV(GetState(ctx))) 04291 { 04292 if (ctx->HasPlayer() && (12 & db_playback_exit_prompt)) 04293 { 04294 ShowOSDStopWatchingRecording(ctx); 04295 return handled; 04296 } 04297 else 04298 { 04299 do_exit = true; 04300 } 04301 } 04302 else 04303 { 04304 if (ctx->HasPlayer() && (5 & db_playback_exit_prompt) && 04305 !underNetworkControl && !isDVDStill) 04306 { 04307 ShowOSDStopWatchingRecording(ctx); 04308 return handled; 04309 } 04310 PrepareToExitPlayer(ctx, __LINE__); 04311 requestDelete = false; 04312 do_exit = true; 04313 } 04314 04315 if (do_exit) 04316 { 04317 PlayerContext *mctx = GetPlayer(ctx, 0); 04318 if (mctx != ctx) 04319 { // A PIP is active, just tear it down.. 04320 PxPTeardownView(ctx); 04321 return handled; 04322 } 04323 else 04324 { 04325 // If it's a DVD, and we're not trying to execute a 04326 // jumppoint, and it's not in a menu, then first try 04327 // jumping to the title or root menu. 04328 if (isDVD && 04329 !GetMythMainWindow()->IsExitingToMain() && 04330 has_action("BACK", actions) && 04331 !ctx->buffer->DVD()->IsInMenu() && 04332 (ctx->player->GoToMenu("title") || 04333 ctx->player->GoToMenu("root")) 04334 ) 04335 { 04336 return handled; 04337 } 04338 SetExitPlayer(true, true); 04339 } 04340 } 04341 04342 SetActive(ctx, 0, false); 04343 } 04344 else if (has_action(ACTION_ENABLEUPMIX, actions)) 04345 EnableUpmix(ctx, true); 04346 else if (has_action(ACTION_DISABLEUPMIX, actions)) 04347 EnableUpmix(ctx, false); 04348 else if (has_action(ACTION_VOLUMEDOWN, actions)) 04349 ChangeVolume(ctx, false); 04350 else if (has_action(ACTION_VOLUMEUP, actions)) 04351 ChangeVolume(ctx, true); 04352 else if (has_action("CYCLEAUDIOCHAN", actions)) 04353 ToggleMute(ctx, true); 04354 else if (has_action(ACTION_MUTEAUDIO, actions)) 04355 ToggleMute(ctx); 04356 else if (has_action("STRETCHINC", actions)) 04357 ChangeTimeStretch(ctx, 1); 04358 else if (has_action("STRETCHDEC", actions)) 04359 ChangeTimeStretch(ctx, -1); 04360 else if (has_action("MENU", actions)) 04361 ShowOSDMenu(ctx); 04362 else if (has_action("INFO", actions) || 04363 has_action("INFOWITHCUTLIST", actions)) 04364 { 04365 if (HasQueuedInput()) 04366 { 04367 DoArbSeek(ctx, ARBSEEK_SET, 04368 has_action("INFOWITHCUTLIST", actions)); 04369 } 04370 else 04371 ToggleOSD(ctx, true); 04372 } 04373 else if (has_action(ACTION_TOGGLEOSDDEBUG, actions)) 04374 ToggleOSDDebug(ctx); 04375 else if (!isDVDStill && SeekHandleAction(ctx, actions, isDVD)) 04376 { 04377 } 04378 else 04379 { 04380 handled = false; 04381 QStringList::const_iterator it = actions.begin(); 04382 for (; it != actions.end() && !handled; ++it) 04383 handled = HandleTrackAction(ctx, *it); 04384 } 04385 04386 return handled; 04387 } 04388 04389 bool TV::FFRewHandleAction(PlayerContext *ctx, const QStringList &actions) 04390 { 04391 bool handled = false; 04392 04393 if (ctx->ff_rew_state) 04394 { 04395 for (int i = 0; i < actions.size() && !handled; i++) 04396 { 04397 QString action = actions[i]; 04398 bool ok = false; 04399 int val = action.toInt(&ok); 04400 04401 if (ok && val < (int)ff_rew_speeds.size()) 04402 { 04403 SetFFRew(ctx, val); 04404 handled = true; 04405 } 04406 } 04407 04408 if (!handled) 04409 { 04410 DoPlayerSeek(ctx, StopFFRew(ctx)); 04411 UpdateOSDSeekMessage(ctx, ctx->GetPlayMessage(), kOSDTimeout_Med); 04412 handled = true; 04413 } 04414 } 04415 04416 if (ctx->ff_rew_speed) 04417 { 04418 NormalSpeed(ctx); 04419 UpdateOSDSeekMessage(ctx, ctx->GetPlayMessage(), kOSDTimeout_Med); 04420 handled = true; 04421 } 04422 04423 return handled; 04424 } 04425 04426 bool TV::ToggleHandleAction(PlayerContext *ctx, 04427 const QStringList &actions, bool isDVD) 04428 { 04429 bool handled = true; 04430 bool islivetv = StateIsLiveTV(GetState(ctx)); 04431 04432 if (has_action("TOGGLEASPECT", actions)) 04433 ToggleAspectOverride(ctx); 04434 else if (has_action("TOGGLEFILL", actions)) 04435 ToggleAdjustFill(ctx); 04436 else if (has_action(ACTION_TOGGELAUDIOSYNC, actions)) 04437 ChangeAudioSync(ctx, 0); // just display 04438 else if (has_action(ACTION_TOGGLESUBTITLEZOOM, actions)) 04439 ChangeSubtitleZoom(ctx, 0); // just display 04440 else if (has_action(ACTION_TOGGLEVISUALISATION, actions)) 04441 EnableVisualisation(ctx, false, true /*toggle*/); 04442 else if (has_action(ACTION_ENABLEVISUALISATION, actions)) 04443 EnableVisualisation(ctx, true); 04444 else if (has_action(ACTION_DISABLEVISUALISATION, actions)) 04445 EnableVisualisation(ctx, false); 04446 else if (has_action("TOGGLEPICCONTROLS", actions)) 04447 DoTogglePictureAttribute(ctx, kAdjustingPicture_Playback); 04448 else if (has_action(ACTION_TOGGLESTUDIOLEVELS, actions)) 04449 DoToggleStudioLevels(ctx); 04450 else if (has_action(ACTION_TOGGLENIGHTMODE, actions)) 04451 DoToggleNightMode(ctx); 04452 else if (has_action("TOGGLESTRETCH", actions)) 04453 ToggleTimeStretch(ctx); 04454 else if (has_action(ACTION_TOGGLEUPMIX, actions)) 04455 EnableUpmix(ctx, false, true); 04456 else if (has_action(ACTION_TOGGLESLEEP, actions)) 04457 ToggleSleepTimer(ctx); 04458 else if (has_action(ACTION_TOGGLERECORD, actions) && islivetv) 04459 ToggleRecord(ctx); 04460 else if (has_action(ACTION_TOGGLEFAV, actions) && islivetv) 04461 ToggleChannelFavorite(ctx); 04462 else if (has_action(ACTION_TOGGLECHANCONTROLS, actions) && islivetv) 04463 DoTogglePictureAttribute(ctx, kAdjustingPicture_Channel); 04464 else if (has_action(ACTION_TOGGLERECCONTROLS, actions) && islivetv) 04465 DoTogglePictureAttribute(ctx, kAdjustingPicture_Recording); 04466 else if (has_action(ACTION_TOGGLEINPUTS, actions) && 04467 islivetv && !ctx->pseudoLiveTVState) 04468 { 04469 ToggleInputs(ctx); 04470 } 04471 else if (has_action("TOGGLEBROWSE", actions)) 04472 { 04473 if (islivetv) 04474 browsehelper->BrowseStart(ctx); 04475 else if (!isDVD) 04476 ShowOSDMenu(ctx); 04477 else 04478 handled = false; 04479 } 04480 else if (has_action("EDIT", actions)) 04481 { 04482 if (islivetv) 04483 StartChannelEditMode(ctx); 04484 else if (!isDVD) 04485 StartProgramEditMode(ctx); 04486 } 04487 else 04488 handled = false; 04489 04490 return handled; 04491 } 04492 04493 void TV::EnableVisualisation(const PlayerContext *ctx, bool enable, 04494 bool toggle, const QString &action) 04495 { 04496 QString visualiser = QString(""); 04497 if (action.startsWith("VISUALISER")) 04498 visualiser = action.mid(11); 04499 04500 ctx->LockDeletePlayer(__FILE__, __LINE__); 04501 if (ctx->player && ctx->player->CanVisualise()) 04502 { 04503 bool want = enable || !visualiser.isEmpty(); 04504 if (toggle && visualiser.isEmpty()) 04505 want = !ctx->player->IsVisualising(); 04506 bool on = ctx->player->EnableVisualisation(want, visualiser); 04507 SetOSDMessage(ctx, on ? ctx->player->GetVisualiserName() : 04508 tr("Visualisation Off")); 04509 } 04510 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04511 } 04512 04513 bool TV::PxPHandleAction(PlayerContext *ctx, const QStringList &actions) 04514 { 04515 if (!IsPIPSupported(ctx) && !IsPBPSupported(ctx)) 04516 return false; 04517 04518 bool handled = true; 04519 { 04520 QMutexLocker locker(&timerIdLock); 04521 04522 if (has_action("TOGGLEPIPMODE", actions)) 04523 changePxP.enqueue("TOGGLEPIPMODE"); 04524 else if (has_action("TOGGLEPBPMODE", actions)) 04525 changePxP.enqueue("TOGGLEPBPMODE"); 04526 else if (has_action("CREATEPIPVIEW", actions)) 04527 changePxP.enqueue("CREATEPIPVIEW"); 04528 else if (has_action("CREATEPBPVIEW", actions)) 04529 changePxP.enqueue("CREATEPBPVIEW"); 04530 else if (has_action("SWAPPIP", actions)) 04531 changePxP.enqueue("SWAPPIP"); 04532 else if (has_action("TOGGLEPIPSTATE", actions)) 04533 changePxP.enqueue("TOGGLEPIPSTATE"); 04534 else 04535 handled = false; 04536 04537 if (!changePxP.empty() && !pipChangeTimerId) 04538 pipChangeTimerId = StartTimer(1, __LINE__); 04539 } 04540 04541 if (has_action("NEXTPIPWINDOW", actions)) 04542 { 04543 SetActive(ctx, -1, true); 04544 handled = true; 04545 } 04546 04547 return handled; 04548 } 04549 04550 void TV::SetBookmark(PlayerContext *ctx, bool clear) 04551 { 04552 ctx->LockDeletePlayer(__FILE__, __LINE__); 04553 if (ctx->player) 04554 { 04555 if (clear) 04556 { 04557 ctx->player->SetBookmark(true); 04558 SetOSDMessage(ctx, QObject::tr("Bookmark Cleared")); 04559 } 04560 else if (IsBookmarkAllowed(ctx)) 04561 { 04562 ctx->player->SetBookmark(); 04563 osdInfo info; 04564 ctx->CalcPlayerSliderPosition(info); 04565 info.text["title"] = QObject::tr("Position"); 04566 UpdateOSDStatus(ctx, info, kOSDFunctionalType_Default, 04567 kOSDTimeout_Med); 04568 SetOSDMessage(ctx, QObject::tr("Bookmark Saved")); 04569 } 04570 } 04571 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04572 } 04573 04574 bool TV::ActivePostQHandleAction(PlayerContext *ctx, const QStringList &actions) 04575 { 04576 bool handled = true; 04577 TVState state = GetState(ctx); 04578 bool islivetv = StateIsLiveTV(state); 04579 bool isdvd = state == kState_WatchingDVD; 04580 bool isdisc = isdvd || state == kState_WatchingBD; 04581 04582 if (has_action(ACTION_SELECT, actions)) 04583 { 04584 if (!islivetv || !CommitQueuedInput(ctx)) 04585 { 04586 ctx->LockDeletePlayer(__FILE__, __LINE__); 04587 SetBookmark(ctx, db_toggle_bookmark && ctx->player->GetBookmark()); 04588 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04589 } 04590 } 04591 else if (has_action("NEXTFAV", actions) && islivetv) 04592 ChangeChannel(ctx, CHANNEL_DIRECTION_FAVORITE); 04593 else if (has_action("NEXTSOURCE", actions) && islivetv) 04594 SwitchSource(ctx, kNextSource); 04595 else if (has_action("PREVSOURCE", actions) && islivetv) 04596 SwitchSource(ctx, kPreviousSource); 04597 else if (has_action("NEXTINPUT", actions) && islivetv) 04598 ToggleInputs(ctx); 04599 else if (has_action("NEXTCARD", actions) && islivetv) 04600 SwitchCards(ctx); 04601 else if (has_action(ACTION_GUIDE, actions)) 04602 EditSchedule(ctx, kScheduleProgramGuide); 04603 else if (has_action("PREVCHAN", actions) && islivetv) 04604 PopPreviousChannel(ctx, false); 04605 else if (has_action(ACTION_CHANNELUP, actions)) 04606 { 04607 if (islivetv) 04608 { 04609 if (db_browse_always) 04610 browsehelper->BrowseDispInfo(ctx, BROWSE_UP); 04611 else 04612 ChangeChannel(ctx, CHANNEL_DIRECTION_UP); 04613 } 04614 else 04615 DoJumpRWND(ctx); 04616 } 04617 else if (has_action(ACTION_CHANNELDOWN, actions)) 04618 { 04619 if (islivetv) 04620 { 04621 if (db_browse_always) 04622 browsehelper->BrowseDispInfo(ctx, BROWSE_DOWN); 04623 else 04624 ChangeChannel(ctx, CHANNEL_DIRECTION_DOWN); 04625 } 04626 else 04627 DoJumpFFWD(ctx); 04628 } 04629 else if (has_action("DELETE", actions) && !islivetv) 04630 { 04631 NormalSpeed(ctx); 04632 StopFFRew(ctx); 04633 SetBookmark(ctx); 04634 ShowOSDPromptDeleteRecording(ctx, tr("Are you sure you want to delete:")); 04635 } 04636 else if (has_action(ACTION_JUMPTODVDROOTMENU, actions) && isdisc) 04637 { 04638 ctx->LockDeletePlayer(__FILE__, __LINE__); 04639 if (ctx->player) 04640 ctx->player->GoToMenu("root"); 04641 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04642 } 04643 else if (has_action(ACTION_JUMPTOPOPUPMENU, actions) && isdisc) 04644 { 04645 ctx->LockDeletePlayer(__FILE__, __LINE__); 04646 if (ctx->player) 04647 ctx->player->GoToMenu("popup"); 04648 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04649 } 04650 else if (has_action(ACTION_FINDER, actions)) 04651 EditSchedule(ctx, kScheduleProgramFinder); 04652 else 04653 handled = false; 04654 04655 return handled; 04656 } 04657 04658 04659 void TV::ProcessNetworkControlCommand(PlayerContext *ctx, 04660 const QString &command) 04661 { 04662 bool ignoreKeys = ctx->IsPlayerChangingBuffers(); 04663 #ifdef DEBUG_ACTIONS 04664 LOG(VB_GENERAL, LOG_DEBUG, LOC + "ProcessNetworkControlCommand(" + 04665 QString("%1) ignoreKeys: %2").arg(command).arg(ignoreKeys)); 04666 #endif 04667 04668 if (ignoreKeys) 04669 { 04670 LOG(VB_GENERAL, LOG_WARNING, LOC + 04671 "Ignoring network control command" 04672 "\n\t\t\tbecause ignoreKeys is set"); 04673 return; 04674 } 04675 04676 QStringList tokens = command.split(" ", QString::SkipEmptyParts); 04677 if (tokens.size() < 2) 04678 { 04679 LOG(VB_GENERAL, LOG_ERR, LOC + "Not enough tokens" 04680 "in network control command" + "\n\t\t\t" + 04681 QString("'%1'").arg(command)); 04682 return; 04683 } 04684 04685 OSD *osd = GetOSDLock(ctx); 04686 bool dlg = false; 04687 if (osd) 04688 dlg = osd->DialogVisible(); 04689 ReturnOSDLock(ctx, osd); 04690 04691 if (dlg) 04692 { 04693 LOG(VB_GENERAL, LOG_WARNING, LOC + 04694 "Ignoring network control command\n\t\t\t" + 04695 QString("because dialog is waiting for a response")); 04696 return; 04697 } 04698 04699 if (tokens[1] != "QUERY") 04700 ClearOSD(ctx); 04701 04702 if (tokens.size() == 3 && tokens[1] == "CHANID") 04703 { 04704 queuedChanID = tokens[2].toUInt(); 04705 queuedChanNum = QString::null; 04706 CommitQueuedInput(ctx); 04707 } 04708 else if (tokens.size() == 3 && tokens[1] == "CHANNEL") 04709 { 04710 if (StateIsLiveTV(GetState(ctx))) 04711 { 04712 if (tokens[2] == "UP") 04713 ChangeChannel(ctx, CHANNEL_DIRECTION_UP); 04714 else if (tokens[2] == "DOWN") 04715 ChangeChannel(ctx, CHANNEL_DIRECTION_DOWN); 04716 else if (tokens[2].contains(QRegExp("^[-\\.\\d_#]+$"))) 04717 ChangeChannel(ctx, 0, tokens[2]); 04718 } 04719 } 04720 else if (tokens.size() == 3 && tokens[1] == "SPEED") 04721 { 04722 bool paused = ContextIsPaused(ctx, __FILE__, __LINE__); 04723 04724 if (tokens[2] == "0x") 04725 { 04726 NormalSpeed(ctx); 04727 StopFFRew(ctx); 04728 if (!paused) 04729 DoTogglePause(ctx, true); 04730 } 04731 else 04732 { 04733 float tmpSpeed = 1.0f; 04734 bool ok = false; 04735 04736 if (tokens[2].contains(QRegExp("^\\-*\\d+x$"))) 04737 { 04738 QString speed = tokens[2].left(tokens[2].length()-1); 04739 tmpSpeed = speed.toFloat(&ok); 04740 } 04741 else if (tokens[2].contains(QRegExp("^\\-*\\d*\\.\\d+x$"))) 04742 { 04743 QString speed = tokens[2].left(tokens[2].length() - 1); 04744 tmpSpeed = speed.toFloat(&ok); 04745 } 04746 else 04747 { 04748 QRegExp re = QRegExp("^(\\-*\\d+)\\/(\\d+)x$"); 04749 if (tokens[2].contains(re)) 04750 { 04751 QStringList matches = re.capturedTexts(); 04752 04753 int numerator, denominator; 04754 numerator = matches[1].toInt(&ok); 04755 denominator = matches[2].toInt(&ok); 04756 04757 if (ok && denominator != 0) 04758 tmpSpeed = static_cast<float>(numerator) / 04759 static_cast<float>(denominator); 04760 else 04761 ok = false; 04762 } 04763 } 04764 04765 if (ok) 04766 { 04767 float searchSpeed = fabs(tmpSpeed); 04768 unsigned int index; 04769 04770 if (paused) 04771 DoTogglePause(ctx, true); 04772 04773 if (tmpSpeed == 0.0f) 04774 { 04775 NormalSpeed(ctx); 04776 StopFFRew(ctx); 04777 04778 if (!paused) 04779 DoTogglePause(ctx, true); 04780 } 04781 else if (tmpSpeed == 1.0f) 04782 { 04783 StopFFRew(ctx); 04784 ctx->ts_normal = 1.0f; 04785 ChangeTimeStretch(ctx, 0, false); 04786 04787 ReturnPlayerLock(ctx); 04788 return; 04789 } 04790 04791 NormalSpeed(ctx); 04792 04793 for (index = 0; index < ff_rew_speeds.size(); index++) 04794 if (float(ff_rew_speeds[index]) == searchSpeed) 04795 break; 04796 04797 if ((index < ff_rew_speeds.size()) && 04798 (float(ff_rew_speeds[index]) == searchSpeed)) 04799 { 04800 if (tmpSpeed < 0) 04801 ctx->ff_rew_state = -1; 04802 else if (tmpSpeed > 1) 04803 ctx->ff_rew_state = 1; 04804 else 04805 StopFFRew(ctx); 04806 04807 if (ctx->ff_rew_state) 04808 SetFFRew(ctx, index); 04809 } 04810 else if (0.48 <= tmpSpeed && tmpSpeed <= 2.0) { 04811 StopFFRew(ctx); 04812 04813 ctx->ts_normal = tmpSpeed; // alter speed before display 04814 ChangeTimeStretch(ctx, 0, false); 04815 } 04816 else 04817 { 04818 LOG(VB_GENERAL, LOG_WARNING, 04819 QString("Couldn't find %1 speed. Setting Speed to 1x") 04820 .arg(searchSpeed)); 04821 04822 ctx->ff_rew_state = 0; 04823 SetFFRew(ctx, kInitFFRWSpeed); 04824 } 04825 } 04826 else 04827 { 04828 LOG(VB_GENERAL, LOG_ERR, 04829 QString("Found an unknown speed of %1").arg(tokens[2])); 04830 } 04831 } 04832 } 04833 else if (tokens.size() == 2 && tokens[1] == "STOP") 04834 { 04835 SetBookmark(ctx); 04836 ctx->LockDeletePlayer(__FILE__, __LINE__); 04837 if (ctx->player && db_auto_set_watched) 04838 ctx->player->SetWatched(); 04839 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04840 SetExitPlayer(true, true); 04841 } 04842 else if (tokens.size() >= 3 && tokens[1] == "SEEK" && ctx->HasPlayer()) 04843 { 04844 if (ctx->buffer && ctx->buffer->IsInDiscMenuOrStillFrame()) 04845 return; 04846 04847 ctx->LockDeletePlayer(__FILE__, __LINE__); 04848 long long fplay = 0; 04849 if (ctx->player && (tokens[2] == "BEGINNING" || tokens[2] == "POSITION")) 04850 { 04851 fplay = ctx->player->GetFramesPlayed(); 04852 } 04853 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04854 04855 if (tokens[2] == "BEGINNING") 04856 DoSeek(ctx, 0, tr("Jump to Beginning"), 04857 /*timeIsOffset*/false, 04858 /*honorCutlist*/true); 04859 else if (tokens[2] == "FORWARD") 04860 DoSeek(ctx, ctx->fftime, tr("Skip Ahead"), 04861 /*timeIsOffset*/true, 04862 /*honorCutlist*/true); 04863 else if (tokens[2] == "BACKWARD") 04864 DoSeek(ctx, -ctx->rewtime, tr("Skip Back"), 04865 /*timeIsOffset*/true, 04866 /*honorCutlist*/true); 04867 else if ((tokens[2] == "POSITION" || 04868 tokens[2] == "POSITIONWITHCUTLIST") && 04869 (tokens.size() == 4) && 04870 (tokens[3].contains(QRegExp("^\\d+$")))) 04871 { 04872 DoSeekAbsolute(ctx, tokens[3].toInt(), 04873 tokens[2] == "POSITIONWITHCUTLIST"); 04874 } 04875 } 04876 else if (tokens.size() >= 3 && tokens[1] == "VOLUME") 04877 { 04878 QRegExp re = QRegExp("(\\d+)%"); 04879 if (tokens[2].contains(re)) 04880 { 04881 QStringList matches = re.capturedTexts(); 04882 04883 LOG(VB_GENERAL, LOG_INFO, QString("Set Volume to %1%") 04884 .arg(matches[1])); 04885 04886 bool ok = false; 04887 04888 int vol = matches[1].toInt(&ok); 04889 04890 if (!ok) 04891 return; 04892 04893 if ( 0 <= vol && vol <= 100) 04894 { 04895 ctx->LockDeletePlayer(__FILE__, __LINE__); 04896 if (!ctx->player) 04897 { 04898 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04899 return; 04900 } 04901 04902 vol -= ctx->player->GetVolume(); 04903 vol = ctx->player->AdjustVolume(vol); 04904 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04905 04906 if (!browsehelper->IsBrowsing() && !editmode) 04907 { 04908 UpdateOSDStatus(ctx, tr("Adjust Volume"), tr("Volume"), 04909 QString::number(vol), 04910 kOSDFunctionalType_PictureAdjust, "%", vol * 10, 04911 kOSDTimeout_Med); 04912 SetUpdateOSDPosition(false); 04913 } 04914 } 04915 } 04916 } 04917 else if (tokens.size() >= 3 && tokens[1] == "QUERY") 04918 { 04919 if (tokens[2] == "POSITION") 04920 { 04921 QString speedStr; 04922 if (ContextIsPaused(ctx, __FILE__, __LINE__)) 04923 { 04924 speedStr = "pause"; 04925 } 04926 else if (ctx->ff_rew_state) 04927 { 04928 speedStr = QString("%1x").arg(ctx->ff_rew_speed); 04929 } 04930 else 04931 { 04932 QRegExp re = QRegExp("Play (.*)x"); 04933 if (QString(ctx->GetPlayMessage()).contains(re)) 04934 { 04935 QStringList matches = re.capturedTexts(); 04936 speedStr = QString("%1x").arg(matches[1]); 04937 } 04938 else 04939 { 04940 speedStr = "1x"; 04941 } 04942 } 04943 04944 osdInfo info; 04945 ctx->CalcPlayerSliderPosition(info, true); 04946 04947 QDateTime respDate = mythCurrentDateTime(); 04948 QString infoStr = ""; 04949 04950 ctx->LockDeletePlayer(__FILE__, __LINE__); 04951 long long fplay = 0; 04952 float rate = 30.0f; 04953 if (ctx->player) 04954 { 04955 fplay = ctx->player->GetFramesPlayed(); 04956 rate = ctx->player->GetFrameRate(); 04957 } 04958 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 04959 04960 ctx->LockPlayingInfo(__FILE__, __LINE__); 04961 if (ctx->GetState() == kState_WatchingLiveTV) 04962 { 04963 infoStr = "LiveTV"; 04964 if (ctx->playingInfo) 04965 respDate = ctx->playingInfo->GetScheduledStartTime(); 04966 } 04967 else 04968 { 04969 if (ctx->buffer->IsDVD()) 04970 infoStr = "DVD"; 04971 else if (ctx->playingInfo->IsRecording()) 04972 infoStr = "Recorded"; 04973 else 04974 infoStr = "Video"; 04975 04976 if (ctx->playingInfo) 04977 respDate = ctx->playingInfo->GetRecordingStartTime(); 04978 } 04979 04980 if ((infoStr == "Recorded") || (infoStr == "LiveTV")) 04981 { 04982 infoStr += QString(" %1 %2 %3 %4 %5 %6 %7") 04983 .arg(info.text["description"]) 04984 .arg(speedStr) 04985 .arg(ctx->playingInfo != NULL ? 04986 ctx->playingInfo->GetChanID() : 0) 04987 .arg(respDate.toString(Qt::ISODate)) 04988 .arg(fplay) 04989 .arg(ctx->buffer->GetFilename()) 04990 .arg(rate); 04991 } 04992 else 04993 { 04994 QString position = info.text["description"].section(" ",0,0); 04995 infoStr += QString(" %1 %2 %3 %4 %5") 04996 .arg(position) 04997 .arg(speedStr) 04998 .arg(ctx->buffer->GetFilename()) 04999 .arg(fplay) 05000 .arg(rate); 05001 } 05002 05003 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 05004 05005 QString message = QString("NETWORK_CONTROL ANSWER %1") 05006 .arg(infoStr); 05007 MythEvent me(message); 05008 gCoreContext->dispatch(me); 05009 } 05010 else if (tokens[2] == "VOLUME") 05011 { 05012 QString infoStr = QString("%1%").arg(ctx->player->GetVolume()); 05013 05014 QString message = QString("NETWORK_CONTROL ANSWER %1") 05015 .arg(infoStr); 05016 MythEvent me(message); 05017 gCoreContext->dispatch(me); 05018 } 05019 } 05020 } 05021 05026 bool TV::CreatePBP(PlayerContext *ctx, const ProgramInfo *info) 05027 { 05028 LOG(VB_PLAYBACK, LOG_INFO, LOC + "CreatePBP() -- begin"); 05029 05030 if (player.size() > 1) 05031 { 05032 LOG(VB_GENERAL, LOG_ERR, LOC + "CreatePBP() -- end : " 05033 "only allowed when player.size() == 1"); 05034 return false; 05035 } 05036 05037 PlayerContext *mctx = GetPlayer(ctx, 0); 05038 if (!IsPBPSupported(mctx)) 05039 { 05040 LOG(VB_GENERAL, LOG_ERR, LOC + "CreatePBP() -- end : " 05041 "PBP not supported by video method."); 05042 return false; 05043 } 05044 05045 if (!mctx->player) 05046 return false; 05047 mctx->LockDeletePlayer(__FILE__, __LINE__); 05048 long long mctx_frame = mctx->player->GetFramesPlayed(); 05049 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 05050 05051 // This is safe because we are already holding lock for a ctx 05052 player.push_back(new PlayerContext(kPBPPlayerInUseID)); 05053 PlayerContext *pbpctx = player.back(); 05054 if (noHardwareDecoders) 05055 pbpctx->SetNoHardwareDecoders(); 05056 pbpctx->SetPIPState(kPBPRight); 05057 05058 if (info) 05059 { 05060 pbpctx->SetPlayingInfo(info); 05061 pbpctx->SetInitialTVState(false); 05062 ScheduleStateChange(pbpctx); 05063 } 05064 else if (RequestNextRecorder(pbpctx, false)) 05065 { 05066 pbpctx->SetInitialTVState(true); 05067 ScheduleStateChange(pbpctx); 05068 } 05069 else 05070 { 05071 delete player.back(); 05072 player.pop_back(); 05073 return false; 05074 } 05075 05076 mctx->PIPTeardown(); 05077 mctx->SetPIPState(kPBPLeft); 05078 mctx->buffer->Seek(0, SEEK_SET); 05079 05080 if (StateIsLiveTV(mctx->GetState())) 05081 mctx->buffer->Unpause(); 05082 05083 bool ok = mctx->CreatePlayer( 05084 this, GetMythMainWindow(), mctx->GetState(), false); 05085 05086 if (ok) 05087 { 05088 ScheduleStateChange(mctx); 05089 mctx->LockDeletePlayer(__FILE__, __LINE__); 05090 if (mctx->player) 05091 mctx->player->JumpToFrame(mctx_frame); 05092 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 05093 SetSpeedChangeTimer(25, __LINE__); 05094 } 05095 else 05096 { 05097 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to restart new main context"); 05098 // Make putative PBP context the main context 05099 swap(player[0],player[1]); 05100 player[0]->SetPIPState(kPIPOff); 05101 // End the old main context.. 05102 ForceNextStateNone(mctx); 05103 } 05104 05105 LOG(VB_PLAYBACK, LOG_INFO, LOC + 05106 QString("CreatePBP() -- end : %1").arg(ok)); 05107 return ok; 05108 } 05109 05114 bool TV::CreatePIP(PlayerContext *ctx, const ProgramInfo *info) 05115 { 05116 PlayerContext *mctx = GetPlayer(ctx, 0); 05117 if (!mctx) 05118 return false; 05119 05120 LOG(VB_PLAYBACK, LOG_INFO, LOC + "CreatePIP -- begin"); 05121 05122 if (mctx->IsPBP()) 05123 { 05124 LOG(VB_GENERAL, LOG_ERR, LOC + 05125 "CreatePIP called, but we're in PBP mode already, ignoring."); 05126 return false; 05127 } 05128 05129 if (!IsPIPSupported(mctx)) 05130 { 05131 LOG(VB_GENERAL, LOG_ERR, LOC + "PiP not supported by video method."); 05132 return false; 05133 } 05134 05135 PlayerContext *pipctx = new PlayerContext(kPIPPlayerInUseID); 05136 if (noHardwareDecoders) 05137 pipctx->SetNoHardwareDecoders(); 05138 pipctx->SetNullVideo(true); 05139 pipctx->SetPIPState(kPIPonTV); 05140 if (info) 05141 { 05142 pipctx->SetPlayingInfo(info); 05143 pipctx->SetInitialTVState(false); 05144 ScheduleStateChange(pipctx); 05145 } 05146 else if (RequestNextRecorder(pipctx, false)) 05147 { 05148 pipctx->SetInitialTVState(true); 05149 ScheduleStateChange(pipctx); 05150 } 05151 else 05152 { 05153 delete pipctx; 05154 return false; 05155 } 05156 05157 // this is safe because we are already holding lock for ctx 05158 player.push_back(pipctx); 05159 05160 return true; 05161 } 05162 05163 int TV::find_player_index(const PlayerContext *ctx) const 05164 { 05165 for (uint i = 0; i < player.size(); i++) 05166 if (GetPlayer(ctx, i) == ctx) 05167 return i; 05168 return -1; 05169 } 05170 05171 bool TV::StartPlayer(PlayerContext *mctx, PlayerContext *ctx, 05172 TVState desiredState) 05173 { 05174 bool wantPiP = ctx->IsPIP(); 05175 05176 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("StartPlayer(%1, %2, %3) -- begin") 05177 .arg(find_player_index(ctx)).arg(StateToString(desiredState)) 05178 .arg((wantPiP) ? "PiP" : "main")); 05179 05180 LOG(VB_PLAYBACK, LOG_INFO, LOC + 05181 QString("Elapsed time since TV constructor was called: %1 ms") 05182 .arg(ctorTime.elapsed())); 05183 05184 if (wantPiP) 05185 { 05186 if (mctx->HasPlayer() && ctx->StartPIPPlayer(this, desiredState) && 05187 ctx->HasPlayer() && PIPAddPlayer(mctx, ctx)) 05188 { 05189 ScheduleStateChange(ctx); 05190 LOG(VB_GENERAL, LOG_INFO, "StartPlayer PiP -- end : ok"); 05191 return true; 05192 } 05193 05194 ForceNextStateNone(ctx); 05195 LOG(VB_GENERAL, LOG_INFO, "StartPlayer PiP -- end : !ok"); 05196 return false; 05197 } 05198 05199 bool ok = false; 05200 if (ctx->IsNullVideoDesired()) 05201 { 05202 ok = ctx->CreatePlayer(this, NULL, desiredState, false); 05203 ScheduleStateChange(ctx); 05204 if (ok) 05205 ok = PIPAddPlayer(mctx, ctx); 05206 } 05207 else 05208 { 05209 ok = ctx->CreatePlayer(this, GetMythMainWindow(), desiredState, false); 05210 ScheduleStateChange(ctx); 05211 } 05212 05213 if (ok) 05214 { 05215 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Created player.")); 05216 SetSpeedChangeTimer(25, __LINE__); 05217 } 05218 05219 LOG(VB_PLAYBACK, LOG_INFO, LOC + 05220 QString("StartPlayer(%1, %2, %3) -- end %4") 05221 .arg(find_player_index(ctx)).arg(StateToString(desiredState)) 05222 .arg((wantPiP) ? "PiP" : "main").arg((ok) ? "ok" : "error")); 05223 05224 return ok; 05225 } 05226 05228 bool TV::PIPAddPlayer(PlayerContext *mctx, PlayerContext *pipctx) 05229 { 05230 if (!mctx || !pipctx) 05231 return false; 05232 05233 if (!mctx->IsPlayerPlaying()) 05234 return false; 05235 05236 bool ok = false, addCondition = false; 05237 pipctx->LockDeletePlayer(__FILE__, __LINE__); 05238 if (pipctx->player) 05239 { 05240 bool is_using_null = pipctx->player->UsingNullVideo(); 05241 pipctx->UnlockDeletePlayer(__FILE__, __LINE__); 05242 05243 if (is_using_null) 05244 { 05245 addCondition = true; 05246 multi_lock(&mctx->deletePlayerLock, &pipctx->deletePlayerLock, NULL); 05247 if (mctx->player && pipctx->player) 05248 { 05249 PIPLocation loc = mctx->player->GetNextPIPLocation(); 05250 if (loc != kPIP_END) 05251 ok = mctx->player->AddPIPPlayer(pipctx->player, loc, 4000); 05252 } 05253 mctx->deletePlayerLock.unlock(); 05254 pipctx->deletePlayerLock.unlock(); 05255 } 05256 else if (pipctx->IsPIP()) 05257 { 05258 ok = ResizePIPWindow(pipctx); 05259 } 05260 } 05261 else 05262 pipctx->UnlockDeletePlayer(__FILE__, __LINE__); 05263 05264 LOG(VB_GENERAL, LOG_ERR, 05265 QString("AddPIPPlayer null: %1 IsPIP: %2 addCond: %3 ok: %4") 05266 .arg(pipctx->player->UsingNullVideo()) 05267 .arg(pipctx->IsPIP()).arg(addCondition).arg(ok)); 05268 05269 return ok; 05270 } 05271 05273 bool TV::PIPRemovePlayer(PlayerContext *mctx, PlayerContext *pipctx) 05274 { 05275 if (!mctx || !pipctx) 05276 return false; 05277 05278 bool ok = false; 05279 multi_lock(&mctx->deletePlayerLock, &pipctx->deletePlayerLock, NULL); 05280 if (mctx->player && pipctx->player) 05281 ok = mctx->player->RemovePIPPlayer(pipctx->player, 4000); 05282 mctx->deletePlayerLock.unlock(); 05283 pipctx->deletePlayerLock.unlock(); 05284 05285 LOG(VB_GENERAL, LOG_INFO, QString("PIPRemovePlayer ok: %1").arg(ok)); 05286 05287 return ok; 05288 } 05289 05291 void TV::PxPToggleView(PlayerContext *actx, bool wantPBP) 05292 { 05293 if (wantPBP && !IsPBPSupported(actx)) 05294 { 05295 LOG(VB_GENERAL, LOG_WARNING, LOC + 05296 "PxPToggleView() -- end: PBP not supported by video method."); 05297 return; 05298 } 05299 05300 if (player.size() <= 1) 05301 PxPCreateView(actx, wantPBP); 05302 else 05303 PxPTeardownView(actx); 05304 } 05305 05307 void TV::PxPCreateView(PlayerContext *actx, bool wantPBP) 05308 { 05309 if (!actx) 05310 return; 05311 05312 QString err_msg = QString::null; 05313 if ((player.size() > kMaxPBPCount) && (wantPBP || actx->IsPBP())) 05314 { 05315 err_msg = tr("Sorry, PBP only supports %n video stream(s)", "", 05316 kMaxPBPCount); 05317 } 05318 05319 if ((player.size() > kMaxPIPCount) && 05320 (!wantPBP || GetPlayer(actx,1)->IsPIP())) 05321 { 05322 err_msg = tr("Sorry, PIP only supports %n video stream(s)", "", 05323 kMaxPIPCount); 05324 } 05325 05326 if ((player.size() > 1) && (wantPBP ^ actx->IsPBP())) 05327 err_msg = tr("Sorry, cannot mix PBP and PIP views"); 05328 05329 if (!err_msg.isEmpty()) 05330 { 05331 LOG(VB_GENERAL, LOG_ERR, LOC + err_msg); 05332 SetOSDMessage(actx, err_msg); 05333 return; 05334 } 05335 05336 bool ok = false; 05337 if (wantPBP) 05338 ok = CreatePBP(actx, NULL); 05339 else 05340 ok = CreatePIP(actx, NULL); 05341 actx = GetPlayer(actx, -1); // CreatePBP/PIP mess with ctx's 05342 05343 QString msg = (ok) ? 05344 ((wantPBP) ? tr("Creating PBP") : tr("Creating PIP")) : 05345 ((wantPBP) ? tr("Cannot create PBP") : tr("Cannot create PIP")); 05346 05347 SetOSDMessage(actx, msg); 05348 } 05349 05351 void TV::PxPTeardownView(PlayerContext *actx) 05352 { 05353 LOG(VB_GENERAL, LOG_INFO, "PxPTeardownView()"); 05354 05355 QString msg; 05356 PlayerContext *mctx = GetPlayer(actx, 0); 05357 PlayerContext *dctx = NULL; 05358 dctx = (mctx != actx) ? actx : dctx; 05359 dctx = (2 == player.size()) ? GetPlayer(actx, 1) : dctx; 05360 05361 SetActive(actx, 0, false); 05362 05363 PlayerContext *ctx1 = GetPlayer(actx, 1); 05364 msg = (ctx1->IsPIP()) ? tr("Stopping PIP") : tr("Stopping PBP"); 05365 if (dctx) 05366 { 05367 ForceNextStateNone(dctx); 05368 } 05369 else 05370 { 05371 if (player.size() > 2) 05372 { 05373 msg = (ctx1->IsPIP()) ? 05374 tr("Stopping all PIPs") : tr("Stopping all PBPs"); 05375 } 05376 05377 for (uint i = player.size() - 1; i > 0; i--) 05378 ForceNextStateNone(GetPlayer(actx,i)); 05379 } 05380 05381 SetOSDMessage(mctx, msg); 05382 } 05383 05387 void TV::PxPToggleType(PlayerContext *mctx, bool wantPBP) 05388 { 05389 const QString before = (mctx->IsPBP()) ? "PBP" : "PIP"; 05390 const QString after = (wantPBP) ? "PBP" : "PIP"; 05391 05392 // TODO renderer may change depending on display profile 05393 // check for support in new renderer 05394 if (wantPBP && !IsPBPSupported(mctx)) 05395 { 05396 LOG(VB_GENERAL, LOG_WARNING, LOC + 05397 "PxPToggleType() -- end: PBP not supported by video method."); 05398 return; 05399 } 05400 05401 05402 LOG(VB_PLAYBACK, LOG_INFO, LOC + 05403 QString("PxPToggleType() converting from %1 to %2 -- begin") 05404 .arg(before).arg(after)); 05405 05406 if (mctx->IsPBP() == wantPBP) 05407 { 05408 LOG(VB_GENERAL, LOG_WARNING, LOC + 05409 "PxPToggleType() -- end: already in desired mode"); 05410 return; 05411 } 05412 05413 uint max_cnt = min(kMaxPBPCount, kMaxPIPCount+1); 05414 if (player.size() > max_cnt) 05415 { 05416 LOG(VB_GENERAL, LOG_ERR, LOC + 05417 QString("PxPToggleType() -- end: # player contexts must be %1 or " 05418 "less, but it is currently %1") 05419 .arg(max_cnt).arg(player.size())); 05420 05421 QString err_msg = tr("Too many views to switch"); 05422 05423 PlayerContext *actx = GetPlayer(mctx, -1); 05424 SetOSDMessage(actx, err_msg); 05425 return; 05426 } 05427 05428 for (uint i = 0; i < player.size(); i++) 05429 { 05430 PlayerContext *ctx = GetPlayer(mctx, i); 05431 if (!ctx->IsPlayerPlaying()) 05432 { 05433 LOG(VB_GENERAL, LOG_ERR, LOC + "PxPToggleType() -- end: " + 05434 QString("player #%1 is not active, exiting without " 05435 "doing anything to avoid danger").arg(i)); 05436 return; 05437 } 05438 } 05439 05440 MuteState mctx_mute = kMuteOff; 05441 mctx->LockDeletePlayer(__FILE__, __LINE__); 05442 if (mctx->player) 05443 mctx_mute = mctx->player->GetMuteState(); 05444 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 05445 05446 vector<long long> pos = TeardownAllPlayers(mctx); 05447 05448 if (wantPBP) 05449 { 05450 GetPlayer(mctx, 0)->SetPIPState(kPBPLeft); 05451 GetPlayer(mctx, 1)->SetPIPState(kPBPRight); 05452 } 05453 else 05454 { 05455 GetPlayer(mctx, 0)->SetPIPState(kPIPOff); 05456 for (uint i = 1; i < player.size(); i++) 05457 { 05458 GetPlayer(mctx, i)->SetPIPState(kPIPonTV); 05459 GetPlayer(mctx, i)->SetNullVideo(true); 05460 } 05461 } 05462 05463 RestartAllPlayers(mctx, pos, mctx_mute); 05464 05465 LOG(VB_PLAYBACK, LOG_INFO, LOC + 05466 QString("PxPToggleType() converting from %1 to %2 -- end") 05467 .arg(before).arg(after)); 05468 } 05469 05473 bool TV::ResizePIPWindow(PlayerContext *ctx) 05474 { 05475 LOG(VB_PLAYBACK, LOG_INFO, LOC + "ResizePIPWindow -- begin"); 05476 PlayerContext *mctx = GetPlayer(ctx, 0); 05477 if (mctx->HasPlayer() && ctx->HasPlayer()) 05478 { 05479 QRect rect; 05480 05481 multi_lock(&mctx->deletePlayerLock, &ctx->deletePlayerLock, (QMutex*)NULL); 05482 if (mctx->player && ctx->player) 05483 { 05484 PIPLocation loc = mctx->player->GetNextPIPLocation(); 05485 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("ResizePIPWindow -- loc %1") 05486 .arg(loc)); 05487 if (loc != kPIP_END) 05488 { 05489 rect = mctx->player->GetVideoOutput()->GetPIPRect( 05490 loc, ctx->player, false); 05491 } 05492 } 05493 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 05494 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 05495 05496 if (rect.isValid()) 05497 { 05498 ctx->ResizePIPWindow(rect); 05499 LOG(VB_PLAYBACK, LOG_INFO, LOC + "ResizePIPWindow -- end : ok"); 05500 return true; 05501 } 05502 } 05503 LOG(VB_PLAYBACK, LOG_ERR, LOC + "ResizePIPWindow -- end : !ok"); 05504 return false; 05505 } 05506 05507 bool TV::IsPBPSupported(const PlayerContext *ctx) const 05508 { 05509 const PlayerContext *mctx = NULL; 05510 if (ctx) 05511 mctx = GetPlayer(ctx, 0); 05512 else 05513 mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 05514 05515 bool yes = mctx->IsPBPSupported(); 05516 05517 if (!ctx) 05518 ReturnPlayerLock(mctx); 05519 05520 return yes; 05521 } 05522 05523 bool TV::IsPIPSupported(const PlayerContext *ctx) const 05524 { 05525 const PlayerContext *mctx = NULL; 05526 if (ctx) 05527 mctx = GetPlayer(ctx, 0); 05528 else 05529 mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 05530 05531 bool yes = mctx->IsPIPSupported(); 05532 05533 if (!ctx) 05534 ReturnPlayerLock(mctx); 05535 05536 return yes; 05537 } 05538 05542 vector<long long> TV::TeardownAllPlayers(PlayerContext *lctx) 05543 { 05544 vector<long long> pos; 05545 for (uint i = 0; i < player.size(); i++) 05546 { 05547 const PlayerContext *ctx = GetPlayer(lctx, i); 05548 ctx->LockDeletePlayer(__FILE__, __LINE__); 05549 pos.push_back((ctx->player) ? ctx->player->GetFramesPlayed() : 0); 05550 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 05551 } 05552 05553 for (uint i = 0; i < player.size(); i++) 05554 { 05555 PlayerContext *ctx = GetPlayer(lctx, i); 05556 ctx->PIPTeardown(); 05557 } 05558 05559 return pos; 05560 } 05561 05566 void TV::PBPRestartMainPlayer(PlayerContext *mctx) 05567 { 05568 LOG(VB_PLAYBACK, LOG_INFO, LOC + "PBPRestartMainPlayer -- begin"); 05569 05570 if (!mctx->IsPlayerPlaying() || 05571 mctx->GetPIPState() != kPBPLeft || exitPlayerTimerId) 05572 { 05573 LOG(VB_PLAYBACK, LOG_ERR, LOC + 05574 "PBPRestartMainPlayer -- end !ok !valid"); 05575 return; 05576 } 05577 05578 mctx->LockDeletePlayer(__FILE__, __LINE__); 05579 long long mctx_frame = (mctx->player) ? mctx->player->GetFramesPlayed() : 0; 05580 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 05581 05582 mctx->PIPTeardown(); 05583 mctx->SetPIPState(kPIPOff); 05584 mctx->buffer->Seek(0, SEEK_SET); 05585 05586 if (mctx->CreatePlayer(this, GetMythMainWindow(), mctx->GetState(), false)) 05587 { 05588 ScheduleStateChange(mctx); 05589 mctx->LockDeletePlayer(__FILE__, __LINE__); 05590 if (mctx->player) 05591 mctx->player->JumpToFrame(mctx_frame); 05592 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 05593 SetSpeedChangeTimer(25, __LINE__); 05594 LOG(VB_PLAYBACK, LOG_INFO, LOC + "PBPRestartMainPlayer -- end ok"); 05595 return; 05596 } 05597 05598 ForceNextStateNone(mctx); 05599 LOG(VB_PLAYBACK, LOG_ERR, LOC + 05600 "PBPRestartMainPlayer -- end !ok Player did not restart"); 05601 } 05602 05606 void TV::RestartAllPlayers(PlayerContext *lctx, 05607 const vector<long long> &pos, 05608 MuteState mctx_mute) 05609 { 05610 QString loc = LOC + QString("RestartAllPlayers(): "); 05611 05612 PlayerContext *mctx = GetPlayer(lctx, 0); 05613 05614 if (!mctx) 05615 return; 05616 05617 mctx->buffer->Seek(0, SEEK_SET); 05618 05619 if (StateIsLiveTV(mctx->GetState())) 05620 mctx->buffer->Unpause(); 05621 05622 bool ok = StartPlayer(mctx, mctx, mctx->GetState()); 05623 05624 if (ok) 05625 { 05626 mctx->LockDeletePlayer(__FILE__, __LINE__); 05627 if (mctx->player) 05628 mctx->player->JumpToFrame(pos[0]); 05629 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 05630 } 05631 else 05632 { 05633 LOG(VB_GENERAL, LOG_ERR, loc + 05634 "Failed to restart new main context (was pip context)"); 05635 ForceNextStateNone(mctx); 05636 return; 05637 } 05638 05639 for (uint i = 1; i < player.size(); i++) 05640 { 05641 PlayerContext *pipctx = GetPlayer(lctx, i); 05642 05643 pipctx->buffer->Seek(0, SEEK_SET); 05644 05645 if (StateIsLiveTV(pipctx->GetState())) 05646 pipctx->buffer->Unpause(); 05647 05648 ok = StartPlayer(mctx, pipctx, pipctx->GetState()); 05649 05650 if (ok) 05651 { 05652 pipctx->LockDeletePlayer(__FILE__, __LINE__); 05653 if (pipctx->player) 05654 { 05655 pipctx->player->SetMuted(true); 05656 pipctx->player->JumpToFrame(pos[i]); 05657 } 05658 pipctx->UnlockDeletePlayer(__FILE__, __LINE__); 05659 } 05660 else 05661 { // TODO print OSD informing user of Swap failure ? 05662 LOG(VB_GENERAL, LOG_ERR, loc + 05663 "Failed to restart new pip context (was main context)"); 05664 ForceNextStateNone(pipctx); 05665 } 05666 } 05667 05668 // If old main player had a kMuteAll | kMuteOff setting, 05669 // apply old main player's mute setting to new main player. 05670 mctx->LockDeletePlayer(__FILE__, __LINE__); 05671 if (mctx->player && ((kMuteAll == mctx_mute) || (kMuteOff == mctx_mute))) 05672 mctx->player->SetMuteState(mctx_mute); 05673 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 05674 } 05675 05676 void TV::PxPSwap(PlayerContext *mctx, PlayerContext *pipctx) 05677 { 05678 if (!mctx || !pipctx) 05679 return; 05680 05681 LOG(VB_PLAYBACK, LOG_INFO, LOC + "PxPSwap -- begin"); 05682 if (mctx == pipctx) 05683 { 05684 LOG(VB_GENERAL, LOG_ERR, LOC + "PxPSwap -- need two contexts"); 05685 return; 05686 } 05687 05688 lockTimerOn = false; 05689 05690 multi_lock(&mctx->deletePlayerLock, &pipctx->deletePlayerLock, NULL); 05691 if (!mctx->player || !mctx->player->IsPlaying() || 05692 !pipctx->player || !pipctx->player->IsPlaying()) 05693 { 05694 mctx->deletePlayerLock.unlock(); 05695 pipctx->deletePlayerLock.unlock(); 05696 LOG(VB_GENERAL, LOG_ERR, LOC + "PxPSwap -- a player is not playing"); 05697 return; 05698 } 05699 05700 MuteState mctx_mute = mctx->player->GetMuteState(); 05701 mctx->deletePlayerLock.unlock(); 05702 pipctx->deletePlayerLock.unlock(); 05703 05704 int ctx_index = find_player_index(pipctx); 05705 05706 vector<long long> pos = TeardownAllPlayers(mctx); 05707 05708 swap(player[0], player[ctx_index]); 05709 swap(pos[0], pos[ctx_index]); 05710 swap(player[0]->pipState, player[ctx_index]->pipState); 05711 playerActive = (ctx_index == playerActive) ? 05712 0 : ((ctx_index == 0) ? ctx_index : playerActive); 05713 05714 RestartAllPlayers(mctx, pos, mctx_mute); 05715 05716 SetActive(mctx, playerActive, false); 05717 05718 LOG(VB_PLAYBACK, LOG_INFO, LOC + "PxPSwap -- end"); 05719 } 05720 05721 void TV::RestartMainPlayer(PlayerContext *mctx) 05722 { 05723 if (!mctx) 05724 return; 05725 05726 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Restart main player -- begin"); 05727 lockTimerOn = false; 05728 05729 mctx->LockDeletePlayer(__FILE__, __LINE__); 05730 if (!mctx->player) 05731 { 05732 mctx->deletePlayerLock.unlock(); 05733 return; 05734 } 05735 05736 MuteState mctx_mute = mctx->player->GetMuteState(); 05737 05738 // HACK - FIXME 05739 // workaround muted audio when Player is re-created 05740 mctx_mute = kMuteOff; 05741 // FIXME - end 05742 mctx->deletePlayerLock.unlock(); 05743 05744 vector<long long> pos = TeardownAllPlayers(mctx); 05745 RestartAllPlayers(mctx, pos, mctx_mute); 05746 SetActive(mctx, playerActive, false); 05747 05748 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Restart main player -- end"); 05749 } 05750 05751 void TV::DoPlay(PlayerContext *ctx) 05752 { 05753 ctx->LockDeletePlayer(__FILE__, __LINE__); 05754 if (!ctx->player) 05755 { 05756 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 05757 return; 05758 } 05759 05760 float time = 0.0; 05761 05762 if (ctx->ff_rew_state || (ctx->ff_rew_speed != 0) || 05763 ctx->player->IsPaused()) 05764 { 05765 if (ctx->ff_rew_state) 05766 time = StopFFRew(ctx); 05767 else if (ctx->player->IsPaused()) 05768 SendMythSystemPlayEvent("PLAY_UNPAUSED", ctx->playingInfo); 05769 05770 ctx->player->Play(ctx->ts_normal, true); 05771 ctx->ff_rew_speed = 0; 05772 } 05773 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 05774 05775 DoPlayerSeek(ctx, time); 05776 UpdateOSDSeekMessage(ctx, ctx->GetPlayMessage(), kOSDTimeout_Med); 05777 05778 GetMythUI()->DisableScreensaver(); 05779 05780 SetSpeedChangeTimer(0, __LINE__); 05781 } 05782 05783 float TV::DoTogglePauseStart(PlayerContext *ctx) 05784 { 05785 if (!ctx) 05786 return 0.0f; 05787 05788 if (ctx->buffer && ctx->buffer->IsInDiscMenuOrStillFrame()) 05789 return 0.0f; 05790 05791 ctx->ff_rew_speed = 0; 05792 float time = 0.0f; 05793 05794 ctx->LockDeletePlayer(__FILE__, __LINE__); 05795 if (!ctx->player) 05796 { 05797 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 05798 return 0.0f; 05799 } 05800 if (ctx->player->IsPaused()) 05801 { 05802 ctx->player->Play(ctx->ts_normal, true); 05803 } 05804 else 05805 { 05806 if (ctx->ff_rew_state) 05807 time = StopFFRew(ctx); 05808 ctx->player->Pause(); 05809 } 05810 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 05811 return time; 05812 } 05813 05814 void TV::DoTogglePauseFinish(PlayerContext *ctx, float time, bool showOSD) 05815 { 05816 if (!ctx || !ctx->HasPlayer()) 05817 return; 05818 05819 if (ctx->buffer && ctx->buffer->IsInDiscMenuOrStillFrame()) 05820 return; 05821 05822 if (ContextIsPaused(ctx, __FILE__, __LINE__)) 05823 { 05824 if (ctx->buffer) 05825 ctx->buffer->WaitForPause(); 05826 05827 DoPlayerSeek(ctx, time); 05828 05829 if (showOSD && ctx == player[0]) 05830 UpdateOSDSeekMessage(ctx, tr("Paused"), kOSDTimeout_None); 05831 else if (showOSD) 05832 UpdateOSDSeekMessage(ctx, tr("Aux Paused"), kOSDTimeout_None); 05833 05834 RestoreScreenSaver(ctx); 05835 } 05836 else 05837 { 05838 DoPlayerSeek(ctx, time); 05839 if (showOSD) 05840 UpdateOSDSeekMessage(ctx, ctx->GetPlayMessage(), kOSDTimeout_Med); 05841 GetMythUI()->DisableScreensaver(); 05842 } 05843 05844 SetSpeedChangeTimer(0, __LINE__); 05845 } 05846 05847 void TV::DoTogglePause(PlayerContext *ctx, bool showOSD) 05848 { 05849 bool ignore = false; 05850 bool paused = false; 05851 ctx->LockDeletePlayer(__FILE__, __LINE__); 05852 if (ctx->player) 05853 { 05854 ignore = ctx->player->GetEditMode(); 05855 paused = ctx->player->IsPaused(); 05856 } 05857 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 05858 05859 if (paused) 05860 SendMythSystemPlayEvent("PLAY_UNPAUSED", ctx->playingInfo); 05861 else 05862 SendMythSystemPlayEvent("PLAY_PAUSED", ctx->playingInfo); 05863 05864 if (!ignore) 05865 DoTogglePauseFinish(ctx, DoTogglePauseStart(ctx), showOSD); 05866 } 05867 05868 bool TV::DoPlayerSeek(PlayerContext *ctx, float time) 05869 { 05870 if (time > -0.001f && time < +0.001f) 05871 return false; 05872 05873 LOG(VB_PLAYBACK, LOG_INFO, LOC + 05874 QString("DoPlayerSeek (%1 seconds)").arg(time)); 05875 05876 ctx->LockDeletePlayer(__FILE__, __LINE__); 05877 if (!ctx->player) 05878 { 05879 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 05880 return false; 05881 } 05882 05883 if (!ctx->buffer->IsSeekingAllowed()) 05884 { 05885 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 05886 return false; 05887 } 05888 05889 if (ctx == GetPlayer(ctx, 0)) 05890 PauseAudioUntilBuffered(ctx); 05891 05892 bool res = false; 05893 05894 if (time > 0.0f) 05895 res = ctx->player->FastForward(time); 05896 else if (time < 0.0) 05897 res = ctx->player->Rewind(-time); 05898 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 05899 05900 return res; 05901 } 05902 05903 bool TV::SeekHandleAction(PlayerContext *actx, const QStringList &actions, 05904 const bool isDVD) 05905 { 05906 const int kRewind = 4, kForward = 8, kSticky = 16, kSlippery = 32, 05907 kRelative = 64, kAbsolute = 128, kIgnoreCutlist = 256, 05908 kWhenceMask = 3; 05909 int flags = 0; 05910 if (has_action(ACTION_SEEKFFWD, actions)) 05911 flags = ARBSEEK_FORWARD | kForward | kSlippery | kRelative; 05912 else if (has_action("FFWDSTICKY", actions)) 05913 flags = ARBSEEK_END | kForward | kSticky | kAbsolute; 05914 else if (has_action(ACTION_RIGHT, actions)) 05915 flags = ARBSEEK_FORWARD | kForward | kSticky | kRelative; 05916 else if (has_action(ACTION_SEEKRWND, actions)) 05917 flags = ARBSEEK_REWIND | kRewind | kSlippery | kRelative; 05918 else if (has_action("RWNDSTICKY", actions)) 05919 flags = ARBSEEK_SET | kRewind | kSticky | kAbsolute; 05920 else if (has_action(ACTION_LEFT, actions)) 05921 flags = ARBSEEK_REWIND | kRewind | kSticky | kRelative; 05922 else 05923 return false; 05924 05925 int direction = (flags & kRewind) ? -1 : 1; 05926 if (HasQueuedInput()) 05927 { 05928 DoArbSeek(actx, static_cast<ArbSeekWhence>(flags & kWhenceMask), 05929 !(flags & kIgnoreCutlist)); 05930 } 05931 else if (ContextIsPaused(actx, __FILE__, __LINE__)) 05932 { 05933 if (!isDVD) 05934 { 05935 float rate = 30.0f; 05936 actx->LockDeletePlayer(__FILE__, __LINE__); 05937 if (actx->player) 05938 rate = actx->player->GetFrameRate(); 05939 actx->UnlockDeletePlayer(__FILE__, __LINE__); 05940 float time = (flags & kAbsolute) ? direction : 05941 direction * (1.001 / rate); 05942 QString message = (flags & kRewind) ? QString(tr("Rewind")) : 05943 QString(tr("Forward")); 05944 DoSeek(actx, time, message, 05945 /*timeIsOffset*/true, 05946 /*honorCutlist*/!(flags & kIgnoreCutlist)); 05947 } 05948 } 05949 else if (flags & kSticky) 05950 { 05951 ChangeFFRew(actx, direction); 05952 } 05953 else if (flags & kRewind) 05954 { 05955 if (smartForward) 05956 doSmartForward = true; 05957 DoSeek(actx, -actx->rewtime, tr("Skip Back"), 05958 /*timeIsOffset*/true, 05959 /*honorCutlist*/!(flags & kIgnoreCutlist)); 05960 } 05961 else 05962 { 05963 if (smartForward & doSmartForward) 05964 DoSeek(actx, actx->rewtime, tr("Skip Ahead"), 05965 /*timeIsOffset*/true, 05966 /*honorCutlist*/!(flags & kIgnoreCutlist)); 05967 else 05968 DoSeek(actx, actx->fftime, tr("Skip Ahead"), 05969 /*timeIsOffset*/true, 05970 /*honorCutlist*/!(flags & kIgnoreCutlist)); 05971 } 05972 return true; 05973 } 05974 05975 void TV::DoSeek(PlayerContext *ctx, float time, const QString &mesg, 05976 bool timeIsOffset, bool honorCutlist) 05977 { 05978 bool limitkeys = false; 05979 05980 ctx->LockDeletePlayer(__FILE__, __LINE__); 05981 if (ctx->player && ctx->player->GetLimitKeyRepeat()) 05982 limitkeys = true; 05983 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 05984 05985 if (!limitkeys || (keyRepeatTimer.elapsed() > (int)kKeyRepeatTimeout)) 05986 { 05987 keyRepeatTimer.start(); 05988 NormalSpeed(ctx); 05989 time += StopFFRew(ctx); 05990 float framerate = ctx->player->GetFrameRate(); 05991 uint64_t currentFrameAbs = ctx->player->GetFramesPlayed(); 05992 uint64_t currentFrameRel = honorCutlist ? 05993 ctx->player->TranslatePositionAbsToRel(currentFrameAbs) : 05994 currentFrameAbs; 05995 int64_t desiredFrameRel = (timeIsOffset ? currentFrameRel : 0) + 05996 time * framerate + 0.5; 05997 if (desiredFrameRel < 0) 05998 desiredFrameRel = 0; 05999 uint64_t desiredFrameAbs = honorCutlist ? 06000 ctx->player->TranslatePositionRelToAbs(desiredFrameRel) : 06001 desiredFrameRel; 06002 time = ((int64_t)desiredFrameAbs - (int64_t)currentFrameAbs) / 06003 framerate; 06004 DoPlayerSeek(ctx, time); 06005 UpdateOSDSeekMessage(ctx, mesg, kOSDTimeout_Med); 06006 } 06007 } 06008 06009 void TV::DoSeekAbsolute(PlayerContext *ctx, long long seconds, 06010 bool honorCutlist) 06011 { 06012 ctx->LockDeletePlayer(__FILE__, __LINE__); 06013 if (!ctx->player) 06014 { 06015 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06016 return; 06017 } 06018 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06019 DoSeek(ctx, seconds, tr("Jump To"), 06020 /*timeIsOffset*/false, 06021 honorCutlist); 06022 } 06023 06024 void TV::DoArbSeek(PlayerContext *ctx, ArbSeekWhence whence, 06025 bool honorCutlist) 06026 { 06027 bool ok = false; 06028 int seek = GetQueuedInputAsInt(&ok); 06029 ClearInputQueues(ctx, true); 06030 if (!ok) 06031 return; 06032 06033 float time = ((seek / 100) * 3600) + ((seek % 100) * 60); 06034 06035 if (whence == ARBSEEK_FORWARD) 06036 DoSeek(ctx, time, tr("Jump Ahead"), 06037 /*timeIsOffset*/true, honorCutlist); 06038 else if (whence == ARBSEEK_REWIND) 06039 DoSeek(ctx, -time, tr("Jump Back"), 06040 /*timeIsOffset*/true, honorCutlist); 06041 else if (whence == ARBSEEK_END) 06042 { 06043 ctx->LockDeletePlayer(__FILE__, __LINE__); 06044 if (!ctx->player) 06045 { 06046 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06047 return; 06048 } 06049 time = (ctx->player->CalcMaxFFTime(LONG_MAX, false) / 06050 ctx->player->GetFrameRate()) - time; 06051 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06052 DoSeek(ctx, time, tr("Jump To"), 06053 /*timeIsOffset*/(whence != ARBSEEK_SET), honorCutlist); 06054 } 06055 else 06056 DoSeekAbsolute(ctx, time, honorCutlist); 06057 } 06058 06059 void TV::NormalSpeed(PlayerContext *ctx) 06060 { 06061 if (!ctx->ff_rew_speed) 06062 return; 06063 06064 ctx->ff_rew_speed = 0; 06065 06066 ctx->LockDeletePlayer(__FILE__, __LINE__); 06067 if (ctx->player) 06068 ctx->player->Play(ctx->ts_normal, true); 06069 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06070 06071 SetSpeedChangeTimer(0, __LINE__); 06072 } 06073 06074 void TV::ChangeSpeed(PlayerContext *ctx, int direction) 06075 { 06076 int old_speed = ctx->ff_rew_speed; 06077 06078 if (ContextIsPaused(ctx, __FILE__, __LINE__)) 06079 ctx->ff_rew_speed = -4; 06080 06081 ctx->ff_rew_speed += direction; 06082 06083 float time = StopFFRew(ctx); 06084 float speed; 06085 QString mesg; 06086 06087 switch (ctx->ff_rew_speed) 06088 { 06089 case 4: speed = 16.0; mesg = QString(tr("Speed 16X")); break; 06090 case 3: speed = 8.0; mesg = QString(tr("Speed 8X")); break; 06091 case 2: speed = 3.0; mesg = QString(tr("Speed 3X")); break; 06092 case 1: speed = 2.0; mesg = QString(tr("Speed 2X")); break; 06093 case 0: speed = 1.0; mesg = ctx->GetPlayMessage(); break; 06094 case -1: speed = 1.0 / 3; mesg = QString(tr("Speed 1/3X")); break; 06095 case -2: speed = 1.0 / 8; mesg = QString(tr("Speed 1/8X")); break; 06096 case -3: speed = 1.0 / 16; mesg = QString(tr("Speed 1/16X")); break; 06097 case -4: 06098 DoTogglePause(ctx, true); 06099 return; 06100 default: 06101 ctx->ff_rew_speed = old_speed; 06102 return; 06103 } 06104 06105 ctx->LockDeletePlayer(__FILE__, __LINE__); 06106 if (ctx->player && !ctx->player->Play( 06107 (!ctx->ff_rew_speed) ? ctx->ts_normal: speed, !ctx->ff_rew_speed)) 06108 { 06109 ctx->ff_rew_speed = old_speed; 06110 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06111 return; 06112 } 06113 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06114 DoPlayerSeek(ctx, time); 06115 UpdateOSDSeekMessage(ctx, mesg, kOSDTimeout_Med); 06116 06117 SetSpeedChangeTimer(0, __LINE__); 06118 } 06119 06120 float TV::StopFFRew(PlayerContext *ctx) 06121 { 06122 float time = 0.0; 06123 06124 if (!ctx->ff_rew_state) 06125 return time; 06126 06127 if (ctx->ff_rew_state > 0) 06128 time = -ff_rew_speeds[ctx->ff_rew_index] * ff_rew_repos; 06129 else 06130 time = ff_rew_speeds[ctx->ff_rew_index] * ff_rew_repos; 06131 06132 ctx->ff_rew_state = 0; 06133 ctx->ff_rew_index = kInitFFRWSpeed; 06134 06135 ctx->LockDeletePlayer(__FILE__, __LINE__); 06136 if (ctx->player) 06137 ctx->player->Play(ctx->ts_normal, true); 06138 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06139 06140 SetSpeedChangeTimer(0, __LINE__); 06141 06142 return time; 06143 } 06144 06145 void TV::ChangeFFRew(PlayerContext *ctx, int direction) 06146 { 06147 if (ctx->ff_rew_state == direction) 06148 { 06149 while (++ctx->ff_rew_index < (int)ff_rew_speeds.size()) 06150 if (ff_rew_speeds[ctx->ff_rew_index]) 06151 break; 06152 if (ctx->ff_rew_index >= (int)ff_rew_speeds.size()) 06153 ctx->ff_rew_index = kInitFFRWSpeed; 06154 SetFFRew(ctx, ctx->ff_rew_index); 06155 } 06156 else if (!ff_rew_reverse && ctx->ff_rew_state == -direction) 06157 { 06158 while (--ctx->ff_rew_index >= kInitFFRWSpeed) 06159 if (ff_rew_speeds[ctx->ff_rew_index]) 06160 break; 06161 if (ctx->ff_rew_index >= kInitFFRWSpeed) 06162 SetFFRew(ctx, ctx->ff_rew_index); 06163 else 06164 { 06165 float time = StopFFRew(ctx); 06166 DoPlayerSeek(ctx, time); 06167 UpdateOSDSeekMessage(ctx, ctx->GetPlayMessage(), kOSDTimeout_Med); 06168 } 06169 } 06170 else 06171 { 06172 NormalSpeed(ctx); 06173 ctx->ff_rew_state = direction; 06174 SetFFRew(ctx, kInitFFRWSpeed); 06175 } 06176 } 06177 06178 void TV::SetFFRew(PlayerContext *ctx, int index) 06179 { 06180 if (!ctx->ff_rew_state) 06181 { 06182 return; 06183 } 06184 06185 if (!ff_rew_speeds[index]) 06186 { 06187 return; 06188 } 06189 06190 int speed; 06191 QString mesg; 06192 if (ctx->ff_rew_state > 0) 06193 { 06194 speed = ff_rew_speeds[index]; 06195 // Don't allow ffwd if seeking is needed but not available 06196 if (!ctx->buffer->IsSeekingAllowed() && speed > 3) 06197 return; 06198 06199 ctx->ff_rew_index = index; 06200 mesg = tr("Forward %1X").arg(ff_rew_speeds[ctx->ff_rew_index]); 06201 ctx->ff_rew_speed = speed; 06202 } 06203 else 06204 { 06205 // Don't rewind if we cannot seek 06206 if (!ctx->buffer->IsSeekingAllowed()) 06207 return; 06208 06209 ctx->ff_rew_index = index; 06210 mesg = tr("Rewind %1X").arg(ff_rew_speeds[ctx->ff_rew_index]); 06211 speed = -ff_rew_speeds[ctx->ff_rew_index]; 06212 ctx->ff_rew_speed = speed; 06213 } 06214 06215 ctx->LockDeletePlayer(__FILE__, __LINE__); 06216 if (ctx->player) 06217 ctx->player->Play((float)speed, (speed == 1) && (ctx->ff_rew_state > 0)); 06218 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06219 06220 UpdateOSDSeekMessage(ctx, mesg, kOSDTimeout_None); 06221 06222 SetSpeedChangeTimer(0, __LINE__); 06223 } 06224 06225 void TV::DoQueueTranscode(PlayerContext *ctx, QString profile) 06226 { 06227 ctx->LockPlayingInfo(__FILE__, __LINE__); 06228 06229 if (ctx->GetState() == kState_WatchingPreRecorded) 06230 { 06231 bool stop = false; 06232 if (queuedTranscode) 06233 stop = true; 06234 else if (JobQueue::IsJobQueuedOrRunning( 06235 JOB_TRANSCODE, 06236 ctx->playingInfo->GetChanID(), 06237 ctx->playingInfo->GetRecordingStartTime())) 06238 { 06239 stop = true; 06240 } 06241 06242 if (stop) 06243 { 06244 JobQueue::ChangeJobCmds( 06245 JOB_TRANSCODE, 06246 ctx->playingInfo->GetChanID(), 06247 ctx->playingInfo->GetRecordingStartTime(), JOB_STOP); 06248 queuedTranscode = false; 06249 SetOSDMessage(ctx, tr("Stopping Transcode")); 06250 } 06251 else 06252 { 06253 const RecordingInfo recinfo(*ctx->playingInfo); 06254 recinfo.ApplyTranscoderProfileChange(profile); 06255 QString jobHost = ""; 06256 06257 if (db_run_jobs_on_remote) 06258 jobHost = ctx->playingInfo->GetHostname(); 06259 06260 QString msg = tr("Try Again"); 06261 if (JobQueue::QueueJob(JOB_TRANSCODE, 06262 ctx->playingInfo->GetChanID(), 06263 ctx->playingInfo->GetRecordingStartTime(), 06264 jobHost, "", "", JOB_USE_CUTLIST)) 06265 { 06266 queuedTranscode = true; 06267 msg = tr("Transcoding"); 06268 } 06269 SetOSDMessage(ctx, msg); 06270 } 06271 } 06272 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 06273 } 06274 06275 int TV::GetNumChapters(const PlayerContext *ctx) const 06276 { 06277 int num_chapters = 0; 06278 ctx->LockDeletePlayer(__FILE__, __LINE__); 06279 if (ctx->player) 06280 num_chapters = ctx->player->GetNumChapters(); 06281 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06282 return num_chapters; 06283 } 06284 06285 void TV::GetChapterTimes(const PlayerContext *ctx, QList<long long> ×) const 06286 { 06287 ctx->LockDeletePlayer(__FILE__, __LINE__); 06288 if (ctx->player) 06289 ctx->player->GetChapterTimes(times); 06290 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06291 } 06292 06293 int TV::GetCurrentChapter(const PlayerContext *ctx) const 06294 { 06295 int chapter = 0; 06296 ctx->LockDeletePlayer(__FILE__, __LINE__); 06297 if (ctx->player) 06298 chapter = ctx->player->GetCurrentChapter(); 06299 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06300 return chapter; 06301 } 06302 06303 void TV::DoJumpChapter(PlayerContext *ctx, int chapter) 06304 { 06305 NormalSpeed(ctx); 06306 StopFFRew(ctx); 06307 06308 PauseAudioUntilBuffered(ctx); 06309 06310 UpdateOSDSeekMessage(ctx, tr("Jump Chapter"), kOSDTimeout_Med); 06311 SetUpdateOSDPosition(true); 06312 06313 ctx->LockDeletePlayer(__FILE__, __LINE__); 06314 if (ctx->player) 06315 ctx->player->JumpChapter(chapter); 06316 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06317 } 06318 06319 int TV::GetNumTitles(const PlayerContext *ctx) const 06320 { 06321 int num_titles = 0; 06322 ctx->LockDeletePlayer(__FILE__, __LINE__); 06323 if (ctx->player) 06324 num_titles = ctx->player->GetNumTitles(); 06325 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06326 return num_titles; 06327 } 06328 06329 int TV::GetCurrentTitle(const PlayerContext *ctx) const 06330 { 06331 int currentTitle = 0; 06332 ctx->LockDeletePlayer(__FILE__, __LINE__); 06333 if (ctx->player) 06334 currentTitle = ctx->player->GetCurrentTitle(); 06335 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06336 return currentTitle; 06337 } 06338 06339 int TV::GetNumAngles(const PlayerContext *ctx) const 06340 { 06341 int num_angles = 0; 06342 ctx->LockDeletePlayer(__FILE__, __LINE__); 06343 if (ctx->player) 06344 num_angles = ctx->player->GetNumAngles(); 06345 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06346 return num_angles; 06347 } 06348 06349 int TV::GetCurrentAngle(const PlayerContext *ctx) const 06350 { 06351 int currentAngle = 0; 06352 ctx->LockDeletePlayer(__FILE__, __LINE__); 06353 if (ctx->player) 06354 currentAngle = ctx->player->GetCurrentAngle(); 06355 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06356 return currentAngle; 06357 } 06358 06359 QString TV::GetAngleName(const PlayerContext *ctx, int angle) const 06360 { 06361 QString name; 06362 ctx->LockDeletePlayer(__FILE__, __LINE__); 06363 if (ctx->player) 06364 name = ctx->player->GetAngleName(angle); 06365 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06366 return name; 06367 } 06368 06369 int TV::GetTitleDuration(const PlayerContext *ctx, int title) const 06370 { 06371 int seconds = 0; 06372 ctx->LockDeletePlayer(__FILE__, __LINE__); 06373 if (ctx->player) 06374 seconds = ctx->player->GetTitleDuration(title); 06375 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06376 return seconds; 06377 } 06378 06379 06380 QString TV::GetTitleName(const PlayerContext *ctx, int title) const 06381 { 06382 QString name; 06383 ctx->LockDeletePlayer(__FILE__, __LINE__); 06384 if (ctx->player) 06385 name = ctx->player->GetTitleName(title); 06386 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06387 return name; 06388 } 06389 06390 void TV::DoSwitchTitle(PlayerContext *ctx, int title) 06391 { 06392 NormalSpeed(ctx); 06393 StopFFRew(ctx); 06394 06395 PauseAudioUntilBuffered(ctx); 06396 06397 UpdateOSDSeekMessage(ctx, tr("Switch Title"), kOSDTimeout_Med); 06398 SetUpdateOSDPosition(true); 06399 06400 ctx->LockDeletePlayer(__FILE__, __LINE__); 06401 if (ctx->player) 06402 ctx->player->SwitchTitle(title); 06403 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06404 } 06405 06406 void TV::DoSwitchAngle(PlayerContext *ctx, int angle) 06407 { 06408 NormalSpeed(ctx); 06409 StopFFRew(ctx); 06410 06411 PauseAudioUntilBuffered(ctx); 06412 06413 UpdateOSDSeekMessage(ctx, tr("Switch Angle"), kOSDTimeout_Med); 06414 SetUpdateOSDPosition(true); 06415 06416 ctx->LockDeletePlayer(__FILE__, __LINE__); 06417 if (ctx->player) 06418 ctx->player->SwitchAngle(angle); 06419 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06420 } 06421 06422 void TV::DoSkipCommercials(PlayerContext *ctx, int direction) 06423 { 06424 NormalSpeed(ctx); 06425 StopFFRew(ctx); 06426 06427 if (StateIsLiveTV(GetState(ctx))) 06428 return; 06429 06430 PauseAudioUntilBuffered(ctx); 06431 06432 osdInfo info; 06433 ctx->CalcPlayerSliderPosition(info); 06434 info.text["title"] = tr("Skip"); 06435 info.text["description"] = tr("Searching"); 06436 UpdateOSDStatus(ctx, info, kOSDFunctionalType_Default, kOSDTimeout_Med); 06437 SetUpdateOSDPosition(true); 06438 06439 ctx->LockDeletePlayer(__FILE__, __LINE__); 06440 if (ctx->player) 06441 ctx->player->SkipCommercials(direction); 06442 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06443 } 06444 06445 void TV::SwitchSource(PlayerContext *ctx, uint source_direction) 06446 { 06447 QMap<uint,InputInfo> sources; 06448 uint cardid = ctx->GetCardID(); 06449 vector<uint> excluded_cardids; 06450 excluded_cardids.push_back(cardid); 06451 vector<uint> cardids = RemoteRequestFreeRecorderList(excluded_cardids); 06452 stable_sort(cardids.begin(), cardids.end()); 06453 06454 InfoMap info; 06455 ctx->recorder->GetChannelInfo(info); 06456 uint sourceid = info["sourceid"].toUInt(); 06457 06458 vector<uint>::const_iterator it = cardids.begin(); 06459 for (; it != cardids.end(); ++it) 06460 { 06461 vector<InputInfo> inputs = RemoteRequestFreeInputList( 06462 *it, excluded_cardids); 06463 06464 if (inputs.empty()) 06465 continue; 06466 06467 for (uint i = 0; i < inputs.size(); i++) 06468 { 06469 // prefer the current card's input in sources list 06470 if ((sources.find(inputs[i].sourceid) == sources.end()) || 06471 ((cardid == inputs[i].cardid) && 06472 (cardid != sources[inputs[i].sourceid].cardid))) 06473 { 06474 sources[inputs[i].sourceid] = inputs[i]; 06475 } 06476 } 06477 } 06478 06479 // Source switching 06480 QMap<uint,InputInfo>::const_iterator beg = sources.find(sourceid); 06481 QMap<uint,InputInfo>::const_iterator sit = beg; 06482 06483 if (sit == sources.end()) 06484 { 06485 return; 06486 } 06487 06488 if (kNextSource == source_direction) 06489 { 06490 ++sit; 06491 if (sit == sources.end()) 06492 sit = sources.begin(); 06493 } 06494 06495 if (kPreviousSource == source_direction) 06496 { 06497 if (sit != sources.begin()) 06498 --sit; 06499 else 06500 { 06501 QMap<uint,InputInfo>::const_iterator tmp = sources.begin(); 06502 while (tmp != sources.end()) 06503 { 06504 sit = tmp; 06505 ++tmp; 06506 } 06507 } 06508 } 06509 06510 if (sit == beg) 06511 { 06512 return; 06513 } 06514 06515 switchToInputId = (*sit).inputid; 06516 06517 QMutexLocker locker(&timerIdLock); 06518 if (!switchToInputTimerId) 06519 switchToInputTimerId = StartTimer(1, __LINE__); 06520 } 06521 06522 void TV::SwitchInputs(PlayerContext *ctx, uint inputid) 06523 { 06524 if (!ctx->recorder) 06525 { 06526 return; 06527 } 06528 06529 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("SwitchInputs(%1)").arg(inputid)); 06530 06531 if ((uint)ctx->GetCardID() == CardUtil::GetCardID(inputid)) 06532 { 06533 ToggleInputs(ctx, inputid); 06534 } 06535 else 06536 { 06537 SwitchCards(ctx, 0, QString::null, inputid); 06538 } 06539 } 06540 06541 void TV::SwitchCards(PlayerContext *ctx, 06542 uint chanid, QString channum, uint inputid) 06543 { 06544 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("SwitchCards(%1,'%2',%3)") 06545 .arg(chanid).arg(channum).arg(inputid)); 06546 06547 RemoteEncoder *testrec = NULL; 06548 06549 if (!StateIsLiveTV(GetState(ctx))) 06550 { 06551 return; 06552 } 06553 06554 uint input_cardid = 0; 06555 QStringList reclist; 06556 if (inputid) 06557 { 06558 // If we are switching to a specific input.. 06559 input_cardid = CardUtil::GetCardID(inputid); 06560 if (input_cardid) 06561 reclist.push_back(QString::number(input_cardid)); 06562 } 06563 else if (chanid || !channum.isEmpty()) 06564 { 06565 // If we are switching to a channel not on the current recorder 06566 // we need to find the next free recorder with that channel. 06567 reclist = ChannelUtil::GetValidRecorderList(chanid, channum); 06568 } 06569 06570 if (!reclist.empty()) 06571 { 06572 vector<uint> excluded_cardids; 06573 excluded_cardids.push_back(ctx->GetCardID()); 06574 testrec = RemoteRequestFreeRecorderFromList(reclist, excluded_cardids); 06575 } 06576 06577 if (testrec && testrec->IsValidRecorder()) 06578 { 06579 uint cardid = testrec->GetRecorderNumber(); 06580 int cardinputid = (int) inputid; 06581 QString inputname; 06582 06583 // We are switching to a specific input.. 06584 if (inputid) 06585 inputname = CardUtil::GetInputName(inputid); 06586 06587 // We are switching to a specific channel... 06588 if (inputname.isEmpty() && (chanid || !channum.isEmpty())) 06589 { 06590 if (chanid && channum.isEmpty()) 06591 channum = ChannelUtil::GetChanNum(chanid); 06592 06593 cardinputid = CardUtil::GetCardInputID( 06594 cardid, channum, inputname); 06595 } 06596 06597 if (cardid && cardinputid>0 && !inputname.isEmpty()) 06598 { 06599 if (!channum.isEmpty()) 06600 CardUtil::SetStartChannel(cardinputid, channum); 06601 } 06602 else 06603 { 06604 LOG(VB_GENERAL, LOG_WARNING, LOC + 06605 QString("SwitchCards(%1,'%2',%3)") 06606 .arg(chanid).arg(channum).arg(inputid) + 06607 "\n\t\t\tWe should have been able to set a start " 06608 "channel or input but failed to do so."); 06609 } 06610 } 06611 06612 // If we are just switching recorders find first available recorder. 06613 if (!testrec) 06614 testrec = RemoteRequestNextFreeRecorder(ctx->GetCardID()); 06615 06616 if (testrec && testrec->IsValidRecorder()) 06617 { 06618 // Switching cards so clear the pseudoLiveTVState. 06619 ctx->SetPseudoLiveTV(NULL, kPseudoNormalLiveTV); 06620 06621 PlayerContext *mctx = GetPlayer(ctx, 0); 06622 if (mctx != ctx) 06623 PIPRemovePlayer(mctx, ctx); 06624 06625 bool muted = false; 06626 ctx->LockDeletePlayer(__FILE__, __LINE__); 06627 if (ctx->player && ctx->player->IsMuted()) 06628 muted = true; 06629 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 06630 06631 // pause the decoder first, so we're not reading too close to the end. 06632 ctx->buffer->IgnoreLiveEOF(true); 06633 ctx->buffer->StopReads(); 06634 ctx->player->PauseDecoder(); 06635 06636 // shutdown stuff 06637 ctx->buffer->Pause(); 06638 ctx->buffer->WaitForPause(); 06639 ctx->StopPlaying(); 06640 ctx->recorder->StopLiveTV(); 06641 ctx->SetPlayer(NULL); 06642 06643 // now restart stuff 06644 ctx->lastSignalUIInfo.clear(); 06645 lockTimerOn = false; 06646 06647 ctx->SetRecorder(testrec); 06648 ctx->recorder->Setup(); 06649 // We need to set channum for SpawnLiveTV.. 06650 if (channum.isEmpty() && chanid) 06651 channum = ChannelUtil::GetChanNum(chanid); 06652 if (channum.isEmpty() && inputid) 06653 channum = CardUtil::GetStartingChannel(inputid); 06654 ctx->recorder->SpawnLiveTV(ctx->tvchain->GetID(), false, channum); 06655 06656 if (!ctx->ReloadTVChain()) 06657 { 06658 LOG(VB_GENERAL, LOG_ERR, LOC + "LiveTV not successfully restarted"); 06659 RestoreScreenSaver(ctx); 06660 ctx->SetRecorder(NULL); 06661 SetErrored(ctx); 06662 SetExitPlayer(true, false); 06663 } 06664 else 06665 { 06666 ctx->LockPlayingInfo(__FILE__, __LINE__); 06667 QString playbackURL = ctx->playingInfo->GetPlaybackURL(true); 06668 bool opennow = (ctx->tvchain->GetCardType(-1) != "DUMMY"); 06669 ctx->SetRingBuffer( 06670 RingBuffer::Create( 06671 playbackURL, false, true, 06672 opennow ? RingBuffer::kLiveTVOpenTimeout : -1)); 06673 06674 ctx->tvchain->SetProgram(*ctx->playingInfo); 06675 ctx->buffer->SetLiveMode(ctx->tvchain); 06676 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 06677 } 06678 06679 bool ok = false; 06680 if (ctx->playingInfo && StartRecorder(ctx,-1)) 06681 { 06682 PlayerContext *mctx = GetPlayer(ctx, 0); 06683 QRect dummy = QRect(); 06684 if (ctx->CreatePlayer( 06685 this, GetMythMainWindow(), ctx->GetState(), 06686 false, dummy, muted)) 06687 { 06688 ScheduleStateChange(ctx); 06689 ok = true; 06690 ctx->PushPreviousChannel(); 06691 for (uint i = 1; i < player.size(); i++) 06692 PIPAddPlayer(mctx, GetPlayer(ctx, i)); 06693 06694 SetSpeedChangeTimer(25, __LINE__); 06695 } 06696 else 06697 StopStuff(mctx, ctx, true, true, true); 06698 } 06699 06700 if (!ok) 06701 { 06702 LOG(VB_GENERAL, LOG_ERR, LOC + "LiveTV not successfully started"); 06703 RestoreScreenSaver(ctx); 06704 ctx->SetRecorder(NULL); 06705 SetErrored(ctx); 06706 SetExitPlayer(true, false); 06707 } 06708 else 06709 { 06710 lockTimer.start(); 06711 lockTimerOn = true; 06712 } 06713 } 06714 else 06715 { 06716 LOG(VB_GENERAL, LOG_ERR, LOC + "No recorder to switch to..."); 06717 delete testrec; 06718 } 06719 06720 UpdateOSDInput(ctx); 06721 UnpauseLiveTV(ctx); 06722 06723 ITVRestart(ctx, true); 06724 } 06725 06726 void TV::ToggleInputs(PlayerContext *ctx, uint inputid) 06727 { 06728 if (!ctx->recorder) 06729 { 06730 return; 06731 } 06732 06733 // If MythPlayer is paused, unpause it 06734 if (ContextIsPaused(ctx, __FILE__, __LINE__)) 06735 { 06736 HideOSDWindow(ctx, "osd_status"); 06737 GetMythUI()->DisableScreensaver(); 06738 } 06739 06740 const QString curinputname = ctx->recorder->GetInput(); 06741 QString inputname = curinputname; 06742 06743 uint cardid = ctx->GetCardID(); 06744 vector<uint> excluded_cardids; 06745 excluded_cardids.push_back(cardid); 06746 vector<InputInfo> inputs = RemoteRequestFreeInputList( 06747 cardid, excluded_cardids); 06748 06749 vector<InputInfo>::const_iterator it = inputs.end(); 06750 06751 if (inputid) 06752 { 06753 it = find(inputs.begin(), inputs.end(), inputid); 06754 } 06755 else 06756 { 06757 it = find(inputs.begin(), inputs.end(), inputname); 06758 if (it != inputs.end()) 06759 ++it; 06760 } 06761 06762 if (it == inputs.end()) 06763 it = inputs.begin(); 06764 06765 if (it != inputs.end()) 06766 inputname = (*it).name; 06767 06768 if (curinputname != inputname) 06769 { 06770 // Pause the backend recorder, send command, and then unpause.. 06771 PauseLiveTV(ctx); 06772 lockTimerOn = false; 06773 inputname = ctx->recorder->SetInput(inputname); 06774 UnpauseLiveTV(ctx); 06775 } 06776 06777 UpdateOSDInput(ctx, inputname); 06778 } 06779 06780 void TV::ToggleChannelFavorite(PlayerContext *ctx) 06781 { 06782 // TOGGLEFAV was broken in [20523], this just prints something 06783 // out so as not to cause further confusion. See #8948. 06784 LOG(VB_GENERAL, LOG_ERR, 06785 "TV::ToggleChannelFavorite() -- currently disabled"); 06786 } 06787 06788 void TV::ToggleChannelFavorite(PlayerContext *ctx, QString changroup_name) 06789 { 06790 if (ctx->recorder) 06791 ctx->recorder->ToggleChannelFavorite(changroup_name); 06792 } 06793 06794 QString TV::GetQueuedInput(void) const 06795 { 06796 QMutexLocker locker(&timerIdLock); 06797 QString ret = queuedInput; 06798 ret.detach(); 06799 return ret; 06800 } 06801 06802 int TV::GetQueuedInputAsInt(bool *ok, int base) const 06803 { 06804 QMutexLocker locker(&timerIdLock); 06805 return queuedInput.toInt(ok, base); 06806 } 06807 06808 QString TV::GetQueuedChanNum(void) const 06809 { 06810 QMutexLocker locker(&timerIdLock); 06811 06812 if (queuedChanNum.isEmpty()) 06813 return ""; 06814 06815 // strip initial zeros and other undesirable characters 06816 int i = 0; 06817 for (; i < queuedChanNum.length(); i++) 06818 { 06819 if ((queuedChanNum[i] > '0') && (queuedChanNum[i] <= '9')) 06820 break; 06821 } 06822 queuedChanNum = queuedChanNum.right(queuedChanNum.length() - i); 06823 06824 // strip whitespace at end of string 06825 queuedChanNum = queuedChanNum.trimmed(); 06826 06827 QString ret = queuedChanNum; 06828 ret.detach(); 06829 return ret; 06830 } 06831 06836 void TV::ClearInputQueues(const PlayerContext *ctx, bool hideosd) 06837 { 06838 if (hideosd) 06839 HideOSDWindow(ctx, "osd_input"); 06840 06841 QMutexLocker locker(&timerIdLock); 06842 queuedInput = ""; 06843 queuedChanNum = ""; 06844 queuedChanID = 0; 06845 if (queueInputTimerId) 06846 { 06847 KillTimer(queueInputTimerId); 06848 queueInputTimerId = 0; 06849 } 06850 } 06851 06852 void TV::AddKeyToInputQueue(PlayerContext *ctx, char key) 06853 { 06854 if (key) 06855 { 06856 QMutexLocker locker(&timerIdLock); 06857 queuedInput = queuedInput.append(key).right(kInputKeysMax); 06858 queuedChanNum = queuedChanNum.append(key).right(kInputKeysMax); 06859 if (!queueInputTimerId) 06860 queueInputTimerId = StartTimer(10, __LINE__); 06861 } 06862 06863 bool commitSmart = false; 06864 QString inputStr = GetQueuedInput(); 06865 06866 // Always use immediate channel change when channel numbers are entered 06867 // in browse mode because in browse mode space/enter exit browse 06868 // mode and change to the currently browsed channel. 06869 if (StateIsLiveTV(GetState(ctx)) && !ccInputMode && !asInputMode && 06870 browsehelper->IsBrowsing()) 06871 { 06872 commitSmart = ProcessSmartChannel(ctx, inputStr); 06873 } 06874 06875 // Handle OSD... 06876 inputStr = inputStr.isEmpty() ? "?" : inputStr; 06877 if (ccInputMode) 06878 { 06879 QString entryStr = (vbimode==VBIMode::PAL_TT) ? tr("TXT:") : tr("CC:"); 06880 inputStr = entryStr + " " + inputStr; 06881 } 06882 else if (asInputMode) 06883 inputStr = tr("Seek:", "seek to location") + " " + inputStr; 06884 SetOSDText(ctx, "osd_input", "osd_number_entry", inputStr, 06885 kOSDTimeout_Med); 06886 06887 // Commit the channel if it is complete and smart changing is enabled. 06888 if (commitSmart) 06889 CommitQueuedInput(ctx); 06890 } 06891 06892 static QString add_spacer(const QString &chan, const QString &spacer) 06893 { 06894 if ((chan.length() >= 2) && !spacer.isEmpty()) 06895 return chan.left(chan.length()-1) + spacer + chan.right(1); 06896 return chan; 06897 } 06898 06899 bool TV::ProcessSmartChannel(const PlayerContext *ctx, QString &inputStr) 06900 { 06901 QString chan = GetQueuedChanNum(); 06902 06903 if (chan.isEmpty()) 06904 return false; 06905 06906 // Check for and remove duplicate separator characters 06907 if ((chan.length() > 2) && (chan.right(1) == chan.right(2).left(1))) 06908 { 06909 bool ok; 06910 chan.right(1).toUInt(&ok); 06911 if (!ok) 06912 { 06913 chan = chan.left(chan.length()-1); 06914 06915 QMutexLocker locker(&timerIdLock); 06916 queuedChanNum = chan; 06917 if (!queueInputTimerId) 06918 queueInputTimerId = StartTimer(10, __LINE__); 06919 } 06920 } 06921 06922 // Look for channel in line-up 06923 QString needed_spacer; 06924 uint pref_cardid; 06925 bool is_not_complete = true; 06926 06927 bool valid_prefix = false; 06928 if (ctx->recorder) 06929 { 06930 valid_prefix = ctx->recorder->CheckChannelPrefix( 06931 chan, pref_cardid, is_not_complete, needed_spacer); 06932 } 06933 06934 #if DEBUG_CHANNEL_PREFIX 06935 LOG(VB_GENERAL, LOG_DEBUG, QString("valid_pref(%1) cardid(%2) chan(%3) " 06936 "pref_cardid(%4) complete(%5) sp(%6)") 06937 .arg(valid_prefix).arg(0).arg(chan) 06938 .arg(pref_cardid).arg(is_not_complete).arg(needed_spacer)); 06939 #endif 06940 06941 if (!valid_prefix) 06942 { 06943 // not a valid prefix.. reset... 06944 QMutexLocker locker(&timerIdLock); 06945 queuedChanNum = ""; 06946 } 06947 else if (!needed_spacer.isEmpty()) 06948 { 06949 // need a spacer.. 06950 QMutexLocker locker(&timerIdLock); 06951 queuedChanNum = add_spacer(chan, needed_spacer); 06952 } 06953 06954 #if DEBUG_CHANNEL_PREFIX 06955 LOG(VB_GENERAL, LOG_DEBUG, QString(" ValidPref(%1) CardId(%2) Chan(%3) " 06956 " PrefCardId(%4) Complete(%5) Sp(%6)") 06957 .arg(valid_prefix).arg(0).arg(GetQueuedChanNum()) 06958 .arg(pref_cardid).arg(is_not_complete).arg(needed_spacer)); 06959 #endif 06960 06961 QMutexLocker locker(&timerIdLock); 06962 inputStr = queuedChanNum; 06963 inputStr.detach(); 06964 if (!queueInputTimerId) 06965 queueInputTimerId = StartTimer(10, __LINE__); 06966 06967 return !is_not_complete; 06968 } 06969 06970 bool TV::CommitQueuedInput(PlayerContext *ctx) 06971 { 06972 bool commited = false; 06973 06974 LOG(VB_PLAYBACK, LOG_INFO, LOC + "CommitQueuedInput() " + 06975 QString("livetv(%1) qchannum(%2) qchanid(%3)") 06976 .arg(StateIsLiveTV(GetState(ctx))) 06977 .arg(GetQueuedChanNum()) 06978 .arg(GetQueuedChanID())); 06979 06980 if (ccInputMode) 06981 { 06982 commited = true; 06983 if (HasQueuedInput()) 06984 HandleTrackAction(ctx, ACTION_TOGGLESUBS); 06985 } 06986 else if (asInputMode) 06987 { 06988 commited = true; 06989 if (HasQueuedInput()) 06990 // XXX Should the cutlist be honored? 06991 DoArbSeek(ctx, ARBSEEK_FORWARD, /*honorCutlist*/false); 06992 } 06993 else if (StateIsLiveTV(GetState(ctx))) 06994 { 06995 QString channum = GetQueuedChanNum(); 06996 QString chaninput = GetQueuedInput(); 06997 if (browsehelper->IsBrowsing()) 06998 { 06999 uint sourceid = 0; 07000 ctx->LockPlayingInfo(__FILE__, __LINE__); 07001 if (ctx->playingInfo) 07002 sourceid = ctx->playingInfo->GetSourceID(); 07003 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 07004 07005 commited = true; 07006 if (channum.isEmpty()) 07007 channum = browsehelper->GetBrowsedInfo().m_channum; 07008 uint chanid = browsehelper->GetChanId( 07009 channum, ctx->GetCardID(), sourceid); 07010 if (chanid) 07011 browsehelper->BrowseChannel(ctx, channum); 07012 07013 HideOSDWindow(ctx, "osd_input"); 07014 } 07015 else if (GetQueuedChanID() || !channum.isEmpty()) 07016 { 07017 commited = true; 07018 ChangeChannel(ctx, GetQueuedChanID(), channum); 07019 } 07020 } 07021 07022 ClearInputQueues(ctx, true); 07023 return commited; 07024 } 07025 07026 void TV::ChangeChannel(PlayerContext *ctx, int direction) 07027 { 07028 if (db_use_channel_groups || (direction == CHANNEL_DIRECTION_FAVORITE)) 07029 { 07030 uint old_chanid = 0; 07031 if (channelGroupId > -1) 07032 { 07033 ctx->LockPlayingInfo(__FILE__, __LINE__); 07034 if (!ctx->playingInfo) 07035 { 07036 LOG(VB_GENERAL, LOG_ERR, LOC + 07037 "ChangeChannel(): no active ctx playingInfo."); 07038 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 07039 ReturnPlayerLock(ctx); 07040 return; 07041 } 07042 // Collect channel info 07043 old_chanid = ctx->playingInfo->GetChanID(); 07044 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 07045 } 07046 07047 if (old_chanid) 07048 { 07049 QMutexLocker locker(&channelGroupLock); 07050 if (channelGroupId > -1) 07051 { 07052 uint chanid = ChannelUtil::GetNextChannel( 07053 channelGroupChannelList, old_chanid, 0, direction); 07054 if (chanid) 07055 ChangeChannel(ctx, chanid, ""); 07056 return; 07057 } 07058 } 07059 } 07060 07061 if (direction == CHANNEL_DIRECTION_FAVORITE) 07062 direction = CHANNEL_DIRECTION_UP; 07063 07064 QString oldinputname = ctx->recorder->GetInput(); 07065 07066 if (ContextIsPaused(ctx, __FILE__, __LINE__)) 07067 { 07068 HideOSDWindow(ctx, "osd_status"); 07069 GetMythUI()->DisableScreensaver(); 07070 } 07071 07072 // Save the current channel if this is the first time 07073 if (ctx->prevChan.empty()) 07074 ctx->PushPreviousChannel(); 07075 07076 PauseAudioUntilBuffered(ctx); 07077 PauseLiveTV(ctx); 07078 07079 ctx->LockDeletePlayer(__FILE__, __LINE__); 07080 if (ctx->player) 07081 { 07082 ctx->player->ResetCaptions(); 07083 ctx->player->ResetTeletext(); 07084 } 07085 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 07086 07087 ctx->recorder->ChangeChannel(direction); 07088 ClearInputQueues(ctx, false); 07089 07090 if (ctx->player) 07091 ctx->player->GetAudio()->Reset(); 07092 07093 UnpauseLiveTV(ctx); 07094 07095 if (oldinputname != ctx->recorder->GetInput()) 07096 UpdateOSDInput(ctx); 07097 } 07098 07099 static uint get_chanid(const PlayerContext *ctx, 07100 uint cardid, const QString &channum) 07101 { 07102 uint chanid = 0, cur_sourceid = 0; 07103 // try to find channel on current input 07104 if (ctx && ctx->playingInfo && ctx->playingInfo->GetSourceID()) 07105 { 07106 cur_sourceid = ctx->playingInfo->GetSourceID(); 07107 chanid = max(ChannelUtil::GetChanID(cur_sourceid, channum), 0); 07108 if (chanid) 07109 return chanid; 07110 } 07111 // try to find channel on all inputs 07112 vector<uint> inputs = CardUtil::GetInputIDs(cardid); 07113 for (vector<uint>::const_iterator it = inputs.begin(); 07114 it != inputs.end(); ++it) 07115 { 07116 uint sourceid = CardUtil::GetSourceID(*it); 07117 if (cur_sourceid == sourceid) 07118 continue; // already tested above 07119 if (sourceid) 07120 { 07121 chanid = max(ChannelUtil::GetChanID(sourceid, channum), 0); 07122 if (chanid) 07123 return chanid; 07124 } 07125 } 07126 return chanid; 07127 } 07128 07129 void TV::ChangeChannel(PlayerContext *ctx, uint chanid, const QString &chan) 07130 { 07131 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("ChangeChannel(%1, '%2') ") 07132 .arg(chanid).arg(chan)); 07133 07134 if ((!chanid && chan.isEmpty()) || !ctx || !ctx->recorder) 07135 return; 07136 07137 QString channum = chan; 07138 QStringList reclist; 07139 QSet<uint> tunable_on; 07140 07141 QString oldinputname = ctx->recorder->GetInput(); 07142 07143 if (channum.isEmpty() && chanid) 07144 { 07145 channum = ChannelUtil::GetChanNum(chanid); 07146 } 07147 07148 bool getit = false; 07149 if (ctx->recorder) 07150 { 07151 if (ctx->pseudoLiveTVState == kPseudoRecording) 07152 { 07153 getit = true; 07154 } 07155 else if (chanid) 07156 { 07157 tunable_on = IsTunableOn(ctx, chanid, true, false); 07158 getit = !tunable_on.contains(ctx->GetCardID()); 07159 } 07160 else 07161 { 07162 QString needed_spacer; 07163 uint pref_cardid; 07164 uint cardid = ctx->GetCardID(); 07165 bool dummy; 07166 07167 ctx->recorder->CheckChannelPrefix(chan, pref_cardid, 07168 dummy, needed_spacer); 07169 07170 LOG(VB_CHANNEL, LOG_INFO, LOC + 07171 QString("CheckChannelPrefix(%1, pref_cardid %2, %3, '%4') " 07172 "cardid %5") 07173 .arg(chan).arg(pref_cardid).arg(dummy).arg(needed_spacer) 07174 .arg(cardid)); 07175 07176 channum = add_spacer(chan, needed_spacer); 07177 if (pref_cardid != cardid) 07178 { 07179 getit = true; 07180 } 07181 else 07182 { 07183 if (!chanid) 07184 chanid = get_chanid(ctx, cardid, chan); 07185 tunable_on = IsTunableOn(ctx, chanid, true, false); 07186 getit = !tunable_on.contains(cardid); 07187 } 07188 } 07189 07190 if (getit) 07191 { 07192 QStringList tmp = 07193 ChannelUtil::GetValidRecorderList(chanid, channum); 07194 if (tunable_on.empty()) 07195 { 07196 if (!chanid) 07197 chanid = get_chanid(ctx, ctx->GetCardID(), chan); 07198 tunable_on = IsTunableOn(ctx, chanid, true, false); 07199 } 07200 QStringList::const_iterator it = tmp.begin(); 07201 for (; it != tmp.end(); ++it) 07202 { 07203 if (!chanid || tunable_on.contains((*it).toUInt())) 07204 reclist.push_back(*it); 07205 } 07206 } 07207 } 07208 07209 if (reclist.size()) 07210 { 07211 RemoteEncoder *testrec = NULL; 07212 vector<uint> excluded_cardids; 07213 excluded_cardids.push_back(ctx->GetCardID()); 07214 testrec = RemoteRequestFreeRecorderFromList(reclist, excluded_cardids); 07215 if (!testrec || !testrec->IsValidRecorder()) 07216 { 07217 ClearInputQueues(ctx, true); 07218 ShowNoRecorderDialog(ctx); 07219 if (testrec) 07220 delete testrec; 07221 return; 07222 } 07223 07224 if (!ctx->prevChan.empty() && ctx->prevChan.back() == channum) 07225 { 07226 // need to remove it if the new channel is the same as the old. 07227 ctx->prevChan.pop_back(); 07228 } 07229 07230 uint new_cardid = testrec->GetRecorderNumber(); 07231 uint sourceid = ChannelUtil::GetSourceIDForChannel(chanid); 07232 uint inputid = CardUtil::GetInputID(new_cardid, sourceid); 07233 07234 // found the card on a different recorder. 07235 delete testrec; 07236 // Save the current channel if this is the first time 07237 if (ctx->prevChan.empty()) 07238 ctx->PushPreviousChannel(); 07239 SwitchCards(ctx, chanid, channum, inputid); 07240 return; 07241 } 07242 07243 if (getit || !ctx->recorder || !ctx->recorder->CheckChannel(channum)) 07244 return; 07245 07246 if (ContextIsPaused(ctx, __FILE__, __LINE__)) 07247 { 07248 HideOSDWindow(ctx, "osd_status"); 07249 GetMythUI()->DisableScreensaver(); 07250 } 07251 07252 // Save the current channel if this is the first time 07253 if (ctx->prevChan.empty()) 07254 ctx->PushPreviousChannel(); 07255 07256 PauseAudioUntilBuffered(ctx); 07257 PauseLiveTV(ctx); 07258 07259 ctx->LockDeletePlayer(__FILE__, __LINE__); 07260 if (ctx->player) 07261 { 07262 ctx->player->ResetCaptions(); 07263 ctx->player->ResetTeletext(); 07264 } 07265 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 07266 07267 ctx->recorder->SetChannel(channum); 07268 07269 if (ctx->player) 07270 ctx->player->GetAudio()->Reset(); 07271 07272 UnpauseLiveTV(ctx, chanid && GetQueuedChanID()); 07273 07274 if (oldinputname != ctx->recorder->GetInput()) 07275 UpdateOSDInput(ctx); 07276 } 07277 07278 void TV::ChangeChannel(const PlayerContext *ctx, const DBChanList &options) 07279 { 07280 for (uint i = 0; i < options.size(); i++) 07281 { 07282 uint chanid = options[i].chanid; 07283 QString channum = options[i].channum; 07284 07285 if (chanid && !channum.isEmpty() && IsTunable(ctx, chanid)) 07286 { 07287 // hide the channel number, activated by certain signal monitors 07288 HideOSDWindow(ctx, "osd_input"); 07289 07290 QMutexLocker locker(&timerIdLock); 07291 queuedInput = channum; queuedInput.detach(); 07292 queuedChanNum = channum; queuedChanNum.detach(); 07293 queuedChanID = chanid; 07294 if (!queueInputTimerId) 07295 queueInputTimerId = StartTimer(10, __LINE__); 07296 break; 07297 } 07298 } 07299 } 07300 07301 void TV::ShowPreviousChannel(PlayerContext *ctx) 07302 { 07303 QString channum = ctx->GetPreviousChannel(); 07304 07305 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("ShowPreviousChannel: '%1'") 07306 .arg(channum)); 07307 07308 if (channum.isEmpty()) 07309 return; 07310 07311 SetOSDText(ctx, "osd_input", "osd_number_entry", channum, kOSDTimeout_Med); 07312 } 07313 07314 void TV::PopPreviousChannel(PlayerContext *ctx, bool immediate_change) 07315 { 07316 if (!ctx->tvchain) 07317 return; 07318 07319 if (!immediate_change) 07320 ShowPreviousChannel(ctx); 07321 07322 QString prev_channum = ctx->PopPreviousChannel(); 07323 QString cur_channum = ctx->tvchain->GetChannelName(-1); 07324 07325 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("PopPreviousChannel: '%1'->'%2'") 07326 .arg(cur_channum).arg(prev_channum)); 07327 07328 // Only change channel if previous channel != current channel 07329 if (cur_channum != prev_channum && !prev_channum.isEmpty()) 07330 { 07331 QMutexLocker locker(&timerIdLock); 07332 queuedInput = prev_channum; queuedInput.detach(); 07333 queuedChanNum = prev_channum; queuedChanNum.detach(); 07334 queuedChanID = 0; 07335 if (!queueInputTimerId) 07336 queueInputTimerId = StartTimer(10, __LINE__); 07337 } 07338 07339 if (immediate_change) 07340 { 07341 // Turn off OSD Channel Num so the channel changes right away 07342 HideOSDWindow(ctx, "osd_input"); 07343 } 07344 } 07345 07346 bool TV::ClearOSD(const PlayerContext *ctx) 07347 { 07348 bool res = false; 07349 07350 if (HasQueuedInput() || HasQueuedChannel()) 07351 { 07352 ClearInputQueues(ctx, true); 07353 res = true; 07354 } 07355 07356 OSD *osd = GetOSDLock(ctx); 07357 if (osd) 07358 { 07359 osd->DialogQuit(); 07360 osd->HideAll(); 07361 res = true; 07362 } 07363 ReturnOSDLock(ctx, osd); 07364 07365 if (browsehelper->IsBrowsing()) 07366 browsehelper->BrowseEnd(NULL, false); 07367 07368 return res; 07369 } 07370 07374 void TV::ToggleOSD(PlayerContext *ctx, bool includeStatusOSD) 07375 { 07376 OSD *osd = GetOSDLock(ctx); 07377 if (!osd) 07378 { 07379 ReturnOSDLock(ctx, osd); 07380 return; 07381 } 07382 07383 bool hideAll = false; 07384 bool showStatus = false; 07385 bool paused = ContextIsPaused(ctx, __FILE__, __LINE__); 07386 bool is_status_disp = osd->IsWindowVisible("osd_status"); 07387 bool has_prog_info = osd->HasWindow("program_info"); 07388 bool is_prog_info_disp = osd->IsWindowVisible("program_info"); 07389 07390 ReturnOSDLock(ctx, osd); 07391 07392 if (is_status_disp) 07393 { 07394 if (has_prog_info) 07395 UpdateOSDProgInfo(ctx, "program_info"); 07396 else 07397 hideAll = true; 07398 } 07399 else if (is_prog_info_disp && !paused) 07400 { 07401 hideAll = true; 07402 } 07403 else if (includeStatusOSD) 07404 { 07405 showStatus = true; 07406 } 07407 else 07408 { 07409 if (has_prog_info) 07410 UpdateOSDProgInfo(ctx, "program_info"); 07411 } 07412 07413 if (hideAll || showStatus) 07414 { 07415 OSD *osd = GetOSDLock(ctx); 07416 if (osd) 07417 osd->HideAll(); 07418 ReturnOSDLock(ctx, osd); 07419 } 07420 07421 if (showStatus) 07422 { 07423 osdInfo info; 07424 if (ctx->CalcPlayerSliderPosition(info)) 07425 { 07426 info.text["title"] = paused ? tr("Paused") : tr("Position"); 07427 UpdateOSDStatus(ctx, info, kOSDFunctionalType_Default, 07428 paused ? kOSDTimeout_None : kOSDTimeout_Med); 07429 SetUpdateOSDPosition(true); 07430 } 07431 else 07432 { 07433 SetUpdateOSDPosition(false); 07434 } 07435 } 07436 else 07437 { 07438 SetUpdateOSDPosition(false); 07439 } 07440 } 07441 07442 void TV::ToggleOSDDebug(PlayerContext *ctx) 07443 { 07444 bool show = false; 07445 OSD *osd = GetOSDLock(ctx); 07446 if (osd && osd->IsWindowVisible("osd_debug")) 07447 { 07448 ctx->buffer->EnableBitrateMonitor(false); 07449 if (ctx->player) 07450 ctx->player->EnableFrameRateMonitor(false); 07451 osd->HideWindow("osd_debug"); 07452 } 07453 else if (osd) 07454 { 07455 ctx->buffer->EnableBitrateMonitor(true); 07456 if (ctx->player) 07457 ctx->player->EnableFrameRateMonitor(true); 07458 show = true; 07459 QMutexLocker locker(&timerIdLock); 07460 if (!updateOSDDebugTimerId) 07461 updateOSDDebugTimerId = StartTimer(250, __LINE__); 07462 } 07463 ReturnOSDLock(ctx, osd); 07464 if (show) 07465 UpdateOSDDebug(ctx); 07466 } 07467 07468 void TV::UpdateOSDDebug(const PlayerContext *ctx) 07469 { 07470 OSD *osd = GetOSDLock(ctx); 07471 if (osd && ctx->player) 07472 { 07473 InfoMap infoMap; 07474 ctx->player->GetPlaybackData(infoMap); 07475 osd->ResetWindow("osd_debug"); 07476 osd->SetText("osd_debug", infoMap, kOSDTimeout_None); 07477 } 07478 ReturnOSDLock(ctx, osd); 07479 } 07480 07484 void TV::UpdateOSDProgInfo(const PlayerContext *ctx, const char *whichInfo) 07485 { 07486 InfoMap infoMap; 07487 ctx->GetPlayingInfoMap(infoMap); 07488 07489 QString nightmode = gCoreContext->GetNumSetting("NightModeEnabled", 0) 07490 ? "yes" : "no"; 07491 infoMap["nightmode"] = nightmode; 07492 07493 // Clear previous osd and add new info 07494 OSD *osd = GetOSDLock(ctx); 07495 if (osd) 07496 { 07497 osd->HideAll(); 07498 osd->SetText(whichInfo, infoMap, kOSDTimeout_Long); 07499 } 07500 ReturnOSDLock(ctx, osd); 07501 } 07502 07503 void TV::UpdateOSDStatus(const PlayerContext *ctx, osdInfo &info, 07504 int type, OSDTimeout timeout) 07505 { 07506 OSD *osd = GetOSDLock(ctx); 07507 if (osd) 07508 { 07509 osd->ResetWindow("osd_status"); 07510 QString nightmode = gCoreContext->GetNumSetting("NightModeEnabled", 0) 07511 ? "yes" : "no"; 07512 info.text.insert("nightmode", nightmode); 07513 osd->SetValues("osd_status", info.values, timeout); 07514 osd->SetText("osd_status", info.text, timeout); 07515 if (type != kOSDFunctionalType_Default) 07516 osd->SetFunctionalWindow("osd_status", (OSDFunctionalType)type); 07517 } 07518 ReturnOSDLock(ctx, osd); 07519 } 07520 07521 void TV::UpdateOSDStatus(const PlayerContext *ctx, QString title, QString desc, 07522 QString value, int type, QString units, 07523 int position, OSDTimeout timeout) 07524 { 07525 osdInfo info; 07526 info.values.insert("position", position); 07527 info.values.insert("relposition", position); 07528 info.text.insert("title", title); 07529 info.text.insert("description", desc); 07530 info.text.insert("value", value); 07531 info.text.insert("units", units); 07532 UpdateOSDStatus(ctx, info, type, timeout); 07533 } 07534 07535 void TV::UpdateOSDSeekMessage(const PlayerContext *ctx, 07536 const QString &mesg, enum OSDTimeout timeout) 07537 { 07538 LOG(VB_PLAYBACK, LOG_INFO, QString("UpdateOSDSeekMessage(%1, %2)") 07539 .arg(mesg).arg(timeout)); 07540 07541 osdInfo info; 07542 if (ctx->CalcPlayerSliderPosition(info)) 07543 { 07544 int osdtype = (doSmartForward) ? kOSDFunctionalType_SmartForward : 07545 kOSDFunctionalType_Default; 07546 info.text["title"] = mesg; 07547 UpdateOSDStatus(ctx, info, osdtype, timeout); 07548 SetUpdateOSDPosition(true); 07549 } 07550 } 07551 07552 void TV::UpdateOSDInput(const PlayerContext *ctx, QString inputname) 07553 { 07554 if (!ctx->recorder || !ctx->tvchain) 07555 return; 07556 07557 int cardid = ctx->GetCardID(); 07558 07559 if (inputname.isEmpty()) 07560 inputname = ctx->tvchain->GetInputName(-1); 07561 07562 QString displayName = CardUtil::GetDisplayName(cardid, inputname); 07563 // If a display name doesn't exist use cardid and inputname 07564 if (displayName.isEmpty()) 07565 displayName = QString("%1: %2").arg(cardid).arg(inputname); 07566 07567 SetOSDMessage(ctx, displayName); 07568 } 07569 07573 void TV::UpdateOSDSignal(const PlayerContext *ctx, const QStringList &strlist) 07574 { 07575 OSD *osd = GetOSDLock(ctx); 07576 if (!osd || browsehelper->IsBrowsing() || !queuedChanNum.isEmpty()) 07577 { 07578 if (&ctx->lastSignalMsg != &strlist) 07579 { 07580 ctx->lastSignalMsg = strlist; 07581 ctx->lastSignalMsg.detach(); 07582 } 07583 ReturnOSDLock(ctx, osd); 07584 07585 QMutexLocker locker(&timerIdLock); 07586 signalMonitorTimerId[StartTimer(1, __LINE__)] = 07587 const_cast<PlayerContext*>(ctx); 07588 return; 07589 } 07590 ReturnOSDLock(ctx, osd); 07591 07592 SignalMonitorList slist = SignalMonitorValue::Parse(strlist); 07593 07594 InfoMap infoMap = ctx->lastSignalUIInfo; 07595 if (ctx->lastSignalUIInfoTime.elapsed() > 5000 || 07596 infoMap["callsign"].isEmpty()) 07597 { 07598 ctx->lastSignalUIInfo.clear(); 07599 ctx->GetPlayingInfoMap(ctx->lastSignalUIInfo); 07600 07601 infoMap = ctx->lastSignalUIInfo; 07602 ctx->lastSignalUIInfoTime.start(); 07603 } 07604 07605 int i = 0; 07606 SignalMonitorList::const_iterator it; 07607 for (it = slist.begin(); it != slist.end(); ++it) 07608 if ("error" == it->GetShortName()) 07609 infoMap[QString("error%1").arg(i++)] = it->GetName(); 07610 i = 0; 07611 for (it = slist.begin(); it != slist.end(); ++it) 07612 if ("message" == it->GetShortName()) 07613 infoMap[QString("message%1").arg(i++)] = it->GetName(); 07614 07615 uint sig = 0; 07616 float snr = 0.0f; 07617 uint ber = 0xffffffff; 07618 int pos = -1; 07619 int tuned = -1; 07620 QString pat(""), pmt(""), mgt(""), vct(""), nit(""), sdt(""), crypt(""); 07621 QString err = QString::null, msg = QString::null; 07622 for (it = slist.begin(); it != slist.end(); ++it) 07623 { 07624 if ("error" == it->GetShortName()) 07625 { 07626 err = it->GetName(); 07627 continue; 07628 } 07629 07630 if ("message" == it->GetShortName()) 07631 { 07632 msg = it->GetName(); 07633 LOG(VB_GENERAL, LOG_INFO, "msg: " + msg); 07634 continue; 07635 } 07636 07637 infoMap[it->GetShortName()] = QString::number(it->GetValue()); 07638 if ("signal" == it->GetShortName()) 07639 sig = it->GetNormalizedValue(0, 100); 07640 else if ("snr" == it->GetShortName()) 07641 snr = it->GetValue(); 07642 else if ("ber" == it->GetShortName()) 07643 ber = it->GetValue(); 07644 else if ("pos" == it->GetShortName()) 07645 pos = it->GetValue(); 07646 else if ("tuned" == it->GetShortName()) 07647 tuned = it->GetValue(); 07648 else if ("seen_pat" == it->GetShortName()) 07649 pat = it->IsGood() ? "a" : "_"; 07650 else if ("matching_pat" == it->GetShortName()) 07651 pat = it->IsGood() ? "A" : pat; 07652 else if ("seen_pmt" == it->GetShortName()) 07653 pmt = it->IsGood() ? "m" : "_"; 07654 else if ("matching_pmt" == it->GetShortName()) 07655 pmt = it->IsGood() ? "M" : pmt; 07656 else if ("seen_mgt" == it->GetShortName()) 07657 mgt = it->IsGood() ? "g" : "_"; 07658 else if ("matching_mgt" == it->GetShortName()) 07659 mgt = it->IsGood() ? "G" : mgt; 07660 else if ("seen_vct" == it->GetShortName()) 07661 vct = it->IsGood() ? "v" : "_"; 07662 else if ("matching_vct" == it->GetShortName()) 07663 vct = it->IsGood() ? "V" : vct; 07664 else if ("seen_nit" == it->GetShortName()) 07665 nit = it->IsGood() ? "n" : "_"; 07666 else if ("matching_nit" == it->GetShortName()) 07667 nit = it->IsGood() ? "N" : nit; 07668 else if ("seen_sdt" == it->GetShortName()) 07669 sdt = it->IsGood() ? "s" : "_"; 07670 else if ("matching_sdt" == it->GetShortName()) 07671 sdt = it->IsGood() ? "S" : sdt; 07672 else if ("seen_crypt" == it->GetShortName()) 07673 crypt = it->IsGood() ? "c" : "_"; 07674 else if ("matching_crypt" == it->GetShortName()) 07675 crypt = it->IsGood() ? "C" : crypt; 07676 } 07677 if (sig) 07678 infoMap["signal"] = QString::number(sig); // use normalized value 07679 07680 bool allGood = SignalMonitorValue::AllGood(slist); 07681 char tuneCode; 07682 QString slock = ("1" == infoMap["slock"]) ? "L" : "l"; 07683 QString lockMsg = (slock=="L") ? tr("Partial Lock") : tr("No Lock"); 07684 QString sigMsg = allGood ? tr("Lock") : lockMsg; 07685 07686 QString sigDesc = tr("Signal %1%").arg(sig,2); 07687 if (snr > 0.0f) 07688 sigDesc += " | " + tr("S/N %1dB").arg(log10f(snr), 3, 'f', 1); 07689 if (ber != 0xffffffff) 07690 sigDesc += " | " + tr("BE %1", "Bit Errors").arg(ber, 2); 07691 if ((pos >= 0) && (pos < 100)) 07692 sigDesc += " | " + tr("Rotor %1%").arg(pos,2); 07693 07694 if (tuned == 1) 07695 tuneCode = 't'; 07696 else if (tuned == 2) 07697 tuneCode = 'F'; 07698 else if (tuned == 3) 07699 tuneCode = 'T'; 07700 else 07701 tuneCode = '_'; 07702 07703 sigDesc = sigDesc + QString(" | (%1%2%3%4%5%6%7%8%9) %10") 07704 .arg(tuneCode).arg(slock).arg(pat).arg(pmt).arg(mgt).arg(vct) 07705 .arg(nit).arg(sdt).arg(crypt).arg(sigMsg); 07706 07707 if (!err.isEmpty()) 07708 sigDesc = err; 07709 else if (!msg.isEmpty()) 07710 sigDesc = msg; 07711 07712 osd = GetOSDLock(ctx); 07713 if (osd) 07714 { 07715 infoMap["description"] = sigDesc; 07716 osd->SetText("program_info", infoMap, kOSDTimeout_Med); 07717 } 07718 ReturnOSDLock(ctx, osd); 07719 07720 ctx->lastSignalMsg.clear(); 07721 ctx->lastSignalMsgTime.start(); 07722 07723 // Turn off lock timer if we have an "All Good" or good PMT 07724 if (allGood || (pmt == "M")) 07725 { 07726 lockTimerOn = false; 07727 lastLockSeenTime = QDateTime::currentDateTime(); 07728 } 07729 } 07730 07731 void TV::UpdateOSDTimeoutMessage(PlayerContext *ctx) 07732 { 07733 bool timed_out = false; 07734 07735 if (ctx->recorder) 07736 { 07737 QString input = ctx->recorder->GetInput(); 07738 uint timeout = ctx->recorder->GetSignalLockTimeout(input); 07739 timed_out = lockTimerOn && ((uint)lockTimer.elapsed() > timeout); 07740 } 07741 07742 OSD *osd = GetOSDLock(ctx); 07743 07744 if (!osd) 07745 { 07746 if (timed_out) 07747 { 07748 LOG(VB_GENERAL, LOG_ERR, LOC + 07749 "You have no OSD, but tuning has already taken too long."); 07750 } 07751 ReturnOSDLock(ctx, osd); 07752 return; 07753 } 07754 07755 bool showing = osd->DialogVisible(OSD_DLG_INFO); 07756 if (!timed_out) 07757 { 07758 if (showing) 07759 osd->DialogQuit(); 07760 ReturnOSDLock(ctx, osd); 07761 return; 07762 } 07763 07764 if (showing) 07765 { 07766 ReturnOSDLock(ctx, osd); 07767 return; 07768 } 07769 07770 // create dialog... 07771 static QString chan_up = GET_KEY("TV Playback", ACTION_CHANNELUP); 07772 static QString chan_down = GET_KEY("TV Playback", ACTION_CHANNELDOWN); 07773 static QString next_src = GET_KEY("TV Playback", "NEXTSOURCE"); 07774 static QString tog_cards = GET_KEY("TV Playback", "NEXTINPUT"); 07775 07776 QString message = tr( 07777 "You should have received a channel lock by now. " 07778 "You can continue to wait for a signal, or you " 07779 "can change the channel with %1 or %2, change " 07780 "video source (%3), inputs (%4), etc.") 07781 .arg(chan_up).arg(chan_down).arg(next_src).arg(tog_cards); 07782 07783 osd->DialogShow(OSD_DLG_INFO, message); 07784 QString action = "DIALOG_INFO_CHANNELLOCK_0"; 07785 osd->DialogAddButton(tr("OK"), action); 07786 osd->DialogBack("", action, true); 07787 07788 ReturnOSDLock(ctx, osd); 07789 } 07790 07791 void TV::UpdateLCD(void) 07792 { 07793 // Make sure the LCD information gets updated shortly 07794 QMutexLocker locker(&timerIdLock); 07795 if (lcdTimerId) 07796 KillTimer(lcdTimerId); 07797 lcdTimerId = StartTimer(1, __LINE__); 07798 } 07799 07800 void TV::ShowLCDChannelInfo(const PlayerContext *ctx) 07801 { 07802 LCD *lcd = LCD::Get(); 07803 ctx->LockPlayingInfo(__FILE__, __LINE__); 07804 if (!lcd || !ctx->playingInfo) 07805 { 07806 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 07807 return; 07808 } 07809 07810 QString title = ctx->playingInfo->GetTitle(); 07811 QString subtitle = ctx->playingInfo->GetSubtitle(); 07812 QString callsign = ctx->playingInfo->GetChannelSchedulingID(); 07813 07814 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 07815 07816 if ((callsign != lcdCallsign) || (title != lcdTitle) || 07817 (subtitle != lcdSubtitle)) 07818 { 07819 lcd->switchToChannel(callsign, title, subtitle); 07820 lcdCallsign = callsign; 07821 lcdTitle = title; 07822 lcdSubtitle = subtitle; 07823 } 07824 } 07825 07826 static void format_time(int seconds, QString &tMin, QString &tHrsMin) 07827 { 07828 int minutes = seconds / 60; 07829 int hours = minutes / 60; 07830 int min = minutes % 60; 07831 07832 tMin = TV::tr("%n minute(s)", "", minutes); 07833 tHrsMin.sprintf("%d:%02d", hours, min); 07834 } 07835 07836 07837 void TV::ShowLCDDVDInfo(const PlayerContext *ctx) 07838 { 07839 LCD *lcd = LCD::Get(); 07840 07841 if (!lcd || !ctx->buffer || !ctx->buffer->IsDVD()) 07842 { 07843 return; 07844 } 07845 07846 DVDRingBuffer *dvd = ctx->buffer->DVD(); 07847 QString dvdName, dvdSerial; 07848 QString mainStatus, subStatus; 07849 07850 if (!dvd->GetNameAndSerialNum(dvdName, dvdSerial)) 07851 { 07852 dvdName = tr("DVD"); 07853 } 07854 07855 if (dvd->IsInMenu()) 07856 { 07857 mainStatus = tr("Menu"); 07858 } 07859 else if (dvd->IsInStillFrame()) 07860 { 07861 mainStatus = tr("Still Frame"); 07862 } 07863 else 07864 { 07865 QString timeMins, timeHrsMin; 07866 int playingTitle, playingPart, totalParts; 07867 07868 dvd->GetPartAndTitle(playingPart, playingTitle); 07869 totalParts = dvd->NumPartsInTitle(); 07870 format_time(dvd->GetTotalTimeOfTitle(), timeMins, timeHrsMin); 07871 07872 mainStatus = tr("Title: %1 (%2)").arg(playingTitle).arg(timeHrsMin); 07873 subStatus = tr("Chapter: %1/%2").arg(playingPart).arg(totalParts); 07874 } 07875 if ((dvdName != lcdCallsign) || (mainStatus != lcdTitle) || 07876 (subStatus != lcdSubtitle)) 07877 { 07878 lcd->switchToChannel(dvdName, mainStatus, subStatus); 07879 lcdCallsign = dvdName; 07880 lcdTitle = mainStatus; 07881 lcdSubtitle = subStatus; 07882 } 07883 } 07884 07885 07886 bool TV::IsTunable(const PlayerContext *ctx, uint chanid, bool use_cache) 07887 { 07888 return !IsTunableOn(ctx,chanid,use_cache,true).empty(); 07889 } 07890 07891 static QString toCommaList(const QSet<uint> &list) 07892 { 07893 QString ret = ""; 07894 for (QSet<uint>::const_iterator it = list.begin(); it != list.end(); ++it) 07895 ret += QString("%1,").arg(*it); 07896 07897 if (ret.length()) 07898 return ret.left(ret.length()-1); 07899 07900 return ""; 07901 } 07902 07903 QSet<uint> TV::IsTunableOn( 07904 const PlayerContext *ctx, uint chanid, bool use_cache, bool early_exit) 07905 { 07906 QSet<uint> tunable_cards; 07907 07908 if (!chanid) 07909 { 07910 LOG(VB_CHANNEL, LOG_INFO, LOC + 07911 QString("IsTunableOn(%1) no").arg(chanid)); 07912 07913 return tunable_cards; 07914 } 07915 07916 uint mplexid = ChannelUtil::GetMplexID(chanid); 07917 mplexid = (32767 == mplexid) ? 0 : mplexid; 07918 07919 vector<uint> excluded_cards; 07920 if (ctx->recorder && ctx->pseudoLiveTVState == kPseudoNormalLiveTV) 07921 excluded_cards.push_back(ctx->GetCardID()); 07922 07923 uint sourceid = ChannelUtil::GetSourceIDForChannel(chanid); 07924 vector<uint> connected = RemoteRequestFreeRecorderList(excluded_cards); 07925 vector<uint> interesting = CardUtil::GetCardIDs(sourceid); 07926 07927 // filter disconnected cards 07928 vector<uint> cardids = excluded_cards; 07929 for (uint i = 0; i < connected.size(); i++) 07930 { 07931 for (uint j = 0; j < interesting.size(); j++) 07932 { 07933 if (connected[i] == interesting[j]) 07934 { 07935 cardids.push_back(interesting[j]); 07936 break; 07937 } 07938 } 07939 } 07940 07941 #if 0 07942 { 07943 QString msg = QString("cardids[%1]: ").arg(sourceid); 07944 for (uint i = 0; i < cardids.size(); i++) 07945 msg += QString("%1, ").arg(cardids[i]); 07946 LOG(VB_CHANNEL, LOG_INFO, msg); 07947 } 07948 #endif 07949 07950 for (uint i = 0; i < cardids.size(); i++) 07951 { 07952 vector<InputInfo> inputs; 07953 07954 bool used_cache = false; 07955 if (use_cache) 07956 { 07957 QMutexLocker locker(&is_tunable_cache_lock); 07958 if (is_tunable_cache_inputs.contains(cardids[i])) 07959 { 07960 inputs = is_tunable_cache_inputs[cardids[i]]; 07961 used_cache = true; 07962 } 07963 } 07964 07965 if (!used_cache) 07966 { 07967 inputs = RemoteRequestFreeInputList(cardids[i], excluded_cards); 07968 QMutexLocker locker(&is_tunable_cache_lock); 07969 is_tunable_cache_inputs[cardids[i]] = inputs; 07970 } 07971 07972 #if 0 07973 { 07974 QString msg = QString("inputs[%1]: ").arg(cardids[i]); 07975 for (uint j = 0; j < inputs.size(); j++) 07976 msg += QString("%1, ").arg(inputs[j].inputid); 07977 LOG(VB_CHANNEL, LOG_INFO, msg); 07978 } 07979 #endif 07980 07981 for (uint j = 0; j < inputs.size(); j++) 07982 { 07983 if (inputs[j].sourceid != sourceid) 07984 continue; 07985 07986 if (inputs[j].mplexid && 07987 inputs[j].mplexid != mplexid) 07988 continue; 07989 07990 tunable_cards.insert(cardids[i]); 07991 07992 break; 07993 } 07994 07995 if (early_exit && !tunable_cards.empty()) 07996 break; 07997 } 07998 07999 if (tunable_cards.empty()) 08000 { 08001 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("IsTunableOn(%1) no") 08002 .arg(chanid)); 08003 } 08004 else 08005 { 08006 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("IsTunableOn(%1) yes { %2 }") 08007 .arg(chanid).arg(toCommaList(tunable_cards))); 08008 } 08009 08010 return tunable_cards; 08011 } 08012 08013 void TV::ClearTunableCache(void) 08014 { 08015 QMutexLocker locker(&is_tunable_cache_lock); 08016 LOG(VB_CHANNEL, LOG_INFO, LOC + "ClearTunableCache()"); 08017 is_tunable_cache_inputs.clear(); 08018 } 08019 08020 bool TV::StartEmbedding(const QRect &embedRect) 08021 { 08022 PlayerContext *ctx = GetPlayerReadLock(-1, __FILE__, __LINE__); 08023 if (!ctx) 08024 return false; 08025 08026 WId wid = GetMythMainWindow()->GetPaintWindow()->winId(); 08027 08028 if (!ctx->IsNullVideoDesired()) 08029 ctx->StartEmbedding(wid, embedRect); 08030 else 08031 { 08032 LOG(VB_GENERAL, LOG_WARNING, LOC + 08033 QString("StartEmbedding called with null video context #%1") 08034 .arg(find_player_index(ctx))); 08035 ctx->ResizePIPWindow(embedRect); 08036 } 08037 08038 // Hide any PIP windows... 08039 PlayerContext *mctx = GetPlayer(ctx, 0); 08040 for (uint i = 1; (mctx == ctx) && (i < player.size()); i++) 08041 { 08042 GetPlayer(ctx,i)->LockDeletePlayer(__FILE__, __LINE__); 08043 if (GetPlayer(ctx,i)->player) 08044 GetPlayer(ctx,i)->player->SetPIPVisible(false); 08045 GetPlayer(ctx,i)->UnlockDeletePlayer(__FILE__, __LINE__); 08046 } 08047 08048 // Start checking for end of file for embedded window.. 08049 QMutexLocker locker(&timerIdLock); 08050 if (embedCheckTimerId) 08051 KillTimer(embedCheckTimerId); 08052 embedCheckTimerId = StartTimer(kEmbedCheckFrequency, __LINE__); 08053 08054 bool embedding = ctx->IsEmbedding(); 08055 ReturnPlayerLock(ctx); 08056 return embedding; 08057 } 08058 08059 void TV::StopEmbedding(void) 08060 { 08061 PlayerContext *ctx = GetPlayerReadLock(-1, __FILE__, __LINE__); 08062 if (!ctx) 08063 return; 08064 08065 if (ctx->IsEmbedding()) 08066 ctx->StopEmbedding(); 08067 08068 // Undo any PIP hiding 08069 PlayerContext *mctx = GetPlayer(ctx, 0); 08070 for (uint i = 1; (mctx == ctx) && (i < player.size()); i++) 08071 { 08072 GetPlayer(ctx,i)->LockDeletePlayer(__FILE__, __LINE__); 08073 if (GetPlayer(ctx,i)->player) 08074 GetPlayer(ctx,i)->player->SetPIPVisible(true); 08075 GetPlayer(ctx,i)->UnlockDeletePlayer(__FILE__, __LINE__); 08076 } 08077 08078 // Stop checking for end of file for embedded window.. 08079 QMutexLocker locker(&timerIdLock); 08080 if (embedCheckTimerId) 08081 KillTimer(embedCheckTimerId); 08082 embedCheckTimerId = 0; 08083 08084 ReturnPlayerLock(ctx); 08085 } 08086 08087 void TV::DrawUnusedRects(void) 08088 { 08089 if (disableDrawUnusedRects) 08090 return; 08091 08092 LOG(VB_PLAYBACK, LOG_INFO, LOC + "DrawUnusedRects() -- begin"); 08093 08094 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 08095 for (uint i = 0; mctx && (i < player.size()); i++) 08096 { 08097 PlayerContext *ctx = GetPlayer(mctx, i); 08098 ctx->LockDeletePlayer(__FILE__, __LINE__); 08099 if (ctx->player) 08100 ctx->player->ExposeEvent(); 08101 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08102 } 08103 ReturnPlayerLock(mctx); 08104 08105 LOG(VB_PLAYBACK, LOG_INFO, LOC + "DrawUnusedRects() -- end"); 08106 } 08107 08108 vector<bool> TV::DoSetPauseState(PlayerContext *lctx, const vector<bool> &pause) 08109 { 08110 vector<bool> was_paused; 08111 vector<float> times; 08112 for (uint i = 0; lctx && i < player.size() && i < pause.size(); i++) 08113 { 08114 PlayerContext *actx = GetPlayer(lctx, i); 08115 if (actx) 08116 was_paused.push_back(ContextIsPaused(actx, __FILE__, __LINE__)); 08117 float time = 0.0f; 08118 if (pause[i] ^ was_paused.back()) 08119 time = DoTogglePauseStart(GetPlayer(lctx,i)); 08120 times.push_back(time); 08121 } 08122 08123 for (uint i = 0; lctx && i < player.size() && i < pause.size(); i++) 08124 { 08125 if (pause[i] ^ was_paused[i]) 08126 DoTogglePauseFinish(GetPlayer(lctx,i), times[i], false); 08127 } 08128 08129 return was_paused; 08130 } 08131 08132 void TV::DoEditSchedule(int editType) 08133 { 08134 if ((editType == kScheduleProgramGuide && !RunProgramGuidePtr) || 08135 (editType == kScheduleProgramFinder && !RunProgramFinderPtr) || 08136 (editType == kScheduledRecording && !RunScheduleEditorPtr) || 08137 (editType == kViewSchedule && !RunViewScheduledPtr)) 08138 { 08139 return; 08140 } 08141 08142 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 08143 08144 actx->LockPlayingInfo(__FILE__, __LINE__); 08145 if (!actx->playingInfo) 08146 { 08147 LOG(VB_GENERAL, LOG_ERR, LOC + 08148 "doEditSchedule(): no active ctx playingInfo."); 08149 actx->UnlockPlayingInfo(__FILE__, __LINE__); 08150 ReturnPlayerLock(actx); 08151 return; 08152 } 08153 08154 // Collect channel info 08155 const ProgramInfo pginfo(*actx->playingInfo); 08156 uint chanid = pginfo.GetChanID(); 08157 QString channum = pginfo.GetChanNum(); 08158 actx->UnlockPlayingInfo(__FILE__, __LINE__); 08159 08160 ClearOSD(actx); 08161 08162 // Pause playback as needed... 08163 bool pause_active = true; 08164 bool isNearEnd = false; 08165 bool isLiveTV = StateIsLiveTV(GetState(actx)); 08166 bool allowEmbedding = false; 08167 bool paused = false; 08168 08169 { 08170 actx->LockDeletePlayer(__FILE__, __LINE__); 08171 pause_active = !actx->player || !actx->player->GetVideoOutput(); 08172 if (actx->player) 08173 { 08174 paused = actx->player->IsPaused(); 08175 if (actx->player->GetVideoOutput()) 08176 allowEmbedding = 08177 actx->player->GetVideoOutput()->AllowPreviewEPG(); 08178 if (!pause_active) 08179 isNearEnd = actx->player->IsNearEnd(); 08180 } 08181 actx->UnlockDeletePlayer(__FILE__, __LINE__); 08182 } 08183 08184 pause_active |= kScheduledRecording == editType; 08185 pause_active |= kViewSchedule == editType; 08186 pause_active |= kScheduleProgramFinder == editType; 08187 pause_active |= !isLiveTV && (!db_continue_embedded || isNearEnd); 08188 pause_active |= paused; 08189 vector<bool> do_pause; 08190 do_pause.insert(do_pause.begin(), true, player.size()); 08191 do_pause[find_player_index(actx)] = pause_active; 08192 LOG(VB_PLAYBACK, LOG_INFO, LOC + 08193 QString("Pausing player: %1").arg(pause_active)); 08194 08195 saved_pause = DoSetPauseState(actx, do_pause); 08196 08197 // Resize window to the MythTV GUI size 08198 PlayerContext *mctx = GetPlayer(actx,0); 08199 mctx->LockDeletePlayer(__FILE__, __LINE__); 08200 if (mctx->player && mctx->player->GetVideoOutput()) 08201 mctx->player->GetVideoOutput()->ResizeForGui(); 08202 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 08203 ReturnPlayerLock(actx); 08204 MythMainWindow *mwnd = GetMythMainWindow(); 08205 if (!db_use_gui_size_for_tv || !db_use_fixed_size) 08206 { 08207 mwnd->setFixedSize(saved_gui_bounds.size()); 08208 mwnd->setGeometry(saved_gui_bounds.left(), saved_gui_bounds.top(), 08209 saved_gui_bounds.width(), saved_gui_bounds.height()); 08210 } 08211 08212 // Actually show the pop-up UI 08213 switch (editType) 08214 { 08215 case kScheduleProgramGuide: 08216 { 08217 isEmbedded = (isLiveTV && !pause_active && allowEmbedding); 08218 RunProgramGuidePtr(chanid, channum, this, 08219 isEmbedded, true, channelGroupId); 08220 ignoreKeyPresses = true; 08221 break; 08222 } 08223 case kScheduleProgramFinder: 08224 { 08225 isEmbedded = (isLiveTV && !pause_active && allowEmbedding); 08226 RunProgramFinderPtr(this, isEmbedded, true); 08227 ignoreKeyPresses = true; 08228 break; 08229 } 08230 case kScheduledRecording: 08231 { 08232 RunScheduleEditorPtr(&pginfo, (void *)this); 08233 ignoreKeyPresses = true; 08234 break; 08235 } 08236 case kViewSchedule: 08237 { 08238 RunViewScheduledPtr((void *)this, !pause_active); 08239 ignoreKeyPresses = true; 08240 break; 08241 } 08242 case kPlaybackBox: 08243 { 08244 RunPlaybackBoxPtr((void *)this, !pause_active); 08245 ignoreKeyPresses = true; 08246 break; 08247 } 08248 } 08249 08250 // If the video is paused, don't paint its unused rects & chromakey 08251 disableDrawUnusedRects = pause_active; 08252 08253 // We are embedding in a mythui window so assuming no one 08254 // else has disabled painting show the MythUI window again. 08255 if (GetMythMainWindow() && weDisabledGUI) 08256 { 08257 GetMythMainWindow()->PopDrawDisabled(); 08258 weDisabledGUI = false; 08259 } 08260 } 08261 08262 void TV::EditSchedule(const PlayerContext *ctx, int editType) 08263 { 08264 // post the request so the guide will be created in the UI thread 08265 QString message = QString("START_EPG %1").arg(editType); 08266 MythEvent* me = new MythEvent(message); 08267 qApp->postEvent(this, me); 08268 } 08269 08270 void TV::ChangeVolume(PlayerContext *ctx, bool up, int newvolume) 08271 { 08272 ctx->LockDeletePlayer(__FILE__, __LINE__); 08273 if (!ctx->player || 08274 (ctx->player && !ctx->player->PlayerControlsVolume())) 08275 { 08276 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08277 return; 08278 } 08279 08280 bool setabsolute = (newvolume >= 0 && newvolume <= 100); 08281 08282 if (ctx->player->IsMuted() && (up || setabsolute)) 08283 ToggleMute(ctx); 08284 08285 uint curvol = setabsolute ? 08286 ctx->player->SetVolume(newvolume) : 08287 ctx->player->AdjustVolume((up) ? +2 : -2); 08288 08289 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08290 08291 if (!browsehelper->IsBrowsing()) 08292 { 08293 UpdateOSDStatus(ctx, tr("Adjust Volume"), tr("Volume"), 08294 QString::number(curvol), 08295 kOSDFunctionalType_PictureAdjust, "%", curvol * 10, 08296 kOSDTimeout_Med); 08297 SetUpdateOSDPosition(false); 08298 08299 if (LCD *lcd = LCD::Get()) 08300 { 08301 QString appName = tr("Video"); 08302 08303 if (StateIsLiveTV(GetState(ctx))) 08304 appName = tr("TV"); 08305 08306 if (ctx->buffer && ctx->buffer->IsDVD()) 08307 appName = tr("DVD"); 08308 08309 lcd->switchToVolume(appName); 08310 lcd->setVolumeLevel((float)curvol / 100); 08311 08312 QMutexLocker locker(&timerIdLock); 08313 if (lcdVolumeTimerId) 08314 KillTimer(lcdVolumeTimerId); 08315 08316 lcdVolumeTimerId = StartTimer(2000, __LINE__); 08317 } 08318 } 08319 } 08320 08321 void TV::ToggleTimeStretch(PlayerContext *ctx) 08322 { 08323 if (ctx->ts_normal == 1.0f) 08324 { 08325 ctx->ts_normal = ctx->ts_alt; 08326 } 08327 else 08328 { 08329 ctx->ts_alt = ctx->ts_normal; 08330 ctx->ts_normal = 1.0f; 08331 } 08332 ChangeTimeStretch(ctx, 0, false); 08333 } 08334 08335 void TV::ChangeTimeStretch(PlayerContext *ctx, int dir, bool allowEdit) 08336 { 08337 const float kTimeStretchMin = 0.5; 08338 const float kTimeStretchMax = 2.0; 08339 float new_ts_normal = ctx->ts_normal + 0.05*dir; 08340 stretchAdjustment = allowEdit; 08341 08342 if (new_ts_normal > kTimeStretchMax && 08343 ctx->ts_normal < kTimeStretchMax) 08344 { 08345 new_ts_normal = kTimeStretchMax; 08346 } 08347 else if (new_ts_normal < kTimeStretchMin && 08348 ctx->ts_normal > kTimeStretchMin) 08349 { 08350 new_ts_normal = kTimeStretchMin; 08351 } 08352 08353 if (new_ts_normal > kTimeStretchMax || 08354 new_ts_normal < kTimeStretchMin) 08355 { 08356 return; 08357 } 08358 08359 ctx->ts_normal = new_ts_normal; 08360 08361 ctx->LockDeletePlayer(__FILE__, __LINE__); 08362 if (ctx->player && !ctx->player->IsPaused()) 08363 ctx->player->Play(ctx->ts_normal, true); 08364 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08365 08366 if (!browsehelper->IsBrowsing()) 08367 { 08368 if (!allowEdit) 08369 { 08370 UpdateOSDSeekMessage(ctx, ctx->GetPlayMessage(), kOSDTimeout_Med); 08371 } 08372 else 08373 { 08374 UpdateOSDStatus(ctx, tr("Adjust Time Stretch"), tr("Time Stretch"), 08375 QString::number(ctx->ts_normal), 08376 kOSDFunctionalType_TimeStretchAdjust, "X", 08377 (int)(ctx->ts_normal*(1000/kTimeStretchMax)), 08378 kOSDTimeout_Med); 08379 SetUpdateOSDPosition(false); 08380 } 08381 } 08382 08383 SetSpeedChangeTimer(0, __LINE__); 08384 } 08385 08386 void TV::EnableUpmix(PlayerContext *ctx, bool enable, bool toggle) 08387 { 08388 if (!ctx->player || !ctx->player->HasAudioOut()) 08389 return; 08390 QString text; 08391 08392 bool enabled = false; 08393 08394 ctx->LockDeletePlayer(__FILE__, __LINE__); 08395 if (toggle) 08396 enabled = ctx->player->GetAudio()->EnableUpmix(false, true); 08397 else 08398 enabled = ctx->player->GetAudio()->EnableUpmix(enable); 08399 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08400 08401 if (!browsehelper->IsBrowsing()) 08402 SetOSDMessage(ctx, enabled ? tr("Upmixer On") : tr("Upmixer Off")); 08403 } 08404 08405 void TV::ChangeSubtitleZoom(PlayerContext *ctx, int dir) 08406 { 08407 ctx->LockDeletePlayer(__FILE__, __LINE__); 08408 if (!ctx->player) 08409 { 08410 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08411 return; 08412 } 08413 08414 OSD *osd = GetOSDLock(ctx); 08415 SubtitleScreen *subs = NULL; 08416 if (osd) 08417 subs = osd->InitSubtitles(); 08418 ReturnOSDLock(ctx, osd); 08419 subtitleZoomAdjustment = true; 08420 bool showing = ctx->player->GetCaptionsEnabled(); 08421 int newval = (subs ? subs->GetZoom() : 100) + dir; 08422 newval = max(50, newval); 08423 newval = min(200, newval); 08424 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08425 08426 if (showing && !browsehelper->IsBrowsing()) 08427 { 08428 UpdateOSDStatus(ctx, tr("Adjust Subtitle Zoom"), tr("Subtitle Zoom"), 08429 QString::number(newval), 08430 kOSDFunctionalType_SubtitleZoomAdjust, 08431 "%", newval * 1000 / 200, kOSDTimeout_Long); 08432 SetUpdateOSDPosition(false); 08433 if (subs) 08434 subs->SetZoom(newval); 08435 } 08436 } 08437 08438 // dir in 10ms jumps 08439 void TV::ChangeAudioSync(PlayerContext *ctx, int dir, int newsync) 08440 { 08441 long long newval; 08442 08443 ctx->LockDeletePlayer(__FILE__, __LINE__); 08444 if (!ctx->player) 08445 { 08446 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08447 return; 08448 } 08449 08450 audiosyncAdjustment = true; 08451 newval = ctx->player->AdjustAudioTimecodeOffset(dir * 10, newsync); 08452 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08453 08454 if (!browsehelper->IsBrowsing()) 08455 { 08456 int val = (int)newval; 08457 UpdateOSDStatus(ctx, tr("Adjust Audio Sync"), tr("Audio Sync"), 08458 QString::number(val), 08459 kOSDFunctionalType_AudioSyncAdjust, 08460 "ms", (val/2) + 500, kOSDTimeout_Med); 08461 SetUpdateOSDPosition(false); 08462 } 08463 } 08464 08465 void TV::ToggleMute(PlayerContext *ctx, const bool muteIndividualChannels) 08466 { 08467 ctx->LockDeletePlayer(__FILE__, __LINE__); 08468 if (!ctx->player || !ctx->player->HasAudioOut() || 08469 !ctx->player->PlayerControlsVolume()) 08470 { 08471 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08472 return; 08473 } 08474 08475 MuteState mute_status; 08476 08477 if (!muteIndividualChannels) 08478 { 08479 ctx->player->SetMuted(!ctx->player->IsMuted()); 08480 mute_status = (ctx->player->IsMuted()) ? kMuteAll : kMuteOff; 08481 } 08482 else 08483 { 08484 mute_status = ctx->player->IncrMuteState(); 08485 } 08486 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08487 08488 QString text; 08489 08490 switch (mute_status) 08491 { 08492 case kMuteOff: text = tr("Mute Off"); break; 08493 case kMuteAll: text = tr("Mute On"); break; 08494 case kMuteLeft: text = tr("Left Channel Muted"); break; 08495 case kMuteRight: text = tr("Right Channel Muted"); break; 08496 } 08497 08498 SetOSDMessage(ctx, text); 08499 } 08500 08501 void TV::ToggleSleepTimer(const PlayerContext *ctx) 08502 { 08503 QString text; 08504 08505 // increment sleep index, cycle through 08506 if (++sleep_index == sleep_times.size()) 08507 sleep_index = 0; 08508 08509 // set sleep timer to next sleep_index timeout 08510 if (sleepTimerId) 08511 { 08512 KillTimer(sleepTimerId); 08513 sleepTimerId = 0; 08514 sleepTimerTimeout = 0; 08515 } 08516 08517 if (sleep_times[sleep_index].seconds != 0) 08518 { 08519 sleepTimerTimeout = sleep_times[sleep_index].seconds * 1000; 08520 sleepTimerId = StartTimer(sleepTimerTimeout, __LINE__); 08521 } 08522 08523 text = tr("Sleep ") + " " + sleep_times[sleep_index].dispString; 08524 08525 if (!browsehelper->IsBrowsing()) 08526 SetOSDMessage(ctx, text); 08527 } 08528 08529 void TV::ShowOSDSleep(void) 08530 { 08531 KillTimer(sleepTimerId); 08532 sleepTimerId = 0; 08533 08534 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 08535 OSD *osd = GetOSDLock(mctx); 08536 if (osd) 08537 { 08538 QString message = QObject::tr( 08539 "MythTV was set to sleep after %1 minutes and " 08540 "will exit in %d seconds.\n" 08541 "Do you wish to continue watching?") 08542 .arg(sleepTimerTimeout * (1.0f/60000.0f)); 08543 08544 osd->DialogShow(OSD_DLG_SLEEP, message, kSleepTimerDialogTimeout); 08545 osd->DialogAddButton(tr("Yes"), "DIALOG_SLEEP_YES_0"); 08546 osd->DialogAddButton(tr("No"), "DIALOG_SLEEP_NO_0"); 08547 } 08548 ReturnOSDLock(mctx, osd); 08549 ReturnPlayerLock(mctx); 08550 08551 sleepDialogTimerId = StartTimer(kSleepTimerDialogTimeout, __LINE__); 08552 } 08553 08554 void TV::HandleOSDSleep(PlayerContext *ctx, QString action) 08555 { 08556 if (!DialogIsVisible(ctx, OSD_DLG_SLEEP)) 08557 return; 08558 08559 if (action == "YES") 08560 { 08561 if (sleepDialogTimerId) 08562 { 08563 KillTimer(sleepDialogTimerId); 08564 sleepDialogTimerId = 0; 08565 } 08566 sleepTimerId = StartTimer(sleepTimerTimeout * 1000, __LINE__); 08567 } 08568 else 08569 { 08570 LOG(VB_GENERAL, LOG_INFO, LOC + "No longer watching TV, exiting"); 08571 SetExitPlayer(true, true); 08572 } 08573 } 08574 08575 void TV::SleepDialogTimeout(void) 08576 { 08577 KillTimer(sleepDialogTimerId); 08578 sleepDialogTimerId = 0; 08579 08580 LOG(VB_GENERAL, LOG_INFO, LOC + "Sleep timeout reached, exiting player."); 08581 08582 SetExitPlayer(true, true); 08583 } 08584 08593 void TV::ShowOSDIdle(void) 08594 { 08595 KillTimer(idleTimerId); 08596 idleTimerId = 0; 08597 08598 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 08599 OSD *osd = GetOSDLock(mctx); 08600 if (osd) 08601 { 08602 QString message = QObject::tr( 08603 "MythTV has been idle for %1 minutes and " 08604 "will exit in %d seconds. Are you still watching?") 08605 .arg(db_idle_timeout * (1.0f/60000.0f)); 08606 08607 osd->DialogShow(OSD_DLG_IDLE, message, kIdleTimerDialogTimeout); 08608 osd->DialogAddButton(tr("Yes"), "DIALOG_IDLE_YES_0"); 08609 osd->DialogAddButton(tr("No"), "DIALOG_IDLE_NO_0"); 08610 } 08611 ReturnOSDLock(mctx, osd); 08612 ReturnPlayerLock(mctx); 08613 08614 idleDialogTimerId = StartTimer(kIdleTimerDialogTimeout, __LINE__); 08615 } 08616 08617 void TV::HandleOSDIdle(PlayerContext *ctx, QString action) 08618 { 08619 if (!DialogIsVisible(ctx, OSD_DLG_IDLE)) 08620 return; 08621 08622 if (action == "YES") 08623 { 08624 if (idleDialogTimerId) 08625 { 08626 KillTimer(idleDialogTimerId); 08627 idleDialogTimerId = 0; 08628 } 08629 if (idleTimerId) 08630 KillTimer(idleTimerId); 08631 idleTimerId = StartTimer(db_idle_timeout, __LINE__); 08632 } 08633 else 08634 { 08635 LOG(VB_GENERAL, LOG_INFO, LOC + "No longer watching LiveTV, exiting"); 08636 SetExitPlayer(true, true); 08637 } 08638 } 08639 08640 void TV::IdleDialogTimeout(void) 08641 { 08642 KillTimer(idleDialogTimerId); 08643 idleDialogTimerId = 0; 08644 08645 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 08646 if (StateIsLiveTV(mctx->GetState())) 08647 { 08648 LOG(VB_GENERAL, LOG_INFO, LOC + "Idle timeout reached, leaving LiveTV"); 08649 SetExitPlayer(true, true); 08650 } 08651 ReturnPlayerLock(mctx); 08652 } 08653 08654 void TV::ToggleAspectOverride(PlayerContext *ctx, AspectOverrideMode aspectMode) 08655 { 08656 ctx->LockDeletePlayer(__FILE__, __LINE__); 08657 if (!ctx->player) 08658 { 08659 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08660 return; 08661 } 08662 ctx->player->ToggleAspectOverride(aspectMode); 08663 QString text = toString(ctx->player->GetAspectOverride()); 08664 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08665 08666 SetOSDMessage(ctx, text); 08667 } 08668 08669 void TV::ToggleAdjustFill(PlayerContext *ctx, AdjustFillMode adjustfillMode) 08670 { 08671 if (ctx != GetPlayer(ctx,0) || ctx->IsPBP()) 08672 return; 08673 08674 ctx->LockDeletePlayer(__FILE__, __LINE__); 08675 if (!ctx->player) 08676 { 08677 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08678 return; 08679 } 08680 ctx->player->ToggleAdjustFill(adjustfillMode); 08681 QString text = toString(ctx->player->GetAdjustFill()); 08682 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08683 08684 SetOSDMessage(ctx, text); 08685 } 08686 08687 void TV::PauseAudioUntilBuffered(PlayerContext *ctx) 08688 { 08689 if (!ctx) 08690 return; 08691 08692 ctx->LockDeletePlayer(__FILE__, __LINE__); 08693 if (ctx->player) 08694 ctx->player->GetAudio()->PauseAudioUntilBuffered(); 08695 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08696 } 08697 08699 void TV::customEvent(QEvent *e) 08700 { 08701 if (e->type() == MythEvent::kUpdateTvProgressEventType && myWindow) 08702 { 08703 myWindow->UpdateProgress(); 08704 return; 08705 } 08706 08707 if (e->type() == MythEvent::MythUserMessage) 08708 { 08709 MythEvent *me = reinterpret_cast<MythEvent*>(e); 08710 QString message = me->Message(); 08711 08712 if (message.isEmpty()) 08713 return; 08714 08715 uint timeout = 0; 08716 if (me->ExtraDataCount() == 1) 08717 { 08718 uint t = me->ExtraData(0).toUInt(); 08719 if (t > 0 && t < 1000) 08720 timeout = t * 1000; 08721 } 08722 08723 if (timeout > 0) 08724 message += " (%d)"; 08725 08726 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 08727 OSD *osd = GetOSDLock(mctx); 08728 if (osd) 08729 osd->DialogShow(OSD_DLG_CONFIRM, message, timeout); 08730 ReturnOSDLock(mctx, osd); 08731 ReturnPlayerLock(mctx); 08732 08733 return; 08734 } 08735 08736 if (e->type() == MythEvent::kUpdateBrowseInfoEventType) 08737 { 08738 UpdateBrowseInfoEvent *b = 08739 reinterpret_cast<UpdateBrowseInfoEvent*>(e); 08740 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 08741 OSD *osd = GetOSDLock(mctx); 08742 if (osd) 08743 { 08744 osd->SetText("browse_info", b->im, kOSDTimeout_None); 08745 osd->SetExpiry("browse_info", kOSDTimeout_None); 08746 } 08747 ReturnOSDLock(mctx, osd); 08748 ReturnPlayerLock(mctx); 08749 return; 08750 } 08751 08752 if (e->type() == DialogCompletionEvent::kEventType) 08753 { 08754 DialogCompletionEvent *dce = 08755 reinterpret_cast<DialogCompletionEvent*>(e); 08756 OSDDialogEvent(dce->GetResult(), dce->GetResultText(), 08757 dce->GetData().toString()); 08758 return; 08759 } 08760 08761 if (e->type() == OSDHideEvent::kEventType) 08762 { 08763 OSDHideEvent *ce = reinterpret_cast<OSDHideEvent*>(e); 08764 HandleOSDClosed(ce->GetFunctionalType()); 08765 return; 08766 } 08767 08768 if (e->type() != MythEvent::MythEventMessage) 08769 return; 08770 08771 uint cardnum = 0; 08772 MythEvent *me = reinterpret_cast<MythEvent*>(e); 08773 QString message = me->Message(); 08774 08775 // TODO Go through these and make sure they make sense... 08776 QStringList tokens = message.split(" ", QString::SkipEmptyParts); 08777 08778 if (me->ExtraDataCount() == 1) 08779 { 08780 PlayerContext *ctx = GetPlayerReadLock(0, __FILE__, __LINE__); 08781 int value = me->ExtraData(0).toInt(); 08782 if (message == ACTION_SETVOLUME) 08783 ChangeVolume(ctx, false, value); 08784 else if (message == ACTION_SETAUDIOSYNC) 08785 ChangeAudioSync(ctx, 0, value); 08786 else if (message == ACTION_SETBRIGHTNESS) 08787 DoChangePictureAttribute(ctx, kAdjustingPicture_Playback, 08788 kPictureAttribute_Brightness, 08789 false, value); 08790 else if (message == ACTION_SETCONTRAST) 08791 DoChangePictureAttribute(ctx, kAdjustingPicture_Playback, 08792 kPictureAttribute_Contrast, 08793 false, value); 08794 else if (message == ACTION_SETCOLOUR) 08795 DoChangePictureAttribute(ctx, kAdjustingPicture_Playback, 08796 kPictureAttribute_Colour, 08797 false, value); 08798 else if (message == ACTION_SETHUE) 08799 DoChangePictureAttribute(ctx, kAdjustingPicture_Playback, 08800 kPictureAttribute_Hue, 08801 false, value); 08802 else if (message == ACTION_JUMPCHAPTER) 08803 DoJumpChapter(ctx, value); 08804 else if (message == ACTION_SWITCHTITLE) 08805 DoSwitchTitle(ctx, value - 1); 08806 else if (message == ACTION_SWITCHANGLE) 08807 DoSwitchAngle(ctx, value); 08808 else if (message == ACTION_SEEKABSOLUTE) 08809 DoSeekAbsolute(ctx, value, /*honorCutlist*/true); 08810 ReturnPlayerLock(ctx); 08811 } 08812 08813 if (message == ACTION_SCREENSHOT) 08814 { 08815 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 08816 int width = 0; 08817 int height = 0; 08818 QString filename; 08819 08820 if (me->ExtraDataCount() >= 2) 08821 { 08822 width = me->ExtraData(0).toInt(); 08823 height = me->ExtraData(1).toInt(); 08824 08825 if (me->ExtraDataCount() == 3) 08826 filename = me->ExtraData(2); 08827 } 08828 if (mctx && mctx->player && 08829 mctx->player->GetScreenShot(width, height, filename)) 08830 { 08831 } 08832 else 08833 { 08834 GetMythMainWindow()->ScreenShot(width, height, filename); 08835 } 08836 ReturnPlayerLock(mctx); 08837 } 08838 else if (message == ACTION_GETSTATUS) 08839 { 08840 GetStatus(); 08841 } 08842 else if (message.left(14) == "DONE_RECORDING") 08843 { 08844 int seconds = 0; 08845 //long long frames = 0; 08846 if (tokens.size() >= 4) 08847 { 08848 cardnum = tokens[1].toUInt(); 08849 seconds = tokens[2].toInt(); 08850 //frames = tokens[3].toLongLong(); 08851 } 08852 08853 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 08854 for (uint i = 0; mctx && (i < player.size()); i++) 08855 { 08856 PlayerContext *ctx = GetPlayer(mctx, i); 08857 if (ctx->GetState() == kState_WatchingRecording) 08858 { 08859 if (ctx->recorder && (cardnum == ctx->GetCardID())) 08860 { 08861 ctx->LockDeletePlayer(__FILE__, __LINE__); 08862 if (ctx->player) 08863 { 08864 ctx->player->SetWatchingRecording(false); 08865 ctx->player->SetLength(seconds); 08866 } 08867 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08868 08869 ctx->ChangeState(kState_WatchingPreRecorded); 08870 ScheduleStateChange(ctx); 08871 } 08872 } 08873 else if (StateIsLiveTV(ctx->GetState())) 08874 { 08875 if (ctx->recorder && cardnum == ctx->GetCardID() && 08876 ctx->tvchain && ctx->tvchain->HasNext()) 08877 { 08878 ctx->LockDeletePlayer(__FILE__, __LINE__); 08879 if (ctx->player) 08880 { 08881 ctx->player->SetWatchingRecording(false); 08882 ctx->player->SetLength(seconds); 08883 } 08884 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 08885 } 08886 } 08887 } 08888 ReturnPlayerLock(mctx); 08889 } 08890 08891 if (message.left(14) == "ASK_RECORDING ") 08892 { 08893 int timeuntil = 0, hasrec = 0, haslater = 0; 08894 if (tokens.size() >= 5) 08895 { 08896 cardnum = tokens[1].toUInt(); 08897 timeuntil = tokens[2].toInt(); 08898 hasrec = tokens[3].toInt(); 08899 haslater = tokens[4].toInt(); 08900 } 08901 LOG(VB_GENERAL, LOG_INFO, 08902 LOC + message + QString(" hasrec: %1 haslater: %2") 08903 .arg(hasrec).arg(haslater)); 08904 08905 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 08906 if (mctx->recorder && cardnum == mctx->GetCardID()) 08907 { 08908 AskAllowRecording(mctx, me->ExtraDataList(), 08909 timeuntil, hasrec, haslater); 08910 } 08911 08912 for (uint i = 1; i < player.size(); i++) 08913 { 08914 PlayerContext *ctx = GetPlayer(mctx, i); 08915 if (ctx->recorder && ctx->GetCardID() == cardnum) 08916 { 08917 LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling PxP for recording"); 08918 QString type = ctx->IsPIP() ? 08919 tr("PiP", "Picture-in-Picture") : 08920 tr("PbP", "Picture-by-Picture"); 08921 StopStuff(mctx, ctx, true, true, true); 08922 SetOSDMessage(mctx, tr("Disabling %1 for recording").arg(type)); 08923 } 08924 } 08925 ReturnPlayerLock(mctx); 08926 } 08927 08928 if (message.left(11) == "QUIT_LIVETV") 08929 { 08930 cardnum = (tokens.size() >= 2) ? tokens[1].toUInt() : 0; 08931 08932 PlayerContext *mctx = GetPlayerReadLock(-1, __FILE__, __LINE__); 08933 int match = -1; 08934 for (uint i = 0; mctx && (i < player.size()); i++) 08935 { 08936 PlayerContext *ctx = GetPlayer(mctx, i); 08937 match = (ctx->GetCardID() == cardnum) ? i : match; 08938 } 08939 08940 if (match >= 0 && GetPlayer(mctx, match)->recorder) 08941 { 08942 if (0 == match) 08943 { 08944 for (uint i = 1; mctx && (i < player.size()); i++) 08945 { 08946 PlayerContext *ctx = GetPlayer(mctx, i); 08947 if (ctx->recorder && (ctx->GetCardID() == cardnum)) 08948 { 08949 LOG(VB_GENERAL, LOG_INFO, LOC + 08950 "Disabling PiP for QUIT_LIVETV"); 08951 StopStuff(mctx, ctx, true, true, true); 08952 } 08953 } 08954 SetLastProgram(NULL); 08955 jumpToProgram = true; 08956 SetExitPlayer(true, false); 08957 } 08958 else 08959 { 08960 PlayerContext *ctx = GetPlayer(mctx, match); 08961 StopStuff(mctx, ctx, true, true, true); 08962 } 08963 } 08964 ReturnPlayerLock(mctx); 08965 } 08966 08967 if (message.left(12) == "LIVETV_WATCH") 08968 { 08969 int watch = 0; 08970 if (tokens.size() >= 3) 08971 { 08972 cardnum = tokens[1].toUInt(); 08973 watch = tokens[2].toInt(); 08974 } 08975 08976 PlayerContext *mctx = GetPlayerWriteLock(0, __FILE__, __LINE__); 08977 int match = -1; 08978 for (uint i = 0; mctx && (i < player.size()); i++) 08979 { 08980 PlayerContext *ctx = GetPlayer(mctx, i); 08981 match = (ctx->GetCardID() == cardnum) ? i : match; 08982 } 08983 08984 if (match >= 0) 08985 { 08986 if (watch) 08987 { 08988 ProgramInfo pi(me->ExtraDataList()); 08989 if (pi.HasPathname() || pi.GetChanID()) 08990 { 08991 PlayerContext *ctx = GetPlayer(mctx, match); 08992 ctx->SetPseudoLiveTV(&pi, kPseudoChangeChannel); 08993 08994 QMutexLocker locker(&timerIdLock); 08995 if (!pseudoChangeChanTimerId) 08996 pseudoChangeChanTimerId = StartTimer(0, __LINE__); 08997 } 08998 } 08999 else 09000 { 09001 PlayerContext *ctx = GetPlayer(mctx, match); 09002 ctx->SetPseudoLiveTV(NULL, kPseudoNormalLiveTV); 09003 } 09004 } 09005 ReturnPlayerLock(mctx); 09006 } 09007 09008 if (message.left(12) == "LIVETV_CHAIN") 09009 { 09010 QString id = QString::null; 09011 if ((tokens.size() >= 2) && tokens[1] == "UPDATE") 09012 id = tokens[2]; 09013 09014 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 09015 for (uint i = 0; mctx && (i < player.size()); i++) 09016 { 09017 PlayerContext *ctx = GetPlayer(mctx, i); 09018 if (ctx->tvchain && ctx->tvchain->GetID() == id) 09019 { 09020 QMutexLocker locker(&timerIdLock); 09021 tvchainUpdateTimerId[StartTimer(1, __LINE__)] = ctx; 09022 break; 09023 } 09024 } 09025 ReturnPlayerLock(mctx); 09026 } 09027 09028 if (message.left(12) == "EXIT_TO_MENU") 09029 { 09030 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 09031 for (uint i = 0; mctx && (i < player.size()); i++) 09032 { 09033 PlayerContext *ctx = GetPlayer(mctx, i); 09034 PrepareToExitPlayer(ctx, __LINE__); 09035 } 09036 09037 SetExitPlayer(true, true); 09038 if (mctx && mctx->player) 09039 mctx->player->DisableEdit(-1); 09040 ReturnPlayerLock(mctx); 09041 } 09042 09043 if (message.left(6) == "SIGNAL") 09044 { 09045 cardnum = (tokens.size() >= 2) ? tokens[1].toUInt() : 0; 09046 QStringList signalList = me->ExtraDataList(); 09047 09048 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 09049 OSD *osd = GetOSDLock(mctx); 09050 if (osd && !osd->IsWindowVisible(OSD_WIN_INTERACT)) 09051 { 09052 for (uint i = 0; mctx && (i < player.size()); i++) 09053 { 09054 PlayerContext *ctx = GetPlayer(mctx, i); 09055 bool tc = ctx->recorder && (ctx->GetCardID() == cardnum); 09056 if (tc && !signalList.empty()) 09057 { 09058 UpdateOSDSignal(ctx, signalList); 09059 UpdateOSDTimeoutMessage(ctx); 09060 } 09061 } 09062 } 09063 ReturnOSDLock(mctx, osd); 09064 ReturnPlayerLock(mctx); 09065 } 09066 09067 if (message.left(15) == "NETWORK_CONTROL") 09068 { 09069 if ((tokens.size() >= 2) && 09070 (tokens[1] != "ANSWER") && (tokens[1] != "RESPONSE")) 09071 { 09072 QStringList tokens = message.split(" ", QString::SkipEmptyParts); 09073 if ((tokens.size() >= 2) && 09074 (tokens[1] != "ANSWER") && (tokens[1] != "RESPONSE")) 09075 { 09076 QMutexLocker locker(&timerIdLock); 09077 message.detach(); 09078 networkControlCommands.enqueue(message); 09079 if (!networkControlTimerId) 09080 networkControlTimerId = StartTimer(1, __LINE__); 09081 } 09082 } 09083 } 09084 09085 if (message.left(9) == "START_EPG") 09086 { 09087 int editType = tokens[1].toInt(); 09088 DoEditSchedule(editType); 09089 } 09090 09091 if (message.left(11) == "EPG_EXITING" || 09092 message.left(18) == "PROGFINDER_EXITING" || 09093 message.left(21) == "VIEWSCHEDULED_EXITING" || 09094 message.left(19) == "PLAYBACKBOX_EXITING" || 09095 message.left(22) == "SCHEDULEEDITOR_EXITING") 09096 { 09097 // Resize the window back to the MythTV Player size 09098 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 09099 PlayerContext *mctx; 09100 MythMainWindow *mwnd = GetMythMainWindow(); 09101 09102 StopEmbedding(); 09103 MythPainter *painter = GetMythPainter(); 09104 if (painter) 09105 painter->FreeResources(); 09106 09107 mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 09108 mctx->LockDeletePlayer(__FILE__, __LINE__); 09109 if (mctx->player && mctx->player->GetVideoOutput()) 09110 mctx->player->GetVideoOutput()->ResizeForVideo(); 09111 mctx->UnlockDeletePlayer(__FILE__, __LINE__); 09112 ReturnPlayerLock(mctx); 09113 09114 if (!db_use_gui_size_for_tv || !db_use_fixed_size) 09115 { 09116 mwnd->setMinimumSize(QSize(16, 16)); 09117 mwnd->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); 09118 mwnd->setGeometry(player_bounds.left(), player_bounds.top(), 09119 player_bounds.width(), player_bounds.height()); 09120 } 09121 09122 DoSetPauseState(actx, saved_pause); // Restore pause states 09123 disableDrawUnusedRects = false; 09124 09125 qApp->processEvents(); 09126 09127 if (!weDisabledGUI) 09128 { 09129 weDisabledGUI = true; 09130 GetMythMainWindow()->PushDrawDisabled(); 09131 DrawUnusedRects(); 09132 } 09133 09134 isEmbedded = false; 09135 ignoreKeyPresses = false; 09136 09137 if (message.left(19) == "PLAYBACKBOX_EXITING") 09138 { 09139 ProgramInfo pginfo(me->ExtraDataList()); 09140 if (pginfo.HasPathname() || pginfo.GetChanID()) 09141 PrepToSwitchToRecordedProgram(actx, pginfo); 09142 } 09143 09144 ReturnPlayerLock(actx); 09145 09146 } 09147 09148 if (message.left(14) == "COMMFLAG_START" && (tokens.size() >= 2)) 09149 { 09150 uint evchanid = 0; 09151 QDateTime evrecstartts; 09152 ProgramInfo::ExtractKey(tokens[1], evchanid, evrecstartts); 09153 09154 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 09155 for (uint i = 0; mctx && (i < player.size()); i++) 09156 { 09157 PlayerContext *ctx = GetPlayer(mctx, i); 09158 ctx->LockPlayingInfo(__FILE__, __LINE__); 09159 bool doit = 09160 ((ctx->playingInfo) && 09161 (ctx->playingInfo->GetChanID() == evchanid) && 09162 (ctx->playingInfo->GetRecordingStartTime() == evrecstartts)); 09163 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 09164 09165 if (doit) 09166 { 09167 QString msg = "COMMFLAG_REQUEST "; 09168 msg += ProgramInfo::MakeUniqueKey(evchanid, evrecstartts); 09169 gCoreContext->SendMessage(msg); 09170 } 09171 } 09172 ReturnPlayerLock(mctx); 09173 } 09174 09175 if (message.left(15) == "COMMFLAG_UPDATE" && (tokens.size() >= 3)) 09176 { 09177 uint evchanid = 0; 09178 QDateTime evrecstartts; 09179 ProgramInfo::ExtractKey(tokens[1], evchanid, evrecstartts); 09180 09181 PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__); 09182 for (uint i = 0; mctx && evchanid && (i < player.size()); i++) 09183 { 09184 PlayerContext *ctx = GetPlayer(mctx, i); 09185 ctx->LockPlayingInfo(__FILE__, __LINE__); 09186 bool doit = 09187 ((ctx->playingInfo) && 09188 (ctx->playingInfo->GetChanID() == evchanid) && 09189 (ctx->playingInfo->GetRecordingStartTime() == evrecstartts)); 09190 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 09191 09192 if (doit) 09193 { 09194 frm_dir_map_t newMap; 09195 QStringList mark; 09196 QStringList marks = 09197 tokens[2].split(",", QString::SkipEmptyParts); 09198 for (uint i = 0; i < (uint)marks.size(); i++) 09199 { 09200 mark = marks[i].split(":", QString::SkipEmptyParts); 09201 if (marks.size() >= 2) 09202 { 09203 newMap[mark[0].toLongLong()] = 09204 (MarkTypes) mark[1].toInt(); 09205 } 09206 } 09207 ctx->LockDeletePlayer(__FILE__, __LINE__); 09208 if (ctx->player) 09209 ctx->player->SetCommBreakMap(newMap); 09210 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 09211 } 09212 } 09213 ReturnPlayerLock(mctx); 09214 } 09215 } 09216 09217 void TV::ToggleRecord(PlayerContext *ctx) 09218 { 09219 BrowseInfo bi = browsehelper->GetBrowsedInfo(); 09220 if (bi.m_chanid) 09221 { 09222 InfoMap infoMap; 09223 QDateTime startts = QDateTime::fromString( 09224 bi.m_starttime, Qt::ISODate); 09225 09226 RecordingInfo::LoadStatus status; 09227 RecordingInfo recinfo(bi.m_chanid, startts, false, 0, &status); 09228 if (RecordingInfo::kFoundProgram == status) 09229 recinfo.ToggleRecord(); 09230 recinfo.ToMap(infoMap); 09231 infoMap["iconpath"] = ChannelUtil::GetIcon(recinfo.GetChanID()); 09232 if ((recinfo.IsVideoFile() || recinfo.IsVideoDVD() || 09233 recinfo.IsVideoBD()) && recinfo.GetPathname() != recinfo.GetBasename()) 09234 { 09235 infoMap["coverartpath"] = VideoMetaDataUtil::GetArtPath( 09236 recinfo.GetPathname(), "Coverart"); 09237 infoMap["fanartpath"] = VideoMetaDataUtil::GetArtPath( 09238 recinfo.GetPathname(), "Fanart"); 09239 infoMap["bannerpath"] = VideoMetaDataUtil::GetArtPath( 09240 recinfo.GetPathname(), "Banners"); 09241 infoMap["screenshotpath"] = VideoMetaDataUtil::GetArtPath( 09242 recinfo.GetPathname(), "Screenshots"); 09243 } 09244 09245 OSD *osd = GetOSDLock(ctx); 09246 if (osd) 09247 { 09248 osd->SetText("browse_info", infoMap, kOSDTimeout_Med); 09249 QHash<QString,QString> map; 09250 map.insert("message_text", tr("Record")); 09251 osd->SetText("osd_message", map, kOSDTimeout_Med); 09252 } 09253 ReturnOSDLock(ctx, osd); 09254 return; 09255 } 09256 09257 ctx->LockPlayingInfo(__FILE__, __LINE__); 09258 if (!ctx->playingInfo) 09259 { 09260 LOG(VB_GENERAL, LOG_INFO, LOC + "Unknown recording during live tv."); 09261 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 09262 return; 09263 } 09264 09265 QString cmdmsg(""); 09266 if (ctx->playingInfo->QueryAutoExpire() == kLiveTVAutoExpire) 09267 { 09268 RecordingInfo recInfo(*ctx->playingInfo); 09269 recInfo.SaveAutoExpire((AutoExpireType)db_autoexpire_default); 09270 recInfo.ApplyRecordRecGroupChange("Default"); 09271 *ctx->playingInfo = recInfo; 09272 09273 cmdmsg = tr("Record"); 09274 ctx->SetPseudoLiveTV(ctx->playingInfo, kPseudoRecording); 09275 ctx->recorder->SetLiveRecording(true); 09276 LOG(VB_RECORD, LOG_INFO, LOC + "Toggling Record on"); 09277 } 09278 else 09279 { 09280 RecordingInfo recInfo(*ctx->playingInfo); 09281 recInfo.SaveAutoExpire(kLiveTVAutoExpire); 09282 recInfo.ApplyRecordRecGroupChange("LiveTV"); 09283 *ctx->playingInfo = recInfo; 09284 09285 cmdmsg = tr("Cancel Record"); 09286 ctx->SetPseudoLiveTV(ctx->playingInfo, kPseudoNormalLiveTV); 09287 ctx->recorder->SetLiveRecording(false); 09288 LOG(VB_RECORD, LOG_INFO, LOC + "Toggling Record off"); 09289 } 09290 09291 QString msg = cmdmsg + " \"" + ctx->playingInfo->GetTitle() + "\""; 09292 09293 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 09294 09295 SetOSDMessage(ctx, msg); 09296 } 09297 09298 void TV::HandleOSDClosed(int osdType) 09299 { 09300 switch (osdType) 09301 { 09302 case kOSDFunctionalType_PictureAdjust: 09303 adjustingPicture = kAdjustingPicture_None; 09304 adjustingPictureAttribute = kPictureAttribute_None; 09305 break; 09306 case kOSDFunctionalType_SmartForward: 09307 doSmartForward = false; 09308 break; 09309 case kOSDFunctionalType_TimeStretchAdjust: 09310 stretchAdjustment = false; 09311 break; 09312 case kOSDFunctionalType_AudioSyncAdjust: 09313 audiosyncAdjustment = false; 09314 break; 09315 case kOSDFunctionalType_SubtitleZoomAdjust: 09316 subtitleZoomAdjustment = false; 09317 break; 09318 case kOSDFunctionalType_Default: 09319 break; 09320 } 09321 } 09322 09323 PictureAttribute TV::NextPictureAdjustType( 09324 PictureAdjustType type, MythPlayer *mp, PictureAttribute attr) 09325 { 09326 if (!mp) 09327 return kPictureAttribute_None; 09328 09329 uint sup = kPictureAttributeSupported_None; 09330 if ((kAdjustingPicture_Playback == type) && mp && mp->GetVideoOutput()) 09331 { 09332 sup = mp->GetVideoOutput()->GetSupportedPictureAttributes(); 09333 if (mp->HasAudioOut() && mp->PlayerControlsVolume()) 09334 sup |= kPictureAttributeSupported_Volume; 09335 } 09336 else if (kAdjustingPicture_Channel == type) 09337 { 09338 sup = (kPictureAttributeSupported_Brightness | 09339 kPictureAttributeSupported_Contrast | 09340 kPictureAttributeSupported_Colour | 09341 kPictureAttributeSupported_Hue); 09342 } 09343 else if (kAdjustingPicture_Recording == type) 09344 { 09345 sup = (kPictureAttributeSupported_Brightness | 09346 kPictureAttributeSupported_Contrast | 09347 kPictureAttributeSupported_Colour | 09348 kPictureAttributeSupported_Hue); 09349 } 09350 09351 return ::next((PictureAttributeSupported)sup, (PictureAttribute) attr); 09352 } 09353 09354 void TV::DoToggleStudioLevels(const PlayerContext *ctx) 09355 { 09356 ctx->LockDeletePlayer(__FILE__, __LINE__); 09357 ctx->player->ToggleStudioLevels(); 09358 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 09359 } 09360 09361 void TV::DoToggleNightMode(const PlayerContext *ctx) 09362 { 09363 ctx->LockDeletePlayer(__FILE__, __LINE__); 09364 ctx->player->ToggleNightMode(); 09365 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 09366 } 09367 09368 void TV::DoTogglePictureAttribute(const PlayerContext *ctx, 09369 PictureAdjustType type) 09370 { 09371 ctx->LockDeletePlayer(__FILE__, __LINE__); 09372 PictureAttribute attr = NextPictureAdjustType(type, ctx->player, 09373 adjustingPictureAttribute); 09374 if (kPictureAttribute_None == attr) 09375 { 09376 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 09377 return; 09378 } 09379 09380 adjustingPicture = type; 09381 adjustingPictureAttribute = attr; 09382 09383 QString title = toTitleString(type); 09384 09385 int value = 99; 09386 if (kAdjustingPicture_Playback == type) 09387 { 09388 if (!ctx->player) 09389 { 09390 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 09391 return; 09392 } 09393 if (kPictureAttribute_Volume != adjustingPictureAttribute) 09394 { 09395 value = ctx->player->GetVideoOutput()->GetPictureAttribute(attr); 09396 } 09397 else if (ctx->player->HasAudioOut()) 09398 { 09399 value = ctx->player->GetVolume(); 09400 title = tr("Adjust Volume"); 09401 } 09402 } 09403 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 09404 09405 if (ctx->recorder && (kAdjustingPicture_Playback != type)) 09406 { 09407 value = ctx->recorder->GetPictureAttribute(attr); 09408 } 09409 09410 QString text = toString(attr) + " " + toTypeString(type); 09411 09412 UpdateOSDStatus(ctx, title, text, QString::number(value), 09413 kOSDFunctionalType_PictureAdjust, "%", 09414 value * 10, kOSDTimeout_Med); 09415 SetUpdateOSDPosition(false); 09416 } 09417 09418 void TV::DoChangePictureAttribute( 09419 PlayerContext *ctx, 09420 PictureAdjustType type, PictureAttribute attr, 09421 bool up, int newvalue) 09422 { 09423 int value = 99; 09424 09425 ctx->LockDeletePlayer(__FILE__, __LINE__); 09426 if (kAdjustingPicture_Playback == type) 09427 { 09428 if (kPictureAttribute_Volume == attr) 09429 { 09430 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 09431 ChangeVolume(ctx, up, newvalue); 09432 return; 09433 } 09434 if (!ctx->player) 09435 { 09436 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 09437 return; 09438 } 09439 09440 if (ctx->player->GetVideoOutput()) 09441 { 09442 VideoOutput *vo = ctx->player->GetVideoOutput(); 09443 if ((newvalue >= 0) && (newvalue <= 100)) 09444 value = vo->SetPictureAttribute(attr, newvalue); 09445 else 09446 value = vo->ChangePictureAttribute(attr, up); 09447 } 09448 } 09449 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 09450 09451 if (ctx->recorder && (kAdjustingPicture_Playback != type)) 09452 { 09453 value = ctx->recorder->ChangePictureAttribute(type, attr, up); 09454 } 09455 09456 QString text = toString(attr) + " " + toTypeString(type); 09457 09458 UpdateOSDStatus(ctx, toTitleString(type), text, QString::number(value), 09459 kOSDFunctionalType_PictureAdjust, "%", 09460 value * 10, kOSDTimeout_Med); 09461 SetUpdateOSDPosition(false); 09462 } 09463 09464 void TV::SetActive(PlayerContext *lctx, int index, bool osd_msg) 09465 { 09466 if (!lctx) 09467 return; 09468 09469 int new_index = (index < 0) ? (playerActive+1) % player.size() : index; 09470 new_index = ((uint)new_index >= player.size()) ? 0 : new_index; 09471 09472 QString loc = LOC + QString("SetActive(%1,%2) %3 -> %4") 09473 .arg(index).arg((osd_msg) ? "with OSD" : "w/o OSD") 09474 .arg(playerActive).arg(new_index); 09475 09476 LOG(VB_PLAYBACK, LOG_INFO, loc + " -- begin"); 09477 09478 for (uint i = 0; i < player.size(); i++) 09479 ClearOSD(GetPlayer(lctx,i)); 09480 09481 playerActive = new_index; 09482 09483 for (int i = 0; i < (int)player.size(); i++) 09484 { 09485 PlayerContext *ctx = GetPlayer(lctx, i); 09486 ctx->LockDeletePlayer(__FILE__, __LINE__); 09487 if (ctx->player) 09488 ctx->player->SetPIPActive(i == playerActive); 09489 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 09490 } 09491 09492 if (osd_msg && !GetPlayer(lctx, -1)->IsPIP() && player.size() > 1) 09493 { 09494 PlayerContext *actx = GetPlayer(lctx, -1); 09495 SetOSDMessage(actx, tr("Active Changed")); 09496 } 09497 09498 LOG(VB_PLAYBACK, LOG_INFO, loc + " -- end"); 09499 } 09500 09501 void TV::ShowOSDCutpoint(PlayerContext *ctx, const QString &type) 09502 { 09503 OSD *osd = GetOSDLock(ctx); 09504 if (!osd) 09505 { 09506 ReturnOSDLock(ctx, osd); 09507 return; 09508 } 09509 09510 09511 if (("EDIT_CUT_POINTS" == type) || ("EDIT_CUT_REGION" == type)) 09512 { 09513 uint64_t frame = ctx->player->GetFramesPlayed(); 09514 uint64_t previous_cut = ctx->player->GetNearestMark(frame, false); 09515 uint64_t next_cut = ctx->player->GetNearestMark(frame, true); 09516 uint64_t total_frames = ctx->player->GetTotalFrameCount(); 09517 09518 osd->DialogShow(OSD_DLG_CUTPOINT, 09519 QObject::tr("Edit Cut Points")); 09520 if (ctx->player->IsInDelete(frame)) 09521 { 09522 if (ctx->player->IsTemporaryMark(frame)) 09523 { 09524 if (previous_cut > 0) 09525 osd->DialogAddButton(QObject::tr("Move Previous Cut End " 09526 "Here"), 09527 QString("DIALOG_CUTPOINT_MOVEPREV_%1") 09528 .arg(frame)); 09529 else 09530 osd->DialogAddButton(QObject::tr("Cut to Beginning"), 09531 QString("DIALOG_CUTPOINT_CUTTOBEGINNING_%1") 09532 .arg(frame)); 09533 09534 if (next_cut == total_frames) 09535 osd->DialogAddButton(QObject::tr("Cut to End"), 09536 QString("DIALOG_CUTPOINT_CUTTOEND_%1") 09537 .arg(frame)); 09538 else 09539 osd->DialogAddButton(QObject::tr("Move Next Cut Start " 09540 "Here"), 09541 QString("DIALOG_CUTPOINT_MOVENEXT_%1") 09542 .arg(frame)); 09543 } 09544 else 09545 { 09546 osd->DialogAddButton(QObject::tr("Move Start of Cut Here"), 09547 QString("DIALOG_CUTPOINT_MOVEPREV_%1") 09548 .arg(frame)); 09549 osd->DialogAddButton(QObject::tr("Move End of Cut Here"), 09550 QString("DIALOG_CUTPOINT_MOVENEXT_%1") 09551 .arg(frame)); 09552 } 09553 osd->DialogAddButton(QObject::tr("Delete This Cut"), 09554 QString("DIALOG_CUTPOINT_DELETE_%1") 09555 .arg(frame)); 09556 } 09557 else 09558 { 09559 if (previous_cut > 0) 09560 osd->DialogAddButton(QObject::tr("Move Previous Cut End Here"), 09561 QString("DIALOG_CUTPOINT_MOVEPREV_%1") 09562 .arg(frame)); 09563 else 09564 osd->DialogAddButton(QObject::tr("Cut to Beginning"), 09565 QString("DIALOG_CUTPOINT_CUTTOBEGINNING_%1") 09566 .arg(frame)); 09567 if (next_cut == total_frames) 09568 osd->DialogAddButton(QObject::tr("Cut to End"), 09569 QString("DIALOG_CUTPOINT_CUTTOEND_%1") 09570 .arg(frame)); 09571 else 09572 osd->DialogAddButton(QObject::tr("Move Next Cut Start Here"), 09573 QString("DIALOG_CUTPOINT_MOVENEXT_%1") 09574 .arg(frame)); 09575 osd->DialogAddButton(QObject::tr("Add New Cut"), 09576 QString("DIALOG_CUTPOINT_NEWCUT_%1") 09577 .arg(frame)); 09578 osd->DialogAddButton(QObject::tr("Join Surrounding Cuts"), 09579 QString("DIALOG_CUTPOINT_DELETE_%1") 09580 .arg(frame)); 09581 } 09582 if (ctx->player->DeleteMapHasUndo()) 09583 osd->DialogAddButton(QObject::tr("Undo") + " - " + 09584 ctx->player->DeleteMapGetUndoMessage(), 09585 QString("DIALOG_CUTPOINT_UNDO_0")); 09586 if (ctx->player->DeleteMapHasRedo()) 09587 osd->DialogAddButton(QObject::tr("Redo") + " - " + 09588 ctx->player->DeleteMapGetRedoMessage(), 09589 QString("DIALOG_CUTPOINT_REDO_0")); 09590 if ("EDIT_CUT_POINTS" == type) 09591 osd->DialogAddButton(QObject::tr("Cut List Options"), 09592 "DIALOG_CUTPOINT_CUTLISTOPTIONS_0", true); 09593 } 09594 else if ("CUT_LIST_OPTIONS" == type) 09595 { 09596 osd->DialogShow(OSD_DLG_CUTPOINT, 09597 QObject::tr("Cut List Options")); 09598 osd->DialogAddButton(QObject::tr("Clear Cuts"), 09599 "DIALOG_CUTPOINT_CLEARMAP_0"); 09600 osd->DialogAddButton(QObject::tr("Reverse Cuts"), 09601 "DIALOG_CUTPOINT_INVERTMAP_0"); 09602 osd->DialogAddButton(QObject::tr("Load Detected Commercials"), 09603 "DIALOG_CUTPOINT_LOADCOMMSKIP_0"); 09604 osd->DialogAddButton(QObject::tr("Undo Changes"), 09605 "DIALOG_CUTPOINT_REVERT_0"); 09606 osd->DialogAddButton(QObject::tr("Exit Without Saving"), 09607 "DIALOG_CUTPOINT_REVERTEXIT_0"); 09608 osd->DialogAddButton(QObject::tr("Save Cuts"), 09609 "DIALOG_CUTPOINT_SAVEMAP_0"); 09610 osd->DialogAddButton(QObject::tr("Save Cuts and Exit"), 09611 "DIALOG_CUTPOINT_SAVEEXIT_0"); 09612 } 09613 else if ("EXIT_EDIT_MODE" == type) 09614 { 09615 osd->DialogShow(OSD_DLG_CUTPOINT, 09616 QObject::tr("Exit Recording Editor")); 09617 osd->DialogAddButton(QObject::tr("Save Cuts and Exit"), 09618 "DIALOG_CUTPOINT_SAVEEXIT_0"); 09619 osd->DialogAddButton(QObject::tr("Exit Without Saving"), 09620 "DIALOG_CUTPOINT_REVERTEXIT_0"); 09621 osd->DialogAddButton(QObject::tr("Save Cuts"), 09622 "DIALOG_CUTPOINT_SAVEMAP_0"); 09623 osd->DialogAddButton(QObject::tr("Undo Changes"), 09624 "DIALOG_CUTPOINT_REVERT_0"); 09625 } 09626 osd->DialogBack("", "DIALOG_CUTPOINT_DONOTHING_0", true); 09627 QHash<QString,QString> map; 09628 map.insert("title", tr("Edit")); 09629 osd->SetText("osd_program_editor", map, kOSDTimeout_None); 09630 ReturnOSDLock(ctx, osd); 09631 } 09632 09633 bool TV::HandleOSDCutpoint(PlayerContext *ctx, QString action, long long frame) 09634 { 09635 bool res = true; 09636 if (!DialogIsVisible(ctx, OSD_DLG_CUTPOINT)) 09637 return res; 09638 09639 OSD *osd = GetOSDLock(ctx); 09640 if (action == "CUTLISTOPTIONS" && osd) 09641 { 09642 ShowOSDCutpoint(ctx, "CUT_LIST_OPTIONS"); 09643 res = false; 09644 } 09645 else if (action == "DONOTHING" && osd) 09646 { 09647 } 09648 else if (osd) 09649 { 09650 QStringList actions(action); 09651 if (!ctx->player->HandleProgramEditorActions(actions, frame)) 09652 LOG(VB_GENERAL, LOG_ERR, LOC + "Unrecognised cutpoint action"); 09653 else 09654 editmode = ctx->player->GetEditMode(); 09655 } 09656 ReturnOSDLock(ctx, osd); 09657 return res; 09658 } 09659 09663 void TV::StartProgramEditMode(PlayerContext *ctx) 09664 { 09665 ctx->LockPlayingInfo(__FILE__, __LINE__); 09666 bool isEditing = ctx->playingInfo->QueryIsEditing(); 09667 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 09668 09669 if (isEditing) 09670 { 09671 ShowOSDAlreadyEditing(ctx); 09672 return; 09673 } 09674 09675 ctx->LockDeletePlayer(__FILE__, __LINE__); 09676 if (ctx->player) 09677 editmode = ctx->player->EnableEdit(); 09678 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 09679 } 09680 09681 void TV::ShowOSDAlreadyEditing(PlayerContext *ctx) 09682 { 09683 OSD *osd = GetOSDLock(ctx); 09684 if (osd) 09685 { 09686 osd->DialogQuit(); 09687 bool was_paused = ContextIsPaused(ctx, __FILE__, __LINE__); 09688 if (!was_paused) 09689 DoTogglePause(ctx, true); 09690 09691 QString message = tr("This program is currently being edited"); 09692 osd->DialogShow(OSD_DLG_EDITING, message); 09693 QString def = QString("DIALOG_EDITING_CONTINUE_%1").arg(was_paused); 09694 osd->DialogAddButton(tr("Continue Editing"), def, false, true); 09695 osd->DialogAddButton(tr("Do not edit"), 09696 QString("DIALOG_EDITING_STOP_%1").arg(was_paused)); 09697 osd->DialogBack("", def, true); 09698 } 09699 ReturnOSDLock(ctx, osd); 09700 } 09701 09702 void TV::HandleOSDAlreadyEditing(PlayerContext *ctx, QString action, 09703 bool was_paused) 09704 { 09705 if (!DialogIsVisible(ctx, OSD_DLG_EDITING)) 09706 return; 09707 09708 bool paused = ContextIsPaused(ctx, __FILE__, __LINE__); 09709 09710 if (action == "STOP") 09711 { 09712 ctx->LockPlayingInfo(__FILE__, __LINE__); 09713 if (ctx->playingInfo) 09714 ctx->playingInfo->SaveEditing(false); 09715 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 09716 if (!was_paused && paused) 09717 DoTogglePause(ctx, true); 09718 } 09719 else // action == "CONTINUE" 09720 { 09721 ctx->LockDeletePlayer(__FILE__, __LINE__); 09722 if (ctx->player) 09723 { 09724 ctx->playingInfo->SaveEditing(false); 09725 editmode = ctx->player->EnableEdit(); 09726 if (!editmode && !was_paused && paused) 09727 DoTogglePause(ctx, false); 09728 } 09729 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 09730 } 09731 09732 } 09733 09734 static void insert_map(InfoMap &infoMap, const InfoMap &newMap) 09735 { 09736 InfoMap::const_iterator it = newMap.begin(); 09737 for (; it != newMap.end(); ++it) 09738 infoMap.insert(it.key(), *it); 09739 } 09740 09744 void TV::StartChannelEditMode(PlayerContext *ctx) 09745 { 09746 OSD *osd = GetOSDLock(ctx); 09747 if (!ctx->recorder || !osd) 09748 { 09749 ReturnOSDLock(ctx, osd); 09750 return; 09751 } 09752 ReturnOSDLock(ctx, osd); 09753 09754 QMutexLocker locker(&chanEditMapLock); 09755 ddMapLoader->wait(); 09756 09757 // Get the info available from the backend 09758 chanEditMap.clear(); 09759 ctx->recorder->GetChannelInfo(chanEditMap); 09760 09761 // Assuming the data is valid, try to load DataDirect listings. 09762 uint sourceid = chanEditMap["sourceid"].toUInt(); 09763 09764 // Update with XDS and DataDirect Info 09765 ChannelEditAutoFill(ctx, chanEditMap); 09766 09767 // Set proper initial values for channel editor, and make it visible.. 09768 osd = GetOSDLock(ctx); 09769 if (osd) 09770 { 09771 osd->DialogQuit(); 09772 osd->DialogShow(OSD_DLG_EDITOR); 09773 osd->SetText(OSD_DLG_EDITOR, chanEditMap, kOSDTimeout_None); 09774 } 09775 ReturnOSDLock(ctx, osd); 09776 09777 if (sourceid && (sourceid != ddMapSourceId)) 09778 { 09779 ddMapLoader->SetSourceID(sourceid); 09780 MThreadPool::globalInstance()->start(ddMapLoader, "DDMapLoader"); 09781 } 09782 } 09783 09787 bool TV::HandleOSDChannelEdit(PlayerContext *ctx, QString action) 09788 { 09789 QMutexLocker locker(&chanEditMapLock); 09790 bool hide = false; 09791 09792 if (!DialogIsVisible(ctx, OSD_DLG_EDITOR)) 09793 return hide; 09794 09795 OSD *osd = GetOSDLock(ctx); 09796 if (osd && action == "PROBE") 09797 { 09798 InfoMap infoMap; 09799 osd->DialogGetText(infoMap); 09800 ChannelEditAutoFill(ctx, infoMap); 09801 insert_map(chanEditMap, infoMap); 09802 osd->SetText(OSD_DLG_EDITOR, chanEditMap, kOSDTimeout_None); 09803 } 09804 else if (osd && action == "OK") 09805 { 09806 InfoMap infoMap; 09807 osd->DialogGetText(infoMap); 09808 insert_map(chanEditMap, infoMap); 09809 ctx->recorder->SetChannelInfo(chanEditMap); 09810 hide = true; 09811 } 09812 else if (osd && action == "QUIT") 09813 { 09814 hide = true; 09815 } 09816 ReturnOSDLock(ctx, osd); 09817 return hide; 09818 } 09819 09823 void TV::ChannelEditAutoFill(const PlayerContext *ctx, InfoMap &infoMap) const 09824 { 09825 QMap<QString,bool> dummy; 09826 ChannelEditAutoFill(ctx, infoMap, dummy); 09827 } 09828 09832 void TV::ChannelEditAutoFill(const PlayerContext *ctx, InfoMap &infoMap, 09833 const QMap<QString,bool> &changed) const 09834 { 09835 const QString keys[4] = { "XMLTV", "callsign", "channame", "channum", }; 09836 09837 // fill in uninitialized and unchanged fields from XDS 09838 ChannelEditXDSFill(ctx, infoMap); 09839 09840 // if no data direct info we're done.. 09841 if (!ddMapSourceId) 09842 return; 09843 09844 if (changed.size()) 09845 { 09846 ChannelEditDDFill(infoMap, changed, false); 09847 } 09848 else 09849 { 09850 QMutexLocker locker(&chanEditMapLock); 09851 QMap<QString,bool> chg; 09852 // check if anything changed 09853 for (uint i = 0; i < 4; i++) 09854 chg[keys[i]] = infoMap[keys[i]] != chanEditMap[keys[i]]; 09855 09856 // clean up case and extra spaces 09857 infoMap["callsign"] = infoMap["callsign"].toUpper().trimmed(); 09858 infoMap["channum"] = infoMap["channum"].trimmed(); 09859 infoMap["channame"] = infoMap["channame"].trimmed(); 09860 infoMap["XMLTV"] = infoMap["XMLTV"].trimmed(); 09861 09862 // make sure changes weren't just chaff 09863 for (uint i = 0; i < 4; i++) 09864 chg[keys[i]] &= infoMap[keys[i]] != chanEditMap[keys[i]]; 09865 09866 ChannelEditDDFill(infoMap, chg, true); 09867 } 09868 } 09869 09870 void TV::ChannelEditXDSFill(const PlayerContext *ctx, InfoMap &infoMap) const 09871 { 09872 QMap<QString,bool> modifiable; 09873 if (!(modifiable["callsign"] = infoMap["callsign"].isEmpty())) 09874 { 09875 QString unsetsign = QObject::tr("UNKNOWN%1", "Synthesized callsign"); 09876 uint unsetcmpl = unsetsign.length() - 2; 09877 unsetsign = unsetsign.left(unsetcmpl); 09878 if (infoMap["callsign"].left(unsetcmpl) == unsetsign) // was unsetcmpl???? 09879 modifiable["callsign"] = true; 09880 } 09881 modifiable["channame"] = infoMap["channame"].isEmpty(); 09882 09883 const QString xds_keys[2] = { "callsign", "channame", }; 09884 for (uint i = 0; i < 2; i++) 09885 { 09886 if (!modifiable[xds_keys[i]]) 09887 continue; 09888 09889 ctx->LockDeletePlayer(__FILE__, __LINE__); 09890 QString tmp = ctx->player->GetXDS(xds_keys[i]).toUpper(); 09891 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 09892 09893 if (tmp.isEmpty()) 09894 continue; 09895 09896 if ((xds_keys[i] == "callsign") && 09897 ((tmp.length() > 5) || (tmp.indexOf(" ") >= 0))) 09898 { 09899 continue; 09900 } 09901 09902 infoMap[xds_keys[i]] = tmp; 09903 } 09904 } 09905 09906 void TV::ChannelEditDDFill(InfoMap &infoMap, 09907 const QMap<QString,bool> &changed, 09908 bool check_unchanged) const 09909 { 09910 if (!ddMapSourceId) 09911 return; 09912 09913 QMutexLocker locker(&chanEditMapLock); 09914 const QString keys[4] = { "XMLTV", "callsign", "channame", "channum", }; 09915 09916 // First check changed keys for availability in our listings source. 09917 // Then, if check_unchanged is set, check unchanged fields. 09918 QString key = "", dd_xmltv = ""; 09919 uint endj = (check_unchanged) ? 2 : 1; 09920 for (uint j = 0; (j < endj) && dd_xmltv.isEmpty(); j++) 09921 { 09922 for (uint i = 0; (i < 4) && dd_xmltv.isEmpty(); i++) 09923 { 09924 key = keys[i]; 09925 if (((j == 1) ^ changed[key]) && !infoMap[key].isEmpty()) 09926 dd_xmltv = GetDataDirect(key, infoMap[key], "XMLTV"); 09927 } 09928 } 09929 09930 // If we found the channel in the listings, fill in all the data we have 09931 if (!dd_xmltv.isEmpty()) 09932 { 09933 infoMap[keys[0]] = dd_xmltv; 09934 for (uint i = 1; i < 4; i++) 09935 { 09936 QString tmp = GetDataDirect(key, infoMap[key], keys[i]); 09937 if (!tmp.isEmpty()) 09938 infoMap[keys[i]] = tmp; 09939 } 09940 return; 09941 } 09942 09943 // If we failed to find an exact match, try partial matches. 09944 // But only fill the current field since this data is dodgy. 09945 key = "callsign"; 09946 if (!infoMap[key].isEmpty()) 09947 { 09948 dd_xmltv = GetDataDirect(key, infoMap[key], "XMLTV", true); 09949 LOG(VB_GENERAL, LOG_INFO, QString("xmltv: %1 for key %2") 09950 .arg(dd_xmltv).arg(key)); 09951 if (!dd_xmltv.isEmpty()) 09952 infoMap[key] = GetDataDirect("XMLTV", dd_xmltv, key); 09953 } 09954 09955 key = "channame"; 09956 if (!infoMap[key].isEmpty()) 09957 { 09958 dd_xmltv = GetDataDirect(key, infoMap[key], "XMLTV", true); 09959 LOG(VB_GENERAL, LOG_INFO, QString("xmltv: %1 for key %2") 09960 .arg(dd_xmltv).arg(key)); 09961 if (!dd_xmltv.isEmpty()) 09962 infoMap[key] = GetDataDirect("XMLTV", dd_xmltv, key); 09963 } 09964 } 09965 09966 QString TV::GetDataDirect(QString key, QString value, QString field, 09967 bool allow_partial_match) const 09968 { 09969 QMutexLocker locker(&chanEditMapLock); 09970 09971 uint sourceid = chanEditMap["sourceid"].toUInt(); 09972 if (!sourceid) 09973 return QString::null; 09974 09975 if (sourceid != ddMapSourceId) 09976 return QString::null; 09977 09978 DDKeyMap::const_iterator it_key = ddMap.find(key); 09979 if (it_key == ddMap.end()) 09980 return QString::null; 09981 09982 DDValueMap::const_iterator it_val = (*it_key).find(value); 09983 if (it_val != (*it_key).end()) 09984 { 09985 InfoMap::const_iterator it_field = (*it_val).find(field); 09986 if (it_field != (*it_val).end()) 09987 { 09988 QString ret = *it_field; 09989 ret.detach(); 09990 return ret; 09991 } 09992 } 09993 09994 if (!allow_partial_match || value.isEmpty()) 09995 return QString::null; 09996 09997 // Check for partial matches.. prefer early match, then short string 09998 DDValueMap::const_iterator best_match = (*it_key).end(); 09999 int best_match_idx = INT_MAX, best_match_len = INT_MAX; 10000 for (it_val = (*it_key).begin(); it_val != (*it_key).end(); ++it_val) 10001 { 10002 int match_idx = it_val.key().indexOf(value); 10003 if (match_idx < 0) 10004 continue; 10005 10006 int match_len = it_val.key().length(); 10007 if ((match_idx < best_match_idx) && (match_len < best_match_len)) 10008 { 10009 best_match = it_val; 10010 best_match_idx = match_idx; 10011 best_match_len = match_len; 10012 } 10013 } 10014 10015 if (best_match != (*it_key).end()) 10016 { 10017 InfoMap::const_iterator it_field = (*best_match).find(field); 10018 if (it_field != (*it_val).end()) 10019 { 10020 QString ret = *it_field; 10021 ret.detach(); 10022 return ret; 10023 } 10024 } 10025 10026 return QString::null; 10027 } 10028 10029 void TV::RunLoadDDMap(uint sourceid) 10030 { 10031 QMutexLocker locker(&chanEditMapLock); 10032 10033 const PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 10034 10035 // Load DataDirect info 10036 LoadDDMap(sourceid); 10037 10038 // Update with XDS and DataDirect Info 10039 ChannelEditAutoFill(actx, chanEditMap); 10040 10041 OSD *osd = GetOSDLock(actx); 10042 if (osd) 10043 { 10044 if (osd->DialogVisible(OSD_DLG_EDITOR)) 10045 osd->SetText(OSD_DLG_EDITOR, chanEditMap, kOSDTimeout_None); 10046 else 10047 LOG(VB_GENERAL, LOG_ERR, LOC + "No channel editor visible. Failed " 10048 "to update data direct channel info."); 10049 } 10050 ReturnOSDLock(actx, osd); 10051 ReturnPlayerLock(actx); 10052 } 10053 10054 bool TV::LoadDDMap(uint sourceid) 10055 { 10056 QMutexLocker locker(&chanEditMapLock); 10057 const QString keys[4] = { "XMLTV", "callsign", "channame", "channum", }; 10058 10059 ddMap.clear(); 10060 ddMapSourceId = 0; 10061 10062 QString grabber, userid, passwd, lineupid; 10063 bool ok = SourceUtil::GetListingsLoginData(sourceid, grabber, userid, 10064 passwd, lineupid); 10065 if (!ok || (grabber != "datadirect")) 10066 { 10067 LOG(VB_PLAYBACK, LOG_ERR, LOC + 10068 QString("LoadDDMap() g(%1)").arg(grabber)); 10069 return false; 10070 } 10071 10072 DataDirectProcessor ddp(DD_ZAP2IT, userid, passwd); 10073 ddp.GrabFullLineup(lineupid, true, false, 36*60*60); 10074 const DDLineupChannels channels = ddp.GetDDLineup(lineupid); 10075 10076 InfoMap tmp; 10077 DDLineupChannels::const_iterator it; 10078 for (it = channels.begin(); it != channels.end(); ++it) 10079 { 10080 DDStation station = ddp.GetDDStation((*it).stationid); 10081 tmp["XMLTV"] = (*it).stationid; 10082 tmp["callsign"] = station.callsign; 10083 tmp["channame"] = station.stationname; 10084 tmp["channum"] = (*it).channel; 10085 if (!(*it).channelMinor.isEmpty()) 10086 { 10087 tmp["channum"] += SourceUtil::GetChannelSeparator(sourceid); 10088 tmp["channum"] += (*it).channelMinor; 10089 } 10090 10091 #if 0 10092 LOG(VB_CHANNEL, LOG_INFO, 10093 QString("Adding channel: %1 -- %2 -- %3 -- %4") 10094 .arg(tmp["channum"],4).arg(tmp["callsign"],7) 10095 .arg(tmp["XMLTV"]).arg(tmp["channame"])); 10096 #endif 10097 10098 for (uint j = 0; j < 4; j++) 10099 for (uint i = 0; i < 4; i++) 10100 ddMap[keys[j]][tmp[keys[j]]][keys[i]] = tmp[keys[i]]; 10101 } 10102 10103 if (!ddMap.empty()) 10104 ddMapSourceId = sourceid; 10105 10106 return !ddMap.empty(); 10107 } 10108 10109 void TV::OSDDialogEvent(int result, QString text, QString action) 10110 { 10111 PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__); 10112 10113 LOG(VB_GENERAL, LOG_DEBUG, LOC + 10114 QString("OSDDialogEvent: result %1 text %2 action %3") 10115 .arg(result).arg(text).arg(action)); 10116 10117 bool hide = true; 10118 10119 if (action.startsWith("DIALOG_")) 10120 { 10121 action.remove("DIALOG_"); 10122 QStringList desc = action.split("_"); 10123 bool valid = desc.size() == 3; 10124 if (valid && desc[0] == "MENU") 10125 { 10126 ShowOSDMenu(actx, desc[1], text); 10127 hide = false; 10128 } 10129 else if (valid && desc[0] == ACTION_JUMPREC) 10130 { 10131 FillOSDMenuJumpRec(actx, desc[1], desc[2].toInt(), text); 10132 hide = false; 10133 } 10134 else if (valid && desc[0] == "VIDEOEXIT") 10135 { 10136 hide = HandleOSDVideoExit(actx, desc[1]); 10137 } 10138 else if (valid && desc[0] == "SLEEP") 10139 { 10140 HandleOSDSleep(actx, desc[1]); 10141 } 10142 else if (valid && desc[0] == "IDLE") 10143 { 10144 HandleOSDIdle(actx, desc[1]); 10145 } 10146 else if (valid && desc[0] == "INFO") 10147 { 10148 HandleOSDInfo(actx, desc[1]); 10149 } 10150 else if (valid && desc[0] == "EDITING") 10151 { 10152 HandleOSDAlreadyEditing(actx, desc[1], desc[2].toInt()); 10153 } 10154 else if (valid && desc[0] == "ASKALLOW") 10155 { 10156 HandleOSDAskAllow(actx, desc[1]); 10157 } 10158 else if (valid && desc[0] == "EDITOR") 10159 { 10160 hide = HandleOSDChannelEdit(actx, desc[1]); 10161 } 10162 else if (valid && desc[0] == "CUTPOINT") 10163 { 10164 hide = HandleOSDCutpoint(actx, desc[1], desc[2].toLongLong()); 10165 } 10166 else if (valid && desc[0] == "DELETE") 10167 { 10168 } 10169 else if (valid && desc[0] == ACTION_PLAY) 10170 { 10171 DoPlay(actx); 10172 } 10173 else if (valid && desc[0] == "CONFIRM") 10174 { 10175 } 10176 else 10177 { 10178 LOG(VB_GENERAL, LOG_ERR, "Unrecognised dialog event."); 10179 } 10180 } 10181 else if (result < 0) 10182 ; // exit dialog 10183 else if (HandleTrackAction(actx, action)) 10184 ; 10185 else if (action == ACTION_PAUSE) 10186 DoTogglePause(actx, true); 10187 else if (action == ACTION_STOP) 10188 { 10189 PrepareToExitPlayer(actx, __LINE__); 10190 SetExitPlayer(true, true); 10191 } 10192 else if (action == "CANCELPLAYLIST") 10193 { 10194 setInPlayList(false); 10195 MythEvent xe("CANCEL_PLAYLIST"); 10196 gCoreContext->dispatch(xe); 10197 } 10198 else if (action == ACTION_JUMPFFWD) 10199 DoJumpFFWD(actx); 10200 else if (action == ACTION_JUMPRWND) 10201 DoJumpRWND(actx); 10202 else if (action.startsWith("DEINTERLACER")) 10203 HandleDeinterlacer(actx, action); 10204 else if (action == ACTION_TOGGLEOSDDEBUG) 10205 ToggleOSDDebug(actx); 10206 else if (action == "TOGGLEMANUALZOOM") 10207 SetManualZoom(actx, true, tr("Zoom Mode ON")); 10208 else if (action == "TOGGLESTRETCH") 10209 ToggleTimeStretch(actx); 10210 else if (action == ACTION_ENABLEUPMIX) 10211 EnableUpmix(actx, true); 10212 else if (action == ACTION_DISABLEUPMIX) 10213 EnableUpmix(actx, false); 10214 else if (action.left(13) == "ADJUSTSTRETCH") 10215 { 10216 bool floatRead; 10217 float stretch = action.right(action.length() - 13).toFloat(&floatRead); 10218 if (floatRead && 10219 stretch <= 2.0 && 10220 stretch >= 0.48) 10221 { 10222 actx->ts_normal = stretch; // alter speed before display 10223 } 10224 10225 StopFFRew(actx); 10226 10227 if (ContextIsPaused(actx, __FILE__, __LINE__)) 10228 DoTogglePause(actx, true); 10229 10230 ChangeTimeStretch(actx, 0, !floatRead); // just display 10231 } 10232 else if (action.left(11) == "SELECTSCAN_") 10233 { 10234 QString msg = QString::null; 10235 actx->LockDeletePlayer(__FILE__, __LINE__); 10236 actx->player->SetScanType((FrameScanType) action.right(1).toInt()); 10237 actx->UnlockDeletePlayer(__FILE__, __LINE__); 10238 msg = toString(actx->player->GetScanType()); 10239 10240 if (!msg.isEmpty()) 10241 SetOSDMessage(actx, msg); 10242 } 10243 else if (action.left(15) == ACTION_TOGGELAUDIOSYNC) 10244 ChangeAudioSync(actx, 0); 10245 else if (action == ACTION_TOGGLESUBTITLEZOOM) 10246 ChangeSubtitleZoom(actx, 0); 10247 else if (action == ACTION_TOGGLEVISUALISATION) 10248 EnableVisualisation(actx, false, true /*toggle*/); 10249 else if (action == ACTION_ENABLEVISUALISATION) 10250 EnableVisualisation(actx, true); 10251 else if (action == ACTION_DISABLEVISUALISATION) 10252 EnableVisualisation(actx, false); 10253 else if (action.left(11) == ACTION_TOGGLESLEEP) 10254 { 10255 ToggleSleepTimer(actx, action.left(13)); 10256 } 10257 else if (action.left(17) == "TOGGLEPICCONTROLS") 10258 { 10259 adjustingPictureAttribute = (PictureAttribute) 10260 (action.right(1).toInt() - 1); 10261 DoTogglePictureAttribute(actx, kAdjustingPicture_Playback); 10262 } 10263 else if (action.left(18) == ACTION_TOGGLESTUDIOLEVELS) 10264 { 10265 DoToggleStudioLevels(actx); 10266 } 10267 else if (action == ACTION_TOGGLENIGHTMODE) 10268 { 10269 DoToggleNightMode(actx); 10270 } 10271 else if (action.left(12) == "TOGGLEASPECT") 10272 { 10273 ToggleAspectOverride(actx, 10274 (AspectOverrideMode) action.right(1).toInt()); 10275 } 10276 else if (action.left(10) == "TOGGLEFILL") 10277 { 10278 ToggleAdjustFill(actx, (AdjustFillMode) action.right(1).toInt()); 10279 } 10280 else if (action == "AUTODETECT_FILL") 10281 { 10282 actx->player->detect_letter_box->SetDetectLetterbox(!actx->player->detect_letter_box->GetDetectLetterbox()); 10283 } 10284 else if (action == ACTION_GUIDE) 10285 EditSchedule(actx, kScheduleProgramGuide); 10286 else if (action.left(10) == "CHANGROUP_" && db_use_channel_groups) 10287 { 10288 if (action == "CHANGROUP_ALL_CHANNELS") 10289 { 10290 UpdateChannelList(-1); 10291 } 10292 else 10293 { 10294 action.remove("CHANGROUP_"); 10295 10296 UpdateChannelList(action.toInt()); 10297 10298 // make sure the current channel is from the selected group 10299 // or tune to the first in the group 10300 QString cur_channum, new_channum; 10301 if (actx->tvchain) 10302 { 10303 QMutexLocker locker(&channelGroupLock); 10304 const DBChanList &list = channelGroupChannelList; 10305 cur_channum = actx->tvchain->GetChannelName(-1); 10306 new_channum = cur_channum; 10307 10308 DBChanList::const_iterator it = list.begin(); 10309 for (; it != list.end(); ++it) 10310 { 10311 if ((*it).channum == cur_channum) 10312 { 10313 break; 10314 } 10315 } 10316 10317 if (it == list.end()) 10318 { 10319 // current channel not found so switch to the 10320 // first channel in the group 10321 it = list.begin(); 10322 if (it != list.end()) 10323 new_channum = (*it).channum; 10324 } 10325 10326 LOG(VB_CHANNEL, LOG_INFO, LOC + 10327 QString("Channel Group: '%1'->'%2'") 10328 .arg(cur_channum).arg(new_channum)); 10329 } 10330 10331 if (actx->tvchain) 10332 { 10333 // Only change channel if new channel != current channel 10334 if (cur_channum != new_channum && !new_channum.isEmpty()) 10335 { 10336 QMutexLocker locker(&timerIdLock); 10337 queuedInput = new_channum; queuedInput.detach(); 10338 queuedChanNum = new_channum; queuedChanNum.detach(); 10339 queuedChanID = 0; 10340 if (!queueInputTimerId) 10341 queueInputTimerId = StartTimer(10, __LINE__); 10342 } 10343 10344 // Turn off OSD Channel Num so the channel 10345 // changes right away 10346 HideOSDWindow(actx, "osd_input"); 10347 } 10348 } 10349 } 10350 else if (action == ACTION_FINDER) 10351 EditSchedule(actx, kScheduleProgramFinder); 10352 else if (action == "SCHEDULE") 10353 EditSchedule(actx, kScheduledRecording); 10354 else if (action == ACTION_VIEWSCHEDULED) 10355 EditSchedule(actx, kViewSchedule); 10356 else if (action.startsWith("VISUALISER")) 10357 EnableVisualisation(actx, true, false, action); 10358 else if (action.startsWith("3D")) 10359 Handle3D(actx, action); 10360 else if (HandleJumpToProgramAction(actx, QStringList(action))) 10361 { 10362 } 10363 else if (PxPHandleAction(actx, QStringList(action))) 10364 { 10365 for (uint i = 0; i < player.size(); i++) 10366 ClearOSD(GetPlayer(actx,i)); 10367 actx = GetPlayer(actx,-1); // "NEXTPIPWINDOW" changes active context.. 10368 } 10369 else if (StateIsLiveTV(GetState(actx))) 10370 { 10371 if (action == "TOGGLEBROWSE") 10372 browsehelper->BrowseStart(actx); 10373 else if (action == "PREVCHAN") 10374 PopPreviousChannel(actx, true); 10375 else if (action.left(14) == "SWITCHTOINPUT_") 10376 { 10377 switchToInputId = action.mid(14).toUInt(); 10378 QMutexLocker locker(&timerIdLock); 10379 if (!switchToInputTimerId) 10380 switchToInputTimerId = StartTimer(1, __LINE__); 10381 } 10382 else if (action == "EDIT") 10383 { 10384 StartChannelEditMode(actx); 10385 hide = false; 10386 } 10387 else 10388 { 10389 LOG(VB_GENERAL, LOG_ERR, LOC + 10390 "Unknown menu action selected: " + action); 10391 hide = false; 10392 } 10393 } 10394 else if (StateIsPlaying(actx->GetState())) 10395 { 10396 if (action == ACTION_JUMPTODVDROOTMENU || 10397 action == ACTION_JUMPTODVDCHAPTERMENU || 10398 action == ACTION_JUMPTOPOPUPMENU || 10399 action == ACTION_JUMPTODVDTITLEMENU) 10400 { 10401 QString menu = "root"; 10402 if (action == ACTION_JUMPTODVDCHAPTERMENU) 10403 menu = "chapter"; 10404 else if (action == ACTION_JUMPTODVDTITLEMENU) 10405 menu = "title"; 10406 else if (action == ACTION_JUMPTOPOPUPMENU) 10407 menu = "popup"; 10408 actx->LockDeletePlayer(__FILE__, __LINE__); 10409 if (actx->player) 10410 actx->player->GoToMenu(menu); 10411 actx->UnlockDeletePlayer(__FILE__, __LINE__); 10412 } 10413 else if (action.left(13) == ACTION_JUMPCHAPTER) 10414 { 10415 int chapter = action.right(3).toInt(); 10416 DoJumpChapter(actx, chapter); 10417 } 10418 else if (action.left(11) == ACTION_SWITCHTITLE) 10419 { 10420 int title = action.right(3).toInt(); 10421 DoSwitchTitle(actx, title); 10422 } 10423 else if (action.left(13) == ACTION_SWITCHANGLE) 10424 { 10425 int angle = action.right(3).toInt(); 10426 DoSwitchAngle(actx, angle); 10427 } 10428 else if (action == "EDIT") 10429 { 10430 StartProgramEditMode(actx); 10431 hide = false; 10432 } 10433 else if (action == "TOGGLEAUTOEXPIRE") 10434 ToggleAutoExpire(actx); 10435 else if (action.left(14) == "TOGGLECOMMSKIP") 10436 SetAutoCommercialSkip( 10437 actx, (CommSkipMode)(action.right(1).toInt())); 10438 else if (action == "QUEUETRANSCODE") 10439 DoQueueTranscode(actx, "Default"); 10440 else if (action == "QUEUETRANSCODE_AUTO") 10441 DoQueueTranscode(actx, "Autodetect"); 10442 else if (action == "QUEUETRANSCODE_HIGH") 10443 DoQueueTranscode(actx, "High Quality"); 10444 else if (action == "QUEUETRANSCODE_MEDIUM") 10445 DoQueueTranscode(actx, "Medium Quality"); 10446 else if (action == "QUEUETRANSCODE_LOW") 10447 DoQueueTranscode(actx, "Low Quality"); 10448 else 10449 { 10450 LOG(VB_GENERAL, LOG_ERR, LOC + 10451 "Unknown menu action selected: " + action); 10452 hide = false; 10453 } 10454 } 10455 10456 if (hide) 10457 { 10458 OSD *osd = GetOSDLock(actx); 10459 if (osd) 10460 osd->DialogQuit(); 10461 ReturnOSDLock(actx, osd); 10462 } 10463 10464 ReturnPlayerLock(actx); 10465 } 10466 10467 bool TV::DialogIsVisible(PlayerContext *ctx, const QString &dialog) 10468 { 10469 bool visible = false; 10470 OSD *osd = GetOSDLock(ctx); 10471 if (osd) 10472 visible = osd->DialogVisible(dialog); 10473 ReturnOSDLock(ctx, osd); 10474 return visible; 10475 } 10476 10477 void TV::HandleOSDInfo(PlayerContext *ctx, QString action) 10478 { 10479 if (!DialogIsVisible(ctx, OSD_DLG_INFO)) 10480 return; 10481 10482 if (action == "CHANNELLOCK") 10483 { 10484 lockTimerOn = false; 10485 } 10486 } 10487 10488 void TV::ShowOSDMenu(const PlayerContext *ctx, const QString category, 10489 const QString selected) 10490 { 10491 QString cat = category.isEmpty() ? "MAIN" : category; 10492 10493 OSD *osd = GetOSDLock(ctx); 10494 if (osd) 10495 { 10496 osd->DialogShow(OSD_DLG_MENU, tr("Playback Menu")); 10497 QString currenttext = QString(); 10498 QString back = QString(); 10499 10500 FillOSDMenuAudio (ctx, osd, cat, selected, currenttext, back); 10501 FillOSDMenuVideo (ctx, osd, cat, selected, currenttext, back); 10502 FillOSDMenuSubtitles(ctx, osd, cat, selected, currenttext, back); 10503 FillOSDMenuPlayback (ctx, osd, cat, selected, currenttext, back); 10504 FillOSDMenuNavigate (ctx, osd, cat, selected, currenttext, back); 10505 FillOSDMenuSchedule (ctx, osd, cat, selected, currenttext, back); 10506 FillOSDMenuSource (ctx, osd, cat, selected, currenttext, back); 10507 FillOSDMenuJobs (ctx, osd, cat, selected, currenttext, back); 10508 10509 if (!currenttext.isEmpty()) 10510 osd->DialogSetText(currenttext); 10511 if (!back.isEmpty() && !category.isEmpty()) 10512 osd->DialogBack(cat, QString("DIALOG_MENU_%1_0").arg(back)); 10513 } 10514 ReturnOSDLock(ctx, osd); 10515 } 10516 10517 void TV::FillOSDMenuAudio(const PlayerContext *ctx, OSD *osd, 10518 QString category, const QString selected, 10519 QString ¤ttext, QString &backaction) 10520 { 10521 QStringList tracks; 10522 uint curtrack = ~0; 10523 bool avsync = true; 10524 bool visual = false; 10525 QString active = QString(""); 10526 bool upmixing = false; 10527 bool canupmix = false; 10528 ctx->LockDeletePlayer(__FILE__, __LINE__); 10529 if (ctx->player) 10530 { 10531 visual = ctx->player->CanVisualise(); 10532 active = ctx->player->GetVisualiserName(); 10533 tracks = ctx->player->GetTracks(kTrackTypeAudio); 10534 if (!tracks.empty()) 10535 curtrack = (uint) ctx->player->GetTrack(kTrackTypeAudio); 10536 avsync = (ctx->player->GetTrackCount(kTrackTypeVideo) > 0) && 10537 !tracks.empty(); 10538 upmixing = ctx->player->GetAudio()->IsUpmixing(); 10539 canupmix = ctx->player->GetAudio()->CanUpmix(); 10540 } 10541 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 10542 10543 if (tracks.empty()) // No audio 10544 return; 10545 10546 if (category == "MAIN") 10547 { 10548 osd->DialogAddButton(tr("Audio"), "DIALOG_MENU_AUDIO_0", 10549 true, selected == "AUDIO"); 10550 } 10551 else if (category == "AUDIO") 10552 { 10553 // TODO Add mute and volume 10554 backaction = "MAIN"; 10555 currenttext = tr("Audio"); 10556 if (tracks.size() > 1) 10557 { 10558 osd->DialogAddButton(tr("Select Audio Track"), 10559 "DIALOG_MENU_AUDIOTRACKS_0", true, 10560 selected == "AUDIOTRACKS"); 10561 } 10562 if (avsync) 10563 osd->DialogAddButton(tr("Adjust Audio Sync"), ACTION_TOGGELAUDIOSYNC); 10564 if (visual) 10565 { 10566 osd->DialogAddButton(tr("Visualisation"), 10567 "DIALOG_MENU_VISUALISATIONS_0", true, 10568 selected == "VISUALISATIONS"); 10569 } 10570 10571 if (canupmix) 10572 { 10573 if (upmixing) 10574 { 10575 osd->DialogAddButton(tr("Disable Audio Upmixer"), 10576 ACTION_DISABLEUPMIX); 10577 } 10578 else 10579 { 10580 osd->DialogAddButton(tr("Enable Audio Upmixer"), 10581 ACTION_ENABLEUPMIX); 10582 } 10583 } 10584 10585 } 10586 else if (category == "AUDIOTRACKS") 10587 { 10588 backaction = "AUDIO"; 10589 currenttext = tr("Select Audio Track"); 10590 for (uint i = 0; i < (uint)tracks.size(); i++) 10591 { 10592 osd->DialogAddButton(tracks[i], 10593 "SELECTAUDIO_" + QString::number(i), 10594 false, i == curtrack); 10595 } 10596 } 10597 else if (category == "VISUALISATIONS") 10598 { 10599 backaction = "AUDIO"; 10600 currenttext = tr("Visualisation"); 10601 osd->DialogAddButton(tr("None"), 10602 ACTION_DISABLEVISUALISATION, false, 10603 active.isEmpty()); 10604 QStringList visualisers; 10605 ctx->LockDeletePlayer(__FILE__, __LINE__); 10606 if (ctx->player) 10607 visualisers = ctx->player->GetVisualiserList(); 10608 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 10609 for (int i = 0; i < visualisers.size(); i++) 10610 { 10611 osd->DialogAddButton(visualisers[i], 10612 "VISUALISER_" + visualisers[i], false, 10613 active == visualisers[i]); 10614 } 10615 } 10616 } 10617 10618 void TV::FillOSDMenuVideo(const PlayerContext *ctx, OSD *osd, 10619 QString category, const QString selected, 10620 QString ¤ttext, QString &backaction) 10621 { 10622 QStringList tracks; 10623 //uint curtrack = ~0; 10624 uint sup = kPictureAttributeSupported_None; 10625 bool studio_levels = false; 10626 bool autodetect = false; 10627 AdjustFillMode adjustfill = kAdjustFill_Off; 10628 AspectOverrideMode aspectoverride = kAspect_Off; 10629 FrameScanType scan_type = kScan_Ignore; 10630 bool scan_type_locked = false; 10631 bool stereoallowed = false; 10632 StereoscopicMode stereomode = kStereoscopicModeNone; 10633 10634 ctx->LockDeletePlayer(__FILE__, __LINE__); 10635 if (ctx->player) 10636 { 10637 tracks = ctx->player->GetTracks(kTrackTypeVideo); 10638 aspectoverride = ctx->player->GetAspectOverride(); 10639 adjustfill = ctx->player->GetAdjustFill(); 10640 scan_type = ctx->player->GetScanType(); 10641 scan_type_locked = ctx->player->IsScanTypeLocked(); 10642 //if (!tracks.empty()) 10643 // curtrack = (uint) ctx->player->GetTrack(kTrackTypeVideo); 10644 VideoOutput *vo = ctx->player->GetVideoOutput(); 10645 if (vo) 10646 { 10647 sup = vo->GetSupportedPictureAttributes(); 10648 studio_levels = vo->GetPictureAttribute(kPictureAttribute_StudioLevels) > 0; 10649 autodetect = !vo->hasHWAcceleration(); 10650 stereoallowed = vo->StereoscopicModesAllowed(); 10651 stereomode = vo->GetStereoscopicMode(); 10652 } 10653 } 10654 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 10655 10656 if (tracks.empty()) // No video 10657 return; 10658 10659 if (category == "MAIN") 10660 { 10661 osd->DialogAddButton(tr("Video"), "DIALOG_MENU_VIDEO_0", 10662 true, selected == "VIDEO"); 10663 } 10664 else if (category == "VIDEO") 10665 { 10666 // TODO Add deinterlacer, filters, video track 10667 backaction = "MAIN"; 10668 currenttext = tr("Video"); 10669 if (ctx == GetPlayer(ctx, 0)) 10670 { 10671 osd->DialogAddButton(tr("Change Aspect Ratio"), 10672 "DIALOG_MENU_VIDEOASPECT_0", true, 10673 selected == "VIDEOASPECT"); 10674 osd->DialogAddButton(tr("Adjust Fill"), 10675 "DIALOG_MENU_ADJUSTFILL_0", true, 10676 selected == "ADJUSTFILL"); 10677 osd->DialogAddButton(tr("Manual Zoom Mode"), "TOGGLEMANUALZOOM"); 10678 if (sup != kPictureAttributeSupported_None) 10679 { 10680 osd->DialogAddButton(tr("Adjust Picture"), 10681 "DIALOG_MENU_ADJUSTPICTURE_0", true, 10682 selected == "ADJUSTPICTURE"); 10683 } 10684 } 10685 if (stereoallowed) 10686 { 10687 osd->DialogAddButton(tr("3D"), "DIALOG_MENU_3D_0", 10688 true, selected == "3D"); 10689 } 10690 osd->DialogAddButton(tr("Advanced"), "DIALOG_MENU_ADVANCEDVIDEO_0", 10691 true, selected == "ADVANCEDVIDEO"); 10692 } 10693 else if (category == "VIDEOASPECT") 10694 { 10695 backaction = "VIDEO"; 10696 currenttext = tr("Change Aspect Ratio"); 10697 10698 for (int j = kAspect_Off; j < kAspect_END; j++) 10699 { 10700 // swap 14:9 and 16:9 10701 int i = ((kAspect_14_9 == j) ? kAspect_16_9 : 10702 ((kAspect_16_9 == j) ? kAspect_14_9 : j)); 10703 osd->DialogAddButton(toString((AspectOverrideMode) i), 10704 QString("TOGGLEASPECT%1").arg(i), false, 10705 aspectoverride == i); 10706 } 10707 } 10708 else if (category == "ADJUSTFILL") 10709 { 10710 backaction = "VIDEO"; 10711 currenttext = tr("Adjust Fill"); 10712 10713 if (autodetect) 10714 { 10715 osd->DialogAddButton(tr("Auto Detect"), "AUTODETECT_FILL", 10716 false, (adjustfill == kAdjustFill_AutoDetect_DefaultHalf) || 10717 (adjustfill == kAdjustFill_AutoDetect_DefaultOff)); 10718 } 10719 for (int i = kAdjustFill_Off; i < kAdjustFill_END; i++) 10720 { 10721 osd->DialogAddButton(toString((AdjustFillMode) i), 10722 QString("TOGGLEFILL%1").arg(i), false, 10723 adjustfill == i); 10724 } 10725 } 10726 else if (category == "ADJUSTPICTURE") 10727 { 10728 backaction = "VIDEO"; 10729 currenttext = tr("Adjust Picture"); 10730 for (int i = kPictureAttribute_MIN; i < kPictureAttribute_MAX; i++) 10731 { 10732 if (toMask((PictureAttribute)i) & sup) 10733 { 10734 if ((PictureAttribute)i == kPictureAttribute_StudioLevels) 10735 { 10736 QString msg = studio_levels ? tr("Disable studio levels") : 10737 tr("Enable studio levels"); 10738 osd->DialogAddButton(msg, ACTION_TOGGLESTUDIOLEVELS); 10739 } 10740 else 10741 { 10742 osd->DialogAddButton(toString((PictureAttribute) i), 10743 QString("TOGGLEPICCONTROLS%1").arg(i)); 10744 } 10745 } 10746 } 10747 osd->DialogAddButton( 10748 gCoreContext->GetNumSetting("NightModeEnabled", 0) ? 10749 tr("Disable Night Mode") : tr("Enable Night Mode"), 10750 ACTION_TOGGLENIGHTMODE); 10751 } 10752 else if (category == "3D") 10753 { 10754 backaction = "VIDEO"; 10755 currenttext = tr("3D"); 10756 osd->DialogAddButton(tr("None"), 10757 ACTION_3DNONE, false, 10758 stereomode == kStereoscopicModeNone); 10759 osd->DialogAddButton(tr("Side by Side"), 10760 ACTION_3DSIDEBYSIDE, false, 10761 stereomode == kStereoscopicModeSideBySide); 10762 osd->DialogAddButton(tr("Discard Side by Side"), 10763 ACTION_3DSIDEBYSIDEDISCARD, false, 10764 stereomode == kStereoscopicModeSideBySideDiscard); 10765 osd->DialogAddButton(tr("Top and Bottom"), 10766 ACTION_3DTOPANDBOTTOM, false, 10767 stereomode == kStereoscopicModeTopAndBottom); 10768 osd->DialogAddButton(tr("Discard Top and Bottom"), 10769 ACTION_3DTOPANDBOTTOMDISCARD, false, 10770 stereomode == kStereoscopicModeTopAndBottomDiscard); 10771 } 10772 else if (category == "ADVANCEDVIDEO") 10773 { 10774 osd->DialogAddButton(tr("Video Scan"), 10775 "DIALOG_MENU_VIDEOSCAN_0", true, 10776 selected == "VIDEOSCAN"); 10777 if (kScan_Progressive != scan_type) 10778 { 10779 osd->DialogAddButton(tr("Deinterlacer"), 10780 "DIALOG_MENU_DEINTERLACER_0", true, 10781 selected == "DEINTERLACER"); 10782 } 10783 backaction = "VIDEO"; 10784 currenttext = tr("Advanced"); 10785 } 10786 else if (category == "DEINTERLACER") 10787 { 10788 backaction = "ADVANCEDVIDEO"; 10789 currenttext = tr("Deinterlacer"); 10790 10791 QStringList deinterlacers; 10792 QString currentdeinterlacer; 10793 bool doublerate = false; 10794 ctx->LockDeletePlayer(__FILE__, __LINE__); 10795 if (ctx->player && ctx->player->GetVideoOutput()) 10796 { 10797 ctx->player->GetVideoOutput()->GetDeinterlacers(deinterlacers); 10798 currentdeinterlacer = ctx->player->GetVideoOutput()->GetDeinterlacer(); 10799 doublerate = ctx->player->CanSupportDoubleRate(); 10800 } 10801 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 10802 10803 foreach (QString deint, deinterlacers) 10804 { 10805 if ((deint.contains("doublerate") || 10806 deint.contains("doubleprocess") || 10807 deint.contains("bobdeint")) && !doublerate) 10808 { 10809 continue; 10810 } 10811 QString trans = VideoDisplayProfile::GetDeinterlacerName(deint); 10812 osd->DialogAddButton(trans, "DEINTERLACER_" + deint, false, 10813 deint == currentdeinterlacer); 10814 } 10815 } 10816 else if (category == "VIDEOSCAN") 10817 { 10818 backaction = "ADVANCEDVIDEO"; 10819 currenttext = tr("Video Scan"); 10820 10821 QString cur_mode = ""; 10822 if (!scan_type_locked) 10823 { 10824 if (kScan_Interlaced == scan_type) 10825 cur_mode = tr("(I)", "Interlaced (Normal)"); 10826 else if (kScan_Intr2ndField == scan_type) 10827 cur_mode = tr("(i)", "Interlaced (Reversed)"); 10828 else if (kScan_Progressive == scan_type) 10829 cur_mode = tr("(P)", "Progressive"); 10830 cur_mode = " " + cur_mode; 10831 scan_type = kScan_Detect; 10832 } 10833 10834 osd->DialogAddButton(tr("Detect") + cur_mode, "SELECTSCAN_0", false, 10835 scan_type == kScan_Detect); 10836 osd->DialogAddButton(tr("Progressive"), "SELECTSCAN_3", false, 10837 scan_type == kScan_Progressive); 10838 osd->DialogAddButton(tr("Interlaced (Normal)"), "SELECTSCAN_1", false, 10839 scan_type == kScan_Interlaced); 10840 osd->DialogAddButton(tr("Interlaced (Reversed)"), "SELECTSCAN_2", false, 10841 scan_type == kScan_Intr2ndField); 10842 } 10843 } 10844 10845 void TV::FillOSDMenuSubtitles(const PlayerContext *ctx, OSD *osd, 10846 QString category, const QString selected, 10847 QString ¤ttext, QString &backaction) 10848 { 10849 uint capmode = 0; 10850 QStringList av_tracks; 10851 QStringList cc708_tracks; 10852 QStringList cc608_tracks; 10853 QStringList ttx_tracks; 10854 QStringList ttm_tracks; 10855 QStringList text_tracks; 10856 uint av_curtrack = ~0; 10857 uint cc708_curtrack = ~0; 10858 uint cc608_curtrack = ~0; 10859 uint ttx_curtrack = ~0; 10860 uint text_curtrack = ~0; 10861 bool havetext = false; 10862 bool forcedon = true; 10863 bool enabled = false; 10864 ctx->LockDeletePlayer(__FILE__, __LINE__); 10865 if (ctx->player) 10866 { 10867 capmode = ctx->player->GetCaptionMode(); 10868 enabled = ctx->player->GetCaptionsEnabled(); 10869 havetext = ctx->player->HasTextSubtitles(); 10870 forcedon = ctx->player->GetAllowForcedSubtitles(); 10871 av_tracks = ctx->player->GetTracks(kTrackTypeSubtitle); 10872 cc708_tracks = ctx->player->GetTracks(kTrackTypeCC708); 10873 cc608_tracks = ctx->player->GetTracks(kTrackTypeCC608); 10874 ttx_tracks = ctx->player->GetTracks(kTrackTypeTeletextCaptions); 10875 ttm_tracks = ctx->player->GetTracks(kTrackTypeTeletextMenu); 10876 text_tracks = ctx->player->GetTracks(kTrackTypeRawText); 10877 if (!av_tracks.empty()) 10878 av_curtrack = (uint) ctx->player->GetTrack(kTrackTypeSubtitle); 10879 if (!cc708_tracks.empty()) 10880 cc708_curtrack = (uint) ctx->player->GetTrack(kTrackTypeCC708); 10881 if (!cc608_tracks.empty()) 10882 cc608_curtrack = (uint) ctx->player->GetTrack(kTrackTypeCC608); 10883 if (!ttx_tracks.empty()) 10884 ttx_curtrack = (uint) ctx->player->GetTrack(kTrackTypeTeletextCaptions); 10885 if (!text_tracks.empty()) 10886 text_curtrack = (uint) ctx->player->GetTrack(kTrackTypeRawText); 10887 } 10888 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 10889 10890 bool have_subs = !av_tracks.empty() || havetext || !cc708_tracks.empty() || 10891 !cc608_tracks.empty() || !ttx_tracks.empty() || 10892 !text_tracks.empty(); 10893 10894 if (category == "MAIN") 10895 { 10896 if (have_subs || !ttm_tracks.empty()) 10897 { 10898 osd->DialogAddButton(tr("Subtitles"), 10899 "DIALOG_MENU_SUBTITLES_0", 10900 true, selected == "SUBTITLES"); 10901 } 10902 } 10903 else if (category == "SUBTITLES") 10904 { 10905 backaction = "MAIN"; 10906 currenttext = tr("Subtitles"); 10907 10908 if (have_subs && enabled) 10909 osd->DialogAddButton(tr("Disable Subtitles"), ACTION_DISABLESUBS); 10910 else if (have_subs && !enabled) 10911 osd->DialogAddButton(tr("Enable Subtitles"), ACTION_ENABLESUBS); 10912 if (!av_tracks.empty()) 10913 { 10914 if (forcedon) 10915 { 10916 osd->DialogAddButton(tr("Disable Forced Subtitles"), 10917 ACTION_DISABLEFORCEDSUBS); 10918 } 10919 else 10920 { 10921 osd->DialogAddButton(tr("Enable Forced Subtitles"), 10922 ACTION_ENABLEFORCEDSUBS); 10923 } 10924 osd->DialogAddButton(tr("Select Subtitle"), 10925 "DIALOG_MENU_AVSUBTITLES_0", 10926 true, selected == "AVSUBTITLES"); 10927 } 10928 if (havetext || !text_tracks.empty()) 10929 { 10930 osd->DialogAddButton(tr("Text Subtitles"), 10931 "DIALOG_MENU_TEXTSUBTITLES_0", 10932 true, selected == "TEXTSUBTITLES"); 10933 } 10934 if (!cc708_tracks.empty()) 10935 { 10936 osd->DialogAddButton(tr("Select ATSC CC"), 10937 "DIALOG_MENU_708SUBTITLES_0", 10938 true, selected == "708SUBTITLES"); 10939 } 10940 if (!cc608_tracks.empty()) 10941 { 10942 osd->DialogAddButton(tr("Select VBI CC"), 10943 "DIALOG_MENU_608SUBTITLES_0", 10944 true, selected == "608SUBTITLES"); 10945 } 10946 if (!ttx_tracks.empty()) 10947 { 10948 osd->DialogAddButton(tr("Select Teletext CC"), 10949 "DIALOG_MENU_TTXSUBTITLES_0", 10950 true, selected == "TTXSUBTITLES"); 10951 } 10952 if (!ttm_tracks.empty()) 10953 osd->DialogAddButton(tr("Toggle Teletext Menu"), "TOGGLETTM"); 10954 if (enabled) 10955 osd->DialogAddButton(tr("Adjust Subtitle Zoom"), 10956 ACTION_TOGGLESUBTITLEZOOM); 10957 } 10958 else if (category == "AVSUBTITLES") 10959 { 10960 backaction = "SUBTITLES"; 10961 currenttext = tr("Select Subtitle"); 10962 for (uint i = 0; i < (uint)av_tracks.size(); i++) 10963 { 10964 osd->DialogAddButton(av_tracks[i], 10965 "SELECTSUBTITLE_" + QString::number(i), 10966 false, i == av_curtrack); 10967 } 10968 } 10969 else if (category == "TEXTSUBTITLES") 10970 { 10971 backaction = "SUBTITLES"; 10972 currenttext = tr("Text Subtitles"); 10973 if (havetext) 10974 { 10975 if (capmode == kDisplayTextSubtitle) 10976 { 10977 osd->DialogAddButton(tr("Disable External Subtitles"), 10978 ACTION_DISABLEEXTTEXT); 10979 } 10980 else 10981 { 10982 osd->DialogAddButton(tr("Enable External Subtitles"), 10983 ACTION_ENABLEEXTTEXT); 10984 } 10985 } 10986 if (!text_tracks.empty()) 10987 { 10988 for (uint i = 0; i < (uint)text_tracks.size(); i++) 10989 { 10990 osd->DialogAddButton(text_tracks[i], 10991 "SELECTRAWTEXT_" + QString::number(i), 10992 false, i == text_curtrack); 10993 } 10994 } 10995 } 10996 else if (category == "708SUBTITLES") 10997 { 10998 backaction = "SUBTITLES"; 10999 currenttext = tr("Select ATSC CC"); 11000 for (uint i = 0; i < (uint)cc708_tracks.size(); i++) 11001 { 11002 osd->DialogAddButton(cc708_tracks[i], 11003 "SELECTCC708_" + QString::number(i), 11004 false, i == cc708_curtrack); 11005 } 11006 } 11007 else if (category == "608SUBTITLES") 11008 { 11009 backaction = "SUBTITLES"; 11010 currenttext = tr("Select VBI CC"); 11011 for (uint i = 0; i < (uint)cc608_tracks.size(); i++) 11012 { 11013 osd->DialogAddButton(cc608_tracks[i], 11014 "SELECTCC608_" + QString::number(i), 11015 false, i == cc608_curtrack); 11016 } 11017 } 11018 else if (category == "TTXSUBTITLES") 11019 { 11020 backaction = "SUBTITLES"; 11021 currenttext = tr("Select Teletext CC"); 11022 for (uint i = 0; i < (uint)ttx_tracks.size(); i++) 11023 { 11024 osd->DialogAddButton(ttx_tracks[i], 11025 "SELECTTTC_" + QString::number(i), 11026 false, i == ttx_curtrack); 11027 } 11028 } 11029 } 11030 11031 void TV::FillOSDMenuNavigate(const PlayerContext *ctx, OSD *osd, 11032 QString category, const QString selected, 11033 QString ¤ttext, QString &backaction) 11034 { 11035 int num_chapters = GetNumChapters(ctx); 11036 int num_titles = GetNumTitles(ctx); 11037 int num_angles = GetNumAngles(ctx); 11038 TVState state = ctx->GetState(); 11039 bool isdvd = state == kState_WatchingDVD; 11040 bool isbd = ctx->buffer && ctx->buffer->IsBD() && 11041 ctx->buffer->BD()->IsHDMVNavigation(); 11042 bool islivetv = StateIsLiveTV(state); 11043 bool isrecording = state == kState_WatchingPreRecorded || 11044 state == kState_WatchingRecording; 11045 bool previouschan = false; 11046 if (islivetv) 11047 { 11048 QString prev_channum = ctx->GetPreviousChannel(); 11049 QString cur_channum = QString(); 11050 if (ctx->tvchain) 11051 cur_channum = ctx->tvchain->GetChannelName(-1); 11052 if (!prev_channum.isEmpty() && prev_channum != cur_channum) 11053 previouschan = true; 11054 } 11055 11056 bool jump = !num_chapters && !isdvd && !isbd && 11057 ctx->buffer->IsSeekingAllowed(); 11058 bool show = isdvd || num_chapters || num_titles || previouschan || 11059 isrecording || num_angles || jump; 11060 11061 if (category == "MAIN") 11062 { 11063 if (show) 11064 { 11065 osd->DialogAddButton(tr("Navigate"), "DIALOG_MENU_NAVIGATE_0", 11066 true, selected == "NAVIGATE"); 11067 } 11068 } 11069 else if (category == "NAVIGATE") 11070 { 11071 backaction = "MAIN"; 11072 currenttext = tr("Navigate"); 11073 if (jump) 11074 { 11075 osd->DialogAddButton(tr("Jump Ahead"), ACTION_JUMPFFWD, false, false); 11076 osd->DialogAddButton(tr("Jump Back"), ACTION_JUMPRWND, false, false); 11077 } 11078 if (isrecording) 11079 { 11080 osd->DialogAddButton(tr("Commercial Auto-Skip"), 11081 "DIALOG_MENU_COMMSKIP_0", 11082 true, selected == "COMMSKIP"); 11083 } 11084 if (isbd) 11085 { 11086 osd->DialogAddButton(tr("Top menu"), ACTION_JUMPTODVDROOTMENU); 11087 osd->DialogAddButton(tr("Popup menu"), ACTION_JUMPTOPOPUPMENU); 11088 } 11089 if (isdvd) 11090 { 11091 osd->DialogAddButton(tr("DVD Root Menu"), ACTION_JUMPTODVDROOTMENU); 11092 osd->DialogAddButton(tr("DVD Title Menu"), ACTION_JUMPTODVDTITLEMENU); 11093 osd->DialogAddButton(tr("DVD Chapter Menu"), ACTION_JUMPTODVDCHAPTERMENU); 11094 } 11095 if (previouschan) 11096 { 11097 osd->DialogAddButton(tr("Previous Channel"), "PREVCHAN"); 11098 } 11099 if (num_chapters) 11100 { 11101 osd->DialogAddButton(tr("Chapter"), "DIALOG_MENU_AVCHAPTER_0", 11102 true, selected == "AVCHAPTER"); 11103 } 11104 if (num_angles > 1) 11105 { 11106 osd->DialogAddButton(tr("Angle"), "DIALOG_MENU_AVANGLE_0", 11107 true, selected == "AVANGLE"); 11108 } 11109 if (num_titles) 11110 { 11111 osd->DialogAddButton(tr("Title"), "DIALOG_MENU_AVTITLE_0", 11112 true, selected == "AVTITLE"); 11113 } 11114 } 11115 else if (category == "AVCHAPTER") 11116 { 11117 backaction = "NAVIGATE"; 11118 currenttext = tr("Chapter"); 11119 int current_chapter = GetCurrentChapter(ctx); 11120 QList<long long> times; 11121 GetChapterTimes(ctx, times); 11122 if (num_chapters == times.size()) 11123 { 11124 int size = QString::number(num_chapters).size(); 11125 for (int i = 0; i < num_chapters; i++) 11126 { 11127 int hours = times[i] / 60 / 60; 11128 int minutes = (times[i] / 60) - (hours * 60); 11129 int secs = times[i] % 60; 11130 QString chapter1 = QString("%1").arg(i+1, size, 10, QChar(48)); 11131 QString chapter2 = QString("%1").arg(i+1, 3 , 10, QChar(48)); 11132 QString desc = chapter1 + QString(" (%1:%2:%3)") 11133 .arg(hours, 2, 10, QChar(48)).arg(minutes, 2, 10, QChar(48)) 11134 .arg(secs, 2, 10, QChar(48)); 11135 osd->DialogAddButton(desc, ACTION_JUMPCHAPTER + chapter2, 11136 false, current_chapter == (i + 1)); 11137 } 11138 } 11139 } 11140 else if (category == "AVTITLE") 11141 { 11142 backaction = "NAVIGATE"; 11143 currenttext = tr("Title"); 11144 int current_title = GetCurrentTitle(ctx); 11145 11146 for (int i = 0; i < num_titles; i++) 11147 { 11148 if (GetTitleDuration(ctx, i) < 120) // Ignore < 2 minutes long 11149 continue; 11150 11151 QString titleIdx = QString("%1").arg(i, 3, 10, QChar(48)); 11152 QString desc = GetTitleName(ctx, i); 11153 osd->DialogAddButton(desc, ACTION_SWITCHTITLE + titleIdx, 11154 false, current_title == i); 11155 } 11156 } 11157 else if (category == "AVANGLE") 11158 { 11159 backaction = "NAVIGATE"; 11160 currenttext = tr("Angle"); 11161 int current_angle = GetCurrentAngle(ctx); 11162 11163 for (int i = 0; i < num_angles; i++) 11164 { 11165 QString angleIdx = QString("%1").arg(i, 3, 10, QChar(48)); 11166 QString desc = GetAngleName(ctx, i); 11167 osd->DialogAddButton(desc, ACTION_SWITCHANGLE + angleIdx, 11168 false, current_angle == i); 11169 } 11170 } 11171 else if (category == "COMMSKIP") 11172 { 11173 backaction = "NAVIGATE"; 11174 currenttext = tr("Commercial Auto-Skip"); 11175 uint cas_ord[] = { 0, 2, 1 }; 11176 ctx->LockDeletePlayer(__FILE__, __LINE__); 11177 CommSkipMode cur = kCommSkipOff; 11178 if (ctx->player) 11179 cur = ctx->player->GetAutoCommercialSkip(); 11180 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 11181 11182 for (uint i = 0; i < sizeof(cas_ord)/sizeof(uint); i++) 11183 { 11184 const CommSkipMode mode = (CommSkipMode) cas_ord[i]; 11185 osd->DialogAddButton(toString((CommSkipMode) cas_ord[i]), 11186 QString("TOGGLECOMMSKIP%1").arg(cas_ord[i]), 11187 false, mode == cur); 11188 } 11189 } 11190 } 11191 11192 void TV::FillOSDMenuSource(const PlayerContext *ctx, OSD *osd, 11193 QString category, const QString selected, 11194 QString ¤ttext, QString &backaction) 11195 { 11196 QMap<uint,InputInfo> sources; 11197 vector<uint> cardids; 11198 uint cardid = 0; 11199 vector<uint> excluded_cardids; 11200 uint sourceid = 0; 11201 11202 if ((category == "SOURCE" || category == "INPUTSWITCHING"|| 11203 category == "SOURCESWITCHING") && ctx->recorder) 11204 { 11205 cardids = CardUtil::GetCardList(); 11206 cardid = ctx->GetCardID(); 11207 // The cardids are already in the preferred order. Don't 11208 // alter it if switching sources. 11209 if (category != "SOURCESWITCHING") 11210 stable_sort(cardids.begin(), cardids.end()); 11211 excluded_cardids.push_back(cardid); 11212 InfoMap info; 11213 ctx->recorder->GetChannelInfo(info); 11214 sourceid = info["sourceid"].toUInt(); 11215 11216 if (category != "INPUTSWITCHING") 11217 { 11218 // Get sources available on other cards 11219 vector<uint>::const_iterator it = cardids.begin(); 11220 for (; it != cardids.end(); ++it) 11221 { 11222 vector<InputInfo> inputs = 11223 RemoteRequestFreeInputList(*it, excluded_cardids); 11224 if (inputs.empty()) 11225 continue; 11226 11227 for (uint i = 0; i < inputs.size(); i++) 11228 if (!sources.contains(inputs[i].sourceid)) 11229 sources[inputs[i].sourceid] = inputs[i]; 11230 } 11231 // Get other sources available on this card 11232 vector<uint> currentinputs = CardUtil::GetInputIDs(cardid); 11233 if (!currentinputs.empty()) 11234 { 11235 for (uint i = 0; i < currentinputs.size(); i++) 11236 { 11237 InputInfo info; 11238 info.inputid = currentinputs[i]; 11239 if (CardUtil::GetInputInfo(info)) 11240 if (!sources.contains(info.sourceid) && 11241 info.livetvorder) 11242 sources[info.sourceid] = info; 11243 } 11244 } 11245 // delete current source from list 11246 sources.remove(sourceid); 11247 } 11248 } 11249 11250 if (category == "MAIN") 11251 { 11252 osd->DialogAddButton(tr("Source"), "DIALOG_MENU_SOURCE_0", 11253 true, selected == "SOURCE"); 11254 } 11255 else if (category == "SOURCE") 11256 { 11257 backaction = "MAIN"; 11258 currenttext = tr("Source"); 11259 osd->DialogAddButton(tr("Jump to Program"), 11260 "DIALOG_MENU_" + ACTION_JUMPREC + "_0", 11261 true, selected == ACTION_JUMPREC); 11262 11263 vector<uint>::const_iterator it = cardids.begin(); 11264 for (; it != cardids.end(); ++it) 11265 { 11266 vector<InputInfo> inputs = RemoteRequestFreeInputList( 11267 *it, excluded_cardids); 11268 uint testsize = 0; 11269 for (uint i = 0; i < inputs.size(); i++) 11270 { 11271 if ((inputs[i].cardid == cardid) && 11272 (inputs[i].sourceid == sourceid)) 11273 { 11274 testsize = 1; 11275 break; 11276 } 11277 } 11278 if (inputs.size() <= testsize) 11279 continue; 11280 osd->DialogAddButton(tr("Switch Input"), 11281 "DIALOG_MENU_INPUTSWITCHING_0", 11282 true, selected == "INPUTSWITCHING"); 11283 break; 11284 } 11285 if (!sources.empty()) 11286 { 11287 osd->DialogAddButton(tr("Switch Source"), 11288 "DIALOG_MENU_SOURCESWITCHING_0", 11289 true, selected == "SOURCESWITCHING"); 11290 } 11291 } 11292 else if (category == ACTION_JUMPREC) 11293 { 11294 backaction = "SOURCE"; 11295 currenttext = tr("Jump to Program"); 11296 osd->DialogAddButton(tr("Recorded Program"), 11297 "DIALOG_" + ACTION_JUMPREC + "_X_0", 11298 true, selected == ACTION_JUMPREC + "2"); 11299 if (lastProgram != NULL) 11300 { 11301 if (lastProgram->GetSubtitle().isEmpty()) 11302 osd->DialogAddButton(lastProgram->GetTitle(), ACTION_JUMPPREV); 11303 else 11304 osd->DialogAddButton(QString("%1: %2") 11305 .arg(lastProgram->GetTitle()) 11306 .arg(lastProgram->GetSubtitle()), ACTION_JUMPPREV); 11307 } 11308 } 11309 else if (category == "INPUTSWITCHING") 11310 { 11311 backaction = "SOURCE"; 11312 currenttext = tr("Switch Input"); 11313 vector<uint>::const_iterator it = cardids.begin(); 11314 for (; it != cardids.end(); ++it) 11315 { 11316 vector<InputInfo> inputs = RemoteRequestFreeInputList( 11317 *it, excluded_cardids);; 11318 11319 for (uint i = 0; i < inputs.size(); i++) 11320 { 11321 // don't add current input to list 11322 if ((inputs[i].cardid == cardid) && 11323 (inputs[i].sourceid == sourceid)) 11324 { 11325 continue; 11326 } 11327 11328 QString name = CardUtil::GetDisplayName(inputs[i].inputid); 11329 if (name.isEmpty()) 11330 { 11331 name = tr("C", "Card") + ":" + QString::number(*it) + " " + 11332 tr("I", "Input") + ":" + inputs[i].name; 11333 } 11334 11335 osd->DialogAddButton(name, 11336 QString("SWITCHTOINPUT_%1").arg(inputs[i].inputid)); 11337 } 11338 } 11339 } 11340 else if (category == "SOURCESWITCHING") 11341 { 11342 backaction = "SOURCE"; 11343 currenttext = tr("Switch Source"); 11344 QMap<uint,InputInfo>::const_iterator sit = sources.begin(); 11345 for (; sit != sources.end(); ++sit) 11346 { 11347 osd->DialogAddButton(SourceUtil::GetSourceName((*sit).sourceid), 11348 QString("SWITCHTOINPUT_%1").arg((*sit).inputid)); 11349 } 11350 } 11351 } 11352 11353 void TV::FillOSDMenuJobs(const PlayerContext *ctx, OSD *osd, 11354 QString category, const QString selected, 11355 QString ¤ttext, QString &backaction) 11356 { 11357 TVState state = ctx->GetState(); 11358 bool islivetv = StateIsLiveTV(state); 11359 bool isrecorded = state == kState_WatchingPreRecorded; 11360 bool isrecording = state == kState_WatchingRecording; 11361 11362 if (category == "MAIN") 11363 { 11364 if (islivetv || isrecording || isrecorded) 11365 { 11366 osd->DialogAddButton(tr("Jobs"), "DIALOG_MENU_JOBS_0", 11367 true, selected == "JOBS"); 11368 } 11369 } 11370 else if (category == "JOBS") 11371 { 11372 backaction = "MAIN"; 11373 currenttext = tr("Jobs"); 11374 11375 ctx->LockPlayingInfo(__FILE__, __LINE__); 11376 bool is_on = ctx->playingInfo->QueryAutoExpire() != kDisableAutoExpire; 11377 bool transcoding = JobQueue::IsJobQueuedOrRunning( 11378 JOB_TRANSCODE, 11379 ctx->playingInfo->GetChanID(), 11380 ctx->playingInfo->GetScheduledStartTime()); 11381 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 11382 11383 if (islivetv) 11384 { 11385 osd->DialogAddButton(tr("Edit Channel"), "EDIT"); 11386 } 11387 11388 if (isrecorded || isrecording) 11389 { 11390 osd->DialogAddButton(tr("Edit Recording"), "EDIT"); 11391 osd->DialogAddButton(is_on ? tr("Turn Auto-Expire OFF") : 11392 tr("Turn Auto-Expire ON"), "TOGGLEAUTOEXPIRE"); 11393 } 11394 11395 if (isrecorded) 11396 { 11397 if (transcoding) 11398 { 11399 osd->DialogAddButton(tr("Stop Transcoding"), "QUEUETRANSCODE"); 11400 } 11401 else 11402 { 11403 osd->DialogAddButton(tr("Begin Transcoding"), 11404 "DIALOG_MENU_TRANSCODE_0", 11405 true, selected == "TRANSCODE"); 11406 } 11407 } 11408 } 11409 else if (category == "TRANSCODE") 11410 { 11411 backaction = "JOBS"; 11412 currenttext = tr("Begin Transcoding"); 11413 osd->DialogAddButton(tr("Default"), "QUEUETRANSCODE"); 11414 osd->DialogAddButton(tr("Autodetect"), "QUEUETRANSCODE_AUTO"); 11415 osd->DialogAddButton(tr("High Quality"), "QUEUETRANSCODE_HIGH"); 11416 osd->DialogAddButton(tr("Medium Quality"),"QUEUETRANSCODE_MEDIUM"); 11417 osd->DialogAddButton(tr("Low Quality"), "QUEUETRANSCODE_LOW"); 11418 } 11419 } 11420 11421 void TV::FillOSDMenuPlayback(const PlayerContext *ctx, OSD *osd, 11422 QString category, const QString selected, 11423 QString ¤ttext, QString &backaction) 11424 { 11425 bool allowPIP = IsPIPSupported(ctx); 11426 bool allowPBP = IsPBPSupported(ctx); 11427 bool ispaused = false; 11428 ctx->LockDeletePlayer(__FILE__, __LINE__); 11429 if (ctx->player) 11430 ispaused = ctx->player->IsPaused(); 11431 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 11432 11433 if (category == "MAIN") 11434 { 11435 osd->DialogAddButton(tr("Playback"), "DIALOG_MENU_PLAYBACK_0", 11436 true, selected == "PLAYBACK"); 11437 } 11438 else if (category == "PLAYBACK") 11439 { 11440 backaction = "MAIN"; 11441 currenttext = tr("Playback"); 11442 11443 osd->DialogAddButton(ispaused ? tr("Play") : tr("Pause"), 11444 ACTION_PAUSE, false, false); 11445 osd->DialogAddButton(tr("Adjust Time Stretch"), 11446 "DIALOG_MENU_TIMESTRETCH_0", true, 11447 selected == "TIMESTRETCH"); 11448 if (allowPIP || allowPBP) 11449 { 11450 osd->DialogAddButton(tr("Picture-in-Picture"), 11451 "DIALOG_MENU_PIP_1", true, 11452 selected == "PIP"); 11453 } 11454 osd->DialogAddButton(tr("Sleep"), 11455 "DIALOG_MENU_SLEEP_0", true, 11456 selected == "SLEEP"); 11457 if (db_use_channel_groups) 11458 { 11459 osd->DialogAddButton(tr("Channel Groups"), 11460 "DIALOG_MENU_CHANNELGROUP_0", 11461 true, selected == "CHANNELGROUP"); 11462 } 11463 if (!db_browse_always) 11464 osd->DialogAddButton(tr("Toggle Browse Mode"), "TOGGLEBROWSE"); 11465 if (inPlaylist) 11466 osd->DialogAddButton(tr("Cancel Playlist"), "CANCELPLAYLIST"); 11467 osd->DialogAddButton(tr("Playback data"), 11468 ACTION_TOGGLEOSDDEBUG, false, false); 11469 } 11470 else if (category == "TIMESTRETCH") 11471 { 11472 backaction = "PLAYBACK"; 11473 currenttext = tr("Adjust Time Stretch"); 11474 int speedX100 = (int)(round(ctx->ts_normal * 100)); 11475 osd->DialogAddButton(tr("Toggle"), "TOGGLESTRETCH"); 11476 osd->DialogAddButton(tr("Adjust"), "ADJUSTSTRETCH"); 11477 osd->DialogAddButton(tr("0.5X"), "ADJUSTSTRETCH0.5", false, speedX100 == 50); 11478 osd->DialogAddButton(tr("0.9X"), "ADJUSTSTRETCH0.9", false, speedX100 == 90); 11479 osd->DialogAddButton(tr("1.0X"), "ADJUSTSTRETCH1.0", false, speedX100 == 100); 11480 osd->DialogAddButton(tr("1.1X"), "ADJUSTSTRETCH1.1", false, speedX100 == 110); 11481 osd->DialogAddButton(tr("1.2X"), "ADJUSTSTRETCH1.2", false, speedX100 == 120); 11482 osd->DialogAddButton(tr("1.3X"), "ADJUSTSTRETCH1.3", false, speedX100 == 130); 11483 osd->DialogAddButton(tr("1.4X"), "ADJUSTSTRETCH1.4", false, speedX100 == 140); 11484 osd->DialogAddButton(tr("1.5X"), "ADJUSTSTRETCH1.5", false, speedX100 == 150); 11485 } 11486 else if (category == "SLEEP") 11487 { 11488 backaction = "PLAYBACK"; 11489 currenttext = tr("Sleep"); 11490 if (sleepTimerId) 11491 osd->DialogAddButton(tr("Sleep Off"), ACTION_TOGGLESLEEP + "ON"); 11492 osd->DialogAddButton(tr("%n minute(s)", "", 30), 11493 ACTION_TOGGLESLEEP + "30"); 11494 osd->DialogAddButton(tr("%n minute(s)", "", 60), 11495 ACTION_TOGGLESLEEP + "60"); 11496 osd->DialogAddButton(tr("%n minute(s)", "", 90), 11497 ACTION_TOGGLESLEEP + "90"); 11498 osd->DialogAddButton(tr("%n minute(s)", "", 120), 11499 ACTION_TOGGLESLEEP + "120"); 11500 } 11501 else if (category == "CHANNELGROUP") 11502 { 11503 backaction = "PLAYBACK"; 11504 currenttext = tr("Channel Groups"); 11505 osd->DialogAddButton(tr("All Channels"), "CHANGROUP_ALL_CHANNELS"); 11506 ChannelGroupList::const_iterator it; 11507 for (it = db_channel_groups.begin(); 11508 it != db_channel_groups.end(); ++it) 11509 { 11510 osd->DialogAddButton(it->name, 11511 QString("CHANGROUP_%1").arg(it->grpid), 11512 false, (int)(it->grpid) == channelGroupId); 11513 } 11514 } 11515 else if (category == "PIP" && (allowPIP || allowPBP)) 11516 { 11517 backaction = "PLAYBACK"; 11518 currenttext = tr("Picture-in-Picture"); 11519 bool hasPBP = (player.size()>1) && GetPlayer(ctx,1)->IsPBP(); 11520 bool hasPIP = (player.size()>1) && GetPlayer(ctx,1)->IsPIP(); 11521 11522 if (RemoteGetFreeRecorderCount()) 11523 { 11524 if (player.size() <= kMaxPIPCount && !hasPBP && allowPIP) 11525 osd->DialogAddButton(tr("Open Live TV PIP"), "CREATEPIPVIEW"); 11526 if (player.size() < kMaxPBPCount && !hasPIP && allowPBP) 11527 osd->DialogAddButton(tr("Open Live TV PBP"), "CREATEPBPVIEW"); 11528 } 11529 11530 if (player.size() <= kMaxPIPCount && !hasPBP && allowPIP) 11531 osd->DialogAddButton(tr("Open Recording PIP"), "JUMPRECPIP"); 11532 if (player.size() < kMaxPBPCount && !hasPIP && allowPBP) 11533 osd->DialogAddButton(tr("Open Recording PBP"), "JUMPRECPBP"); 11534 11535 if (player.size() > 1) 11536 { 11537 osd->DialogAddButton(tr("Change Active Window"), "NEXTPIPWINDOW"); 11538 11539 QString pipType = (ctx->IsPBP()) ? "PBP" : "PIP"; 11540 QString toggleMode = QString("TOGGLE%1MODE").arg(pipType); 11541 11542 bool isPBP = ctx->IsPBP(); 11543 const PlayerContext *mctx = GetPlayer(ctx, 0); 11544 QString pipClose = (isPBP) ? tr("Close PBP") : tr("Close PIP"); 11545 if (mctx == ctx) 11546 { 11547 if (player.size() > 2) 11548 pipClose = (isPBP) ? tr("Close PBPs") : tr("Close PIPs"); 11549 osd->DialogAddButton(pipClose, toggleMode); 11550 11551 if (player.size() == 2) 11552 osd->DialogAddButton(tr("Swap Windows"), "SWAPPIP"); 11553 } 11554 else 11555 { 11556 osd->DialogAddButton(pipClose, toggleMode); 11557 osd->DialogAddButton(tr("Swap Windows"), "SWAPPIP"); 11558 } 11559 11560 uint max_cnt = min(kMaxPBPCount, kMaxPIPCount+1); 11561 if (player.size() <= max_cnt && 11562 !(hasPIP && !allowPBP) && 11563 !(hasPBP && !allowPIP)) 11564 { 11565 QString switchTo = (isPBP) ? tr("Switch to PIP") : tr("Switch to PBP"); 11566 osd->DialogAddButton(switchTo, "TOGGLEPIPSTATE"); 11567 } 11568 } 11569 } 11570 } 11571 11572 void TV::FillOSDMenuSchedule(const PlayerContext *ctx, OSD *osd, 11573 QString category, const QString selected, 11574 QString ¤ttext, QString &backaction) 11575 { 11576 if (category == "MAIN") 11577 { 11578 osd->DialogAddButton(tr("Schedule"), 11579 "DIALOG_MENU_SCHEDULE_0", 11580 true, selected == "SCHEDULE"); 11581 } 11582 else if (category == "SCHEDULE") 11583 { 11584 backaction = "MAIN"; 11585 currenttext = tr("Schedule"); 11586 osd->DialogAddButton(tr("Program Guide"), ACTION_GUIDE); 11587 osd->DialogAddButton(tr("Program Finder"), ACTION_FINDER); 11588 osd->DialogAddButton(tr("Upcoming Recordings"), ACTION_VIEWSCHEDULED); 11589 osd->DialogAddButton(tr("Edit Recording Schedule"), "SCHEDULE"); 11590 } 11591 } 11592 11593 void TV::FillOSDMenuJumpRec(PlayerContext* ctx, const QString category, 11594 int level, const QString selected) 11595 { 11596 // bool in_recgroup = !category.isEmpty() && level > 0; 11597 if (level < 0 || level > 1) 11598 { 11599 level = 0; 11600 // in_recgroup = false; 11601 } 11602 11603 OSD *osd = GetOSDLock(ctx); 11604 if (osd) 11605 { 11606 QString title = tr("Recorded Program"); 11607 osd->DialogShow("osd_jumprec", title); 11608 11609 QMutexLocker locker(&progListsLock); 11610 progLists.clear(); 11611 vector<ProgramInfo*> *infoList = RemoteGetRecordedList(0); 11612 bool LiveTVInAllPrograms = gCoreContext->GetNumSetting("LiveTVInAllPrograms",0); 11613 if (infoList) 11614 { 11615 QList<QString> titles_seen; 11616 11617 ctx->LockPlayingInfo(__FILE__, __LINE__); 11618 QString currecgroup = ctx->playingInfo->GetRecordingGroup(); 11619 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 11620 11621 vector<ProgramInfo *>::const_iterator it = infoList->begin(); 11622 for ( ; it != infoList->end(); ++it) 11623 { 11624 if ((*it)->GetRecordingGroup() != "LiveTV" || LiveTVInAllPrograms || 11625 (*it)->GetRecordingGroup() == currecgroup) 11626 { 11627 progLists[(*it)->GetRecordingGroup()].push_front( 11628 new ProgramInfo(*(*it))); 11629 } 11630 } 11631 11632 ProgramInfo *lastprog = GetLastProgram(); 11633 QMap<QString,ProgramList>::const_iterator Iprog; 11634 for (Iprog = progLists.begin(); Iprog != progLists.end(); ++Iprog) 11635 { 11636 const ProgramList &plist = *Iprog; 11637 uint progIndex = (uint) plist.size(); 11638 QString group = Iprog.key(); 11639 11640 if (plist[0]->GetRecordingGroup() != currecgroup) 11641 SetLastProgram(plist[0]); 11642 11643 if (progIndex == 1 && level == 0) 11644 { 11645 osd->DialogAddButton(Iprog.key(), 11646 QString("JUMPPROG %1 0").arg(group)); 11647 } 11648 else if (progIndex > 1 && level == 0) 11649 { 11650 QString act = QString("DIALOG_%1_%2_1") 11651 .arg(ACTION_JUMPREC).arg(group); 11652 osd->DialogAddButton(group, act, 11653 true, selected == group); 11654 } 11655 else if (level == 1 && Iprog.key() == category) 11656 { 11657 ProgramList::const_iterator it = plist.begin(); 11658 for (; it != plist.end(); ++it) 11659 { 11660 const ProgramInfo *p = *it; 11661 11662 if (titles_seen.contains(p->GetTitle())) 11663 continue; 11664 11665 titles_seen.push_back(p->GetTitle()); 11666 11667 ProgramList::const_iterator it2 = plist.begin(); 11668 int j = -1; 11669 for (; it2 != plist.end(); ++it2) 11670 { 11671 const ProgramInfo *q = *it2; 11672 j++; 11673 11674 if (q->GetTitle() != p->GetTitle()) 11675 continue; 11676 11677 osd->DialogAddButton(q->GetSubtitle().isEmpty() ? 11678 q->GetTitle() : q->GetSubtitle(), 11679 QString("JUMPPROG %1 %2") 11680 .arg(Iprog.key()).arg(j)); 11681 } 11682 } 11683 } 11684 } 11685 SetLastProgram(lastprog); 11686 if (lastprog) 11687 delete lastprog; 11688 11689 while (!infoList->empty()) 11690 { 11691 delete infoList->back(); 11692 infoList->pop_back(); 11693 } 11694 delete infoList; 11695 } 11696 11697 if (!category.isEmpty()) 11698 { 11699 if (level == 1) 11700 osd->DialogBack(category, "DIALOG_" + ACTION_JUMPREC + "_X_0"); 11701 else if (level == 0) 11702 osd->DialogBack(ACTION_JUMPREC, 11703 "DIALOG_MENU_" + ACTION_JUMPREC +"_0"); 11704 } 11705 } 11706 ReturnOSDLock(ctx, osd); 11707 } 11708 11709 void TV::HandleDeinterlacer(PlayerContext *ctx, const QString &action) 11710 { 11711 if (!action.startsWith("DEINTERLACER")) 11712 return; 11713 11714 QString deint = action.mid(13); 11715 ctx->LockDeletePlayer(__FILE__, __LINE__); 11716 if (ctx->player) 11717 ctx->player->ForceDeinterlacer(deint); 11718 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 11719 } 11720 11721 void TV::ToggleAutoExpire(PlayerContext *ctx) 11722 { 11723 QString desc = QString::null; 11724 11725 ctx->LockPlayingInfo(__FILE__, __LINE__); 11726 11727 if (ctx->playingInfo->QueryAutoExpire() != kDisableAutoExpire) 11728 { 11729 ctx->playingInfo->SaveAutoExpire(kDisableAutoExpire); 11730 desc = tr("Auto-Expire OFF"); 11731 } 11732 else 11733 { 11734 ctx->playingInfo->SaveAutoExpire(kNormalAutoExpire); 11735 desc = tr("Auto-Expire ON"); 11736 } 11737 11738 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 11739 11740 if (!desc.isEmpty()) 11741 UpdateOSDSeekMessage(ctx, desc, kOSDTimeout_Med); 11742 } 11743 11744 void TV::SetAutoCommercialSkip(const PlayerContext *ctx, 11745 CommSkipMode skipMode) 11746 { 11747 QString desc = QString::null; 11748 11749 ctx->LockDeletePlayer(__FILE__, __LINE__); 11750 if (ctx->player) 11751 { 11752 ctx->player->SetAutoCommercialSkip(skipMode); 11753 desc = toString(ctx->player->GetAutoCommercialSkip()); 11754 } 11755 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 11756 11757 if (!desc.isEmpty()) 11758 UpdateOSDSeekMessage(ctx, desc, kOSDTimeout_Med); 11759 } 11760 11761 void TV::SetManualZoom(const PlayerContext *ctx, bool zoomON, QString desc) 11762 { 11763 if (ctx->GetPIPState() != kPIPOff) 11764 return; 11765 11766 zoomMode = zoomON; 11767 if (zoomON) 11768 ClearOSD(ctx); 11769 11770 if (!desc.isEmpty()) 11771 UpdateOSDSeekMessage(ctx, desc, kOSDTimeout_Med); 11772 } 11773 11774 bool TV::HandleJumpToProgramAction( 11775 PlayerContext *ctx, const QStringList &actions) 11776 { 11777 const PlayerContext *mctx = GetPlayer(ctx, 0); 11778 TVState s = ctx->GetState(); 11779 if (has_action(ACTION_JUMPPREV, actions) || 11780 (has_action("PREVCHAN", actions) && !StateIsLiveTV(s))) 11781 { 11782 if (mctx == ctx) 11783 { 11784 PrepareToExitPlayer(ctx, __LINE__); 11785 jumpToProgram = true; 11786 SetExitPlayer(true, true); 11787 } 11788 else 11789 { 11790 // TODO 11791 } 11792 return true; 11793 } 11794 11795 QStringList::const_iterator it = actions.begin(); 11796 for (; it != actions.end(); ++it) 11797 { 11798 if ((*it).left(8) != "JUMPPROG") 11799 continue; 11800 11801 const QString &action = *it; 11802 11803 bool ok; 11804 QString progKey = action.section(" ",1,-2); 11805 uint progIndex = action.section(" ",-1,-1).toUInt(&ok); 11806 ProgramInfo *p = NULL; 11807 11808 if (ok) 11809 { 11810 QMutexLocker locker(&progListsLock); 11811 QMap<QString,ProgramList>::const_iterator it = 11812 progLists.find(progKey); 11813 if (it != progLists.end()) 11814 { 11815 const ProgramInfo *tmp = (*it)[progIndex]; 11816 if (tmp) 11817 p = new ProgramInfo(*tmp); 11818 } 11819 } 11820 11821 if (!p) 11822 { 11823 LOG(VB_GENERAL, LOG_ERR, LOC + 11824 QString("Failed to locate jump to program '%1' @ %2") 11825 .arg(progKey).arg(action.section(" ",-1,-1))); 11826 return true; 11827 } 11828 11829 PIPState state = kPIPOff; 11830 { 11831 QMutexLocker locker(&timerIdLock); 11832 state = jumpToProgramPIPState; 11833 } 11834 11835 if (kPIPOff == state) 11836 { 11837 if (mctx == ctx) 11838 { 11839 PrepToSwitchToRecordedProgram(ctx, *p); 11840 } 11841 else 11842 { 11843 // TODO 11844 } 11845 } 11846 else 11847 { 11848 QString type = (kPIPonTV == jumpToProgramPIPState) ? "PIP" : "PBP"; 11849 LOG(VB_GENERAL, LOG_INFO, LOC + 11850 QString("Creating %1 with program: %2") 11851 .arg(type).arg(p->toString(ProgramInfo::kTitleSubtitle))); 11852 11853 if (jumpToProgramPIPState == kPIPonTV) 11854 CreatePIP(ctx, p); 11855 else if (jumpToProgramPIPState == kPBPLeft) 11856 CreatePBP(ctx, p); 11857 } 11858 11859 delete p; 11860 11861 return true; 11862 } 11863 11864 bool wants_jump = has_action(ACTION_JUMPREC, actions); 11865 bool wants_pip = !wants_jump && has_action("JUMPRECPIP", actions); 11866 bool wants_pbp = !wants_jump && !wants_pip && 11867 has_action("JUMPRECPBP", actions); 11868 11869 if (!wants_jump && !wants_pip && !wants_pbp) 11870 return false; 11871 11872 { 11873 QMutexLocker locker(&timerIdLock); 11874 jumpToProgramPIPState = wants_pip ? kPIPonTV : 11875 (wants_pbp ? kPBPLeft : kPIPOff); 11876 } 11877 11878 if ((wants_pbp || wants_pip || db_jump_prefer_osd) && 11879 (StateIsPlaying(s) || StateIsLiveTV(s))) 11880 { 11881 QMutexLocker locker(&timerIdLock); 11882 if (jumpMenuTimerId) 11883 KillTimer(jumpMenuTimerId); 11884 jumpMenuTimerId = StartTimer(1, __LINE__); 11885 } 11886 else if (RunPlaybackBoxPtr) 11887 EditSchedule(ctx, kPlaybackBox); 11888 else 11889 LOG(VB_GENERAL, LOG_ERR, "Failed to open jump to program GUI"); 11890 11891 return true; 11892 } 11893 11894 #define MINUTE 60*1000 11895 11896 void TV::ToggleSleepTimer(const PlayerContext *ctx, const QString &time) 11897 { 11898 int mins = 0; 11899 11900 if (time == ACTION_TOGGLESLEEP + "ON") 11901 { 11902 if (sleepTimerId) 11903 { 11904 KillTimer(sleepTimerId); 11905 sleepTimerId = 0; 11906 } 11907 else 11908 { 11909 mins = 60; 11910 sleepTimerTimeout = mins * MINUTE; 11911 sleepTimerId = StartTimer(sleepTimerTimeout, __LINE__); 11912 } 11913 } 11914 else 11915 { 11916 if (sleepTimerId) 11917 { 11918 KillTimer(sleepTimerId); 11919 sleepTimerId = 0; 11920 } 11921 11922 if (time.length() > 11) 11923 { 11924 bool intRead = false; 11925 mins = time.right(time.length() - 11).toInt(&intRead); 11926 11927 if (intRead) 11928 { 11929 // catch 120 -> 240 mins 11930 if (mins < 30) 11931 { 11932 mins *= 10; 11933 } 11934 } 11935 else 11936 { 11937 mins = 0; 11938 LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid time " + time); 11939 } 11940 } 11941 else 11942 { 11943 LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid time string " + time); 11944 } 11945 11946 if (mins) 11947 { 11948 sleepTimerTimeout = mins * MINUTE; 11949 sleepTimerId = StartTimer(sleepTimerTimeout, __LINE__); 11950 } 11951 } 11952 11953 QString out; 11954 if (mins != 0) 11955 out = tr("Sleep") + " " + QString::number(mins); 11956 else 11957 out = tr("Sleep") + " " + sleep_times[0].dispString; 11958 SetOSDMessage(ctx, out); 11959 } 11960 11961 void TV::ShowNoRecorderDialog(const PlayerContext *ctx, NoRecorderMsg msgType) 11962 { 11963 QString errorText; 11964 11965 switch (msgType) 11966 { 11967 case kNoRecorders: 11968 errorText = tr("MythTV is already using all available " 11969 "inputs for the channel you selected. " 11970 "If you want to watch an in-progress recording, " 11971 "select one from the playback menu. If you " 11972 "want to watch Live TV, cancel one of the " 11973 "in-progress recordings from the delete " 11974 "menu."); 11975 break; 11976 case kNoCurrRec: 11977 errorText = tr("Error: MythTV is using all inputs, " 11978 "but there are no active recordings?"); 11979 break; 11980 case kNoTuners: 11981 errorText = tr("MythTV has no capture cards defined. " 11982 "Please run the mythtv-setup program."); 11983 break; 11984 } 11985 11986 OSD *osd = GetOSDLock(ctx); 11987 if (osd) 11988 { 11989 osd->DialogShow(OSD_DLG_INFO, errorText); 11990 osd->DialogAddButton(tr("OK"), "DIALOG_INFO_X_X"); 11991 } 11992 else 11993 { 11994 ShowOkPopup(errorText); 11995 } 11996 ReturnOSDLock(ctx, osd); 11997 } 11998 12003 void TV::PauseLiveTV(PlayerContext *ctx) 12004 { 12005 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("PauseLiveTV() player ctx %1") 12006 .arg(find_player_index(ctx))); 12007 12008 lockTimerOn = false; 12009 12010 ctx->LockDeletePlayer(__FILE__, __LINE__); 12011 if (ctx->player && ctx->buffer) 12012 { 12013 ctx->buffer->IgnoreLiveEOF(true); 12014 ctx->buffer->StopReads(); 12015 ctx->player->PauseDecoder(); 12016 ctx->buffer->StartReads(); 12017 } 12018 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 12019 12020 // XXX: Get rid of this? 12021 ctx->recorder->PauseRecorder(); 12022 12023 ctx->lastSignalMsg.clear(); 12024 ctx->lastSignalUIInfo.clear(); 12025 12026 lockTimerOn = false; 12027 12028 QString input = ctx->recorder->GetInput(); 12029 uint timeout = ctx->recorder->GetSignalLockTimeout(input); 12030 12031 if (timeout < 0xffffffff && !ctx->IsPIP()) 12032 { 12033 lockTimer.start(); 12034 lockTimerOn = true; 12035 } 12036 12037 SetSpeedChangeTimer(0, __LINE__); 12038 } 12039 12044 void TV::UnpauseLiveTV(PlayerContext *ctx, bool bQuietly /*=false*/) 12045 { 12046 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("UnpauseLiveTV() player ctx %1") 12047 .arg(find_player_index(ctx))); 12048 12049 if (ctx->HasPlayer() && ctx->tvchain) 12050 { 12051 ctx->ReloadTVChain(); 12052 ctx->tvchain->JumpTo(-1, 1); 12053 ctx->LockDeletePlayer(__FILE__, __LINE__); 12054 if (ctx->player) 12055 ctx->player->Play(ctx->ts_normal, true, false); 12056 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 12057 ctx->buffer->IgnoreLiveEOF(false); 12058 12059 SetSpeedChangeTimer(0, __LINE__); 12060 } 12061 12062 ITVRestart(ctx, true); 12063 12064 if (ctx->HasPlayer() && !bQuietly) 12065 { 12066 UpdateOSDProgInfo(ctx, "program_info"); 12067 UpdateLCD(); 12068 ctx->PushPreviousChannel(); 12069 } 12070 } 12071 12075 void TV::ITVRestart(PlayerContext *ctx, bool isLive) 12076 { 12077 int chanid = -1; 12078 int sourceid = -1; 12079 12080 if (ContextIsPaused(ctx, __FILE__, __LINE__)) 12081 return; 12082 12083 ctx->LockPlayingInfo(__FILE__, __LINE__); 12084 if (ctx->playingInfo) 12085 { 12086 chanid = ctx->playingInfo->GetChanID(); 12087 sourceid = ChannelUtil::GetSourceIDForChannel(chanid); 12088 } 12089 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 12090 12091 ctx->LockDeletePlayer(__FILE__, __LINE__); 12092 if (ctx->player) 12093 ctx->player->ITVRestart(chanid, sourceid, isLive); 12094 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 12095 } 12096 12097 void TV::DoJumpFFWD(PlayerContext *ctx) 12098 { 12099 if (GetState(ctx) == kState_WatchingDVD) 12100 DVDJumpForward(ctx); 12101 else if (GetNumChapters(ctx) > 0) 12102 DoJumpChapter(ctx, 9999); 12103 else 12104 DoSeek(ctx, ctx->jumptime * 60, tr("Jump Ahead"), 12105 /*timeIsOffset*/true, 12106 /*honorCutlist*/true); 12107 } 12108 12109 void TV::DoJumpRWND(PlayerContext *ctx) 12110 { 12111 if (GetState(ctx) == kState_WatchingDVD) 12112 DVDJumpBack(ctx); 12113 else if (GetNumChapters(ctx) > 0) 12114 DoJumpChapter(ctx, -1); 12115 else 12116 DoSeek(ctx, -ctx->jumptime * 60, tr("Jump Back"), 12117 /*timeIsOffset*/true, 12118 /*honorCutlist*/true); 12119 } 12120 12121 /* \fn TV::DVDJumpBack(PlayerContext*) 12122 \brief jump to the previous dvd title or chapter 12123 */ 12124 void TV::DVDJumpBack(PlayerContext *ctx) 12125 { 12126 DVDRingBuffer *dvdrb = dynamic_cast<DVDRingBuffer*>(ctx->buffer); 12127 if (!ctx->HasPlayer() || !dvdrb) 12128 return; 12129 12130 if (ctx->buffer->IsInDiscMenuOrStillFrame()) 12131 { 12132 UpdateOSDSeekMessage(ctx, tr("Skip Back Not Allowed"), kOSDTimeout_Med); 12133 } 12134 else if (!dvdrb->StartOfTitle()) 12135 { 12136 DoJumpChapter(ctx, -1); 12137 } 12138 else 12139 { 12140 uint titleLength = dvdrb->GetTotalTimeOfTitle(); 12141 uint chapterLength = dvdrb->GetChapterLength(); 12142 if ((titleLength == chapterLength) && chapterLength > 300) 12143 { 12144 DoSeek(ctx, -ctx->jumptime * 60, tr("Jump Back"), 12145 /*timeIsOffset*/true, 12146 /*honorCutlist*/true); 12147 } 12148 else 12149 { 12150 ctx->LockDeletePlayer(__FILE__, __LINE__); 12151 if (ctx->player) 12152 ctx->player->GoToDVDProgram(0); 12153 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 12154 12155 UpdateOSDSeekMessage(ctx, tr("Previous Title"), kOSDTimeout_Med); 12156 } 12157 } 12158 } 12159 12160 /* \fn TV::DVDJumpForward(PlayerContext*) 12161 * \brief jump to the next dvd title or chapter 12162 */ 12163 void TV::DVDJumpForward(PlayerContext *ctx) 12164 { 12165 DVDRingBuffer *dvdrb = dynamic_cast<DVDRingBuffer*>(ctx->buffer); 12166 if (!ctx->HasPlayer() || !dvdrb) 12167 return; 12168 12169 bool in_still = dvdrb->IsInStillFrame(); 12170 bool in_menu = dvdrb->IsInMenu(); 12171 if (in_still && !dvdrb->NumMenuButtons()) 12172 { 12173 dvdrb->SkipStillFrame(); 12174 UpdateOSDSeekMessage(ctx, tr("Skip Still Frame"), kOSDTimeout_Med); 12175 } 12176 else if (!dvdrb->EndOfTitle() && !in_still && !in_menu) 12177 { 12178 DoJumpChapter(ctx, 9999); 12179 } 12180 else if (!in_still && !in_menu) 12181 { 12182 uint titleLength = dvdrb->GetTotalTimeOfTitle(); 12183 uint chapterLength = dvdrb->GetChapterLength(); 12184 uint currentTime = dvdrb->GetCurrentTime(); 12185 if ((titleLength == chapterLength) && 12186 (currentTime < (chapterLength - (ctx->jumptime * 60))) && 12187 chapterLength > 300) 12188 { 12189 DoSeek(ctx, ctx->jumptime * 60, tr("Jump Ahead"), 12190 /*timeIsOffset*/true, 12191 /*honorCutlist*/true); 12192 } 12193 else 12194 { 12195 ctx->LockDeletePlayer(__FILE__, __LINE__); 12196 if (ctx->player) 12197 ctx->player->GoToDVDProgram(1); 12198 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 12199 12200 UpdateOSDSeekMessage(ctx, tr("Next Title"), kOSDTimeout_Med); 12201 } 12202 } 12203 } 12204 12205 /* \fn TV::IsBookmarkAllowed(const PlayerContext*) const 12206 * \brief Returns true if bookmarks are allowed for the current player. 12207 */ 12208 bool TV::IsBookmarkAllowed(const PlayerContext *ctx) const 12209 { 12210 ctx->LockPlayingInfo(__FILE__, __LINE__); 12211 12212 // Allow bookmark of "Record current LiveTV program" 12213 if (StateIsLiveTV(GetState(ctx)) && ctx->playingInfo && 12214 (ctx->playingInfo->QueryAutoExpire() == kLiveTVAutoExpire)) 12215 { 12216 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 12217 return false; 12218 } 12219 12220 if (StateIsLiveTV(GetState(ctx)) && !ctx->playingInfo) 12221 { 12222 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 12223 return false; 12224 } 12225 12226 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 12227 12228 return ctx->buffer && ctx->buffer->IsBookmarkAllowed(); 12229 } 12230 12231 /* \fn TV::IsDeleteAllowed(const PlayerContext*) const 12232 * \brief Returns true if the delete menu option should be offered. 12233 */ 12234 bool TV::IsDeleteAllowed(const PlayerContext *ctx) const 12235 { 12236 bool allowed = false; 12237 12238 if (!StateIsLiveTV(GetState(ctx))) 12239 { 12240 ctx->LockPlayingInfo(__FILE__, __LINE__); 12241 ProgramInfo *curProgram = ctx->playingInfo; 12242 allowed = curProgram && curProgram->QueryIsDeleteCandidate(true); 12243 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 12244 } 12245 12246 return allowed; 12247 } 12248 12249 void TV::ShowOSDStopWatchingRecording(PlayerContext *ctx) 12250 { 12251 ClearOSD(ctx); 12252 12253 if ((ctx != GetPlayer(ctx, 0))) 12254 return; 12255 12256 if (!ContextIsPaused(ctx, __FILE__, __LINE__)) 12257 DoTogglePause(ctx, false); 12258 12259 QString message; 12260 QString videotype = QString::null; 12261 QStringList options; 12262 12263 if (StateIsLiveTV(GetState(ctx))) 12264 videotype = tr("Live TV"); 12265 else if (ctx->buffer->IsDVD()) 12266 videotype = tr("this DVD"); 12267 12268 ctx->LockPlayingInfo(__FILE__, __LINE__); 12269 if (videotype.isEmpty() && ctx->playingInfo->IsVideo()) 12270 videotype = tr("this Video"); 12271 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 12272 12273 if (videotype.isEmpty()) 12274 videotype = tr("this recording"); 12275 12276 OSD *osd = GetOSDLock(ctx); 12277 if (osd) 12278 { 12279 osd->DialogShow(OSD_DLG_VIDEOEXIT, 12280 tr("You are exiting %1").arg(videotype)); 12281 12282 if (IsBookmarkAllowed(ctx)) 12283 { 12284 osd->DialogAddButton(tr("Save this position and go to the menu"), 12285 "DIALOG_VIDEOEXIT_SAVEPOSITIONANDEXIT_0"); 12286 osd->DialogAddButton(tr("Do not save, just exit to the menu"), 12287 ACTION_STOP); 12288 } 12289 else 12290 osd->DialogAddButton(tr("Exit %1").arg(videotype), 12291 ACTION_STOP); 12292 12293 if (IsDeleteAllowed(ctx)) 12294 osd->DialogAddButton(tr("Delete this recording"), 12295 "DIALOG_VIDEOEXIT_CONFIRMDELETE_0"); 12296 12297 osd->DialogAddButton(tr("Keep watching"), 12298 "DIALOG_VIDEOEXIT_KEEPWATCHING_0"); 12299 osd->DialogBack("", "DIALOG_VIDEOEXIT_KEEPWATCHING_0", true); 12300 } 12301 ReturnOSDLock(ctx, osd); 12302 12303 QMutexLocker locker(&timerIdLock); 12304 if (videoExitDialogTimerId) 12305 KillTimer(videoExitDialogTimerId); 12306 videoExitDialogTimerId = StartTimer(kVideoExitDialogTimeout, __LINE__); 12307 } 12308 12309 void TV::ShowOSDPromptDeleteRecording(PlayerContext *ctx, QString title, 12310 bool force) 12311 { 12312 ctx->LockPlayingInfo(__FILE__, __LINE__); 12313 12314 if (ctx->ff_rew_state || 12315 StateIsLiveTV(ctx->GetState()) || 12316 exitPlayerTimerId) 12317 { 12318 // this should only occur when the cat walks on the keyboard. 12319 LOG(VB_GENERAL, LOG_ERR, "It is unsafe to delete at the moment"); 12320 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 12321 return; 12322 } 12323 12324 if ((ctx != GetPlayer(ctx, 0))) 12325 { 12326 // just to avoid confusion... 12327 LOG(VB_GENERAL, LOG_ERR, "Only the main program may be deleted"); 12328 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 12329 return; 12330 } 12331 12332 bool paused = ContextIsPaused(ctx, __FILE__, __LINE__); 12333 if (!ctx->playingInfo->QueryIsDeleteCandidate(true)) 12334 { 12335 LOG(VB_GENERAL, LOG_ERR, 12336 "This program cannot be deleted at this time."); 12337 ProgramInfo pginfo(*ctx->playingInfo); 12338 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 12339 12340 OSD *osd = GetOSDLock(ctx); 12341 if (osd && !osd->DialogVisible()) 12342 { 12343 QString message = QObject::tr("Cannot delete program ") + 12344 QString("%1 ") 12345 .arg(pginfo.GetTitle()); 12346 12347 if (!pginfo.GetSubtitle().isEmpty()) 12348 message += QString("\"%1\" ").arg(pginfo.GetSubtitle()); 12349 12350 if (!pginfo.IsRecording()) 12351 { 12352 message += QObject::tr("because it is not a recording."); 12353 } 12354 else 12355 { 12356 message += QObject::tr("because it is in use by"); 12357 QStringList byWho; 12358 pginfo.QueryIsInUse(byWho); 12359 for (uint i = 0; i+2 < (uint)byWho.size(); i+=3) 12360 { 12361 if (byWho[i+1] == gCoreContext->GetHostName() && 12362 byWho[i+0].contains(kPlayerInUseID)) 12363 continue; 12364 if (byWho[i+0].contains(kRecorderInUseID)) 12365 continue; 12366 message += " " + byWho[i+2]; 12367 } 12368 } 12369 osd->DialogShow(OSD_DLG_DELETE, message); 12370 QString action = "DIALOG_DELETE_OK_0"; 12371 osd->DialogAddButton(tr("OK"), action); 12372 osd->DialogBack("", action, true); 12373 } 12374 ReturnOSDLock(ctx, osd); 12375 // If the delete prompt is to be displayed at the end of a 12376 // recording that ends in a final cut region, it will get into 12377 // a loop of popping up the OK button while the cut region 12378 // plays. Avoid this. 12379 if (ctx->player->IsNearEnd() && !paused) 12380 SetExitPlayer(true, true); 12381 12382 return; 12383 } 12384 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 12385 12386 ClearOSD(ctx); 12387 12388 if (!paused) 12389 DoTogglePause(ctx, false); 12390 12391 InfoMap infoMap; 12392 ctx->GetPlayingInfoMap(infoMap); 12393 QString message = QString("%1\n%2\n%3") 12394 .arg(title).arg(infoMap["title"]).arg(infoMap["timedate"]); 12395 12396 OSD *osd = GetOSDLock(ctx); 12397 if (osd && (!osd->DialogVisible() || force)) 12398 { 12399 osd->DialogShow(OSD_DLG_VIDEOEXIT, message); 12400 if (title == "End Of Recording") 12401 { 12402 osd->DialogAddButton(tr("Delete it, but allow it to re-record"), 12403 "DIALOG_VIDEOEXIT_DELETEANDRERECORD_0"); 12404 osd->DialogAddButton(tr("Delete it"), 12405 "DIALOG_VIDEOEXIT_JUSTDELETE_0"); 12406 osd->DialogAddButton(tr("Save it so I can watch it again"), 12407 ACTION_STOP, false, true); 12408 } 12409 else 12410 { 12411 osd->DialogAddButton(tr("Yes, and allow re-record"), 12412 "DIALOG_VIDEOEXIT_DELETEANDRERECORD_0"); 12413 osd->DialogAddButton(tr("Yes, delete it"), 12414 "DIALOG_VIDEOEXIT_JUSTDELETE_0"); 12415 osd->DialogAddButton(tr("No, keep it"), 12416 ACTION_STOP, false, true); 12417 if (!paused) 12418 osd->DialogBack("", "DIALOG_PLAY_0_0", true); 12419 } 12420 12421 QMutexLocker locker(&timerIdLock); 12422 if (videoExitDialogTimerId) 12423 KillTimer(videoExitDialogTimerId); 12424 videoExitDialogTimerId = StartTimer(kVideoExitDialogTimeout, __LINE__); 12425 } 12426 ReturnOSDLock(ctx, osd); 12427 } 12428 12429 bool TV::HandleOSDVideoExit(PlayerContext *ctx, QString action) 12430 { 12431 if (!DialogIsVisible(ctx, OSD_DLG_VIDEOEXIT)) 12432 return false; 12433 12434 bool hide = true; 12435 bool delete_ok = IsDeleteAllowed(ctx); 12436 bool bookmark_ok = IsBookmarkAllowed(ctx); 12437 12438 ctx->LockDeletePlayer(__FILE__, __LINE__); 12439 bool near_end = ctx->player && ctx->player->IsNearEnd(); 12440 ctx->UnlockDeletePlayer(__FILE__, __LINE__); 12441 12442 if (action == "DELETEANDRERECORD" && delete_ok) 12443 { 12444 allowRerecord = true; 12445 requestDelete = true; 12446 SetExitPlayer(true, true); 12447 } 12448 else if (action == "JUSTDELETE" && delete_ok) 12449 { 12450 requestDelete = true; 12451 SetExitPlayer(true, true); 12452 } 12453 else if (action == "CONFIRMDELETE") 12454 { 12455 hide = false; 12456 ShowOSDPromptDeleteRecording(ctx, tr("Are you sure you want to delete:"), 12457 true); 12458 } 12459 else if (action == "SAVEPOSITIONANDEXIT" && bookmark_ok) 12460 { 12461 PrepareToExitPlayer(ctx, __LINE__, kBookmarkAlways); 12462 SetExitPlayer(true, true); 12463 } 12464 else if (action == "KEEPWATCHING" && !near_end) 12465 { 12466 DoTogglePause(ctx, true); 12467 } 12468 12469 return hide; 12470 } 12471 12472 void TV::SetLastProgram(const ProgramInfo *rcinfo) 12473 { 12474 QMutexLocker locker(&lastProgramLock); 12475 12476 if (lastProgram) 12477 delete lastProgram; 12478 12479 if (rcinfo) 12480 lastProgram = new ProgramInfo(*rcinfo); 12481 else 12482 lastProgram = NULL; 12483 } 12484 12485 ProgramInfo *TV::GetLastProgram(void) const 12486 { 12487 QMutexLocker locker(&lastProgramLock); 12488 if (lastProgram) 12489 return new ProgramInfo(*lastProgram); 12490 return NULL; 12491 } 12492 12493 QString TV::GetRecordingGroup(int player_idx) const 12494 { 12495 QString ret = QString::null; 12496 12497 const PlayerContext *ctx = GetPlayerReadLock(player_idx, __FILE__, __LINE__); 12498 if (ctx) 12499 { 12500 if (StateIsPlaying(GetState(ctx))) 12501 { 12502 ctx->LockPlayingInfo(__FILE__, __LINE__); 12503 if (ctx->playingInfo) 12504 ret = ctx->playingInfo->GetRecordingGroup(); 12505 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 12506 } 12507 } 12508 ReturnPlayerLock(ctx); 12509 return ret; 12510 } 12511 12512 bool TV::IsSameProgram(int player_idx, const ProgramInfo *rcinfo) const 12513 { 12514 if (!rcinfo) 12515 return false; 12516 12517 bool ret = false; 12518 const PlayerContext *ctx = GetPlayerReadLock(player_idx, __FILE__, __LINE__); 12519 if (ctx) 12520 ret = ctx->IsSameProgram(*rcinfo); 12521 ReturnPlayerLock(ctx); 12522 12523 return ret; 12524 } 12525 12526 void TV::RestoreScreenSaver(const PlayerContext *ctx) 12527 { 12528 if (ctx == GetPlayer(ctx, 0)) 12529 GetMythUI()->RestoreScreensaver(); 12530 } 12531 12532 bool TV::ContextIsPaused(PlayerContext *ctx, const char *file, int location) 12533 { 12534 if (!ctx) 12535 return false; 12536 bool paused = false; 12537 ctx->LockDeletePlayer(file, location); 12538 if (ctx->player) 12539 paused = ctx->player->IsPaused(); 12540 ctx->UnlockDeletePlayer(file, location); 12541 return paused; 12542 } 12543 12544 OSD *TV::GetOSDL(const char *file, int location) 12545 { 12546 PlayerContext *actx = GetPlayerReadLock(-1, file, location); 12547 12548 OSD *osd = GetOSDL(actx, file, location); 12549 if (!osd) 12550 ReturnPlayerLock(actx); 12551 12552 return osd; 12553 } 12554 12555 OSD *TV::GetOSDL(const PlayerContext *ctx, const char *file, int location) 12556 { 12557 if (!ctx) 12558 return NULL; 12559 12560 const PlayerContext *mctx = GetPlayer(ctx, 0); 12561 12562 mctx->LockDeletePlayer(file, location); 12563 if (mctx->player && ctx->IsPIP()) 12564 { 12565 mctx->LockOSD(); 12566 OSD *osd = mctx->player->GetOSD(); 12567 if (!osd) 12568 { 12569 mctx->UnlockOSD(); 12570 mctx->UnlockDeletePlayer(file, location); 12571 } 12572 else 12573 osd_lctx[osd] = mctx; 12574 return osd; 12575 } 12576 mctx->UnlockDeletePlayer(file, location); 12577 12578 ctx->LockDeletePlayer(file, location); 12579 if (ctx->player && !ctx->IsPIP()) 12580 { 12581 ctx->LockOSD(); 12582 OSD *osd = ctx->player->GetOSD(); 12583 if (!osd) 12584 { 12585 ctx->UnlockOSD(); 12586 ctx->UnlockDeletePlayer(file, location); 12587 } 12588 else 12589 osd_lctx[osd] = ctx; 12590 return osd; 12591 } 12592 ctx->UnlockDeletePlayer(file, location); 12593 12594 return NULL; 12595 } 12596 12597 void TV::ReturnOSDLock(const PlayerContext *ctx, OSD *&osd) 12598 { 12599 if (!ctx || !osd) 12600 return; 12601 12602 osd_lctx[osd]->UnlockOSD(); 12603 osd_lctx[osd]->UnlockDeletePlayer(__FILE__, __LINE__); 12604 12605 osd = NULL; 12606 } 12607 12608 PlayerContext *TV::GetPlayerWriteLock(int which, const char *file, int location) 12609 { 12610 playerLock.lockForWrite(); 12611 12612 if ((which >= (int)player.size())) 12613 { 12614 LOG(VB_GENERAL, LOG_WARNING, LOC + 12615 QString("GetPlayerWriteLock(%1,%2,%3) returning NULL size(%4)") 12616 .arg(which).arg(file).arg(location).arg(player.size())); 12617 return NULL; 12618 } 12619 12620 return (which < 0) ? player[playerActive] : player[which]; 12621 } 12622 12623 PlayerContext *TV::GetPlayerReadLock(int which, const char *file, int location) 12624 { 12625 playerLock.lockForRead(); 12626 12627 if ((which >= (int)player.size())) 12628 { 12629 LOG(VB_GENERAL, LOG_WARNING, LOC + 12630 QString("GetPlayerReadLock(%1,%2,%3) returning NULL size(%4)") 12631 .arg(which).arg(file).arg(location).arg(player.size())); 12632 return NULL; 12633 } 12634 12635 return (which < 0) ? player[playerActive] : player[which]; 12636 } 12637 12638 const PlayerContext *TV::GetPlayerReadLock( 12639 int which, const char *file, int location) const 12640 { 12641 playerLock.lockForRead(); 12642 12643 if ((which >= (int)player.size())) 12644 { 12645 LOG(VB_GENERAL, LOG_WARNING, LOC + 12646 QString("GetPlayerReadLock(%1,%2,%3) returning NULL size(%4)") 12647 .arg(which).arg(file).arg(location).arg(player.size())); 12648 return NULL; 12649 } 12650 12651 return (which < 0) ? player[playerActive] : player[which]; 12652 } 12653 12654 PlayerContext *TV::GetPlayerHaveLock( 12655 PlayerContext *locked_context, 12656 int which, const char *file, int location) 12657 { 12658 if (!locked_context || (which >= (int)player.size())) 12659 { 12660 LOG(VB_GENERAL, LOG_WARNING, LOC + 12661 QString("GetPlayerHaveLock(0x%1,%2,%3,%4) returning NULL size(%5)") 12662 .arg((uint64_t)locked_context, 0, 16) 12663 .arg(which).arg(file).arg(location).arg(player.size())); 12664 return NULL; 12665 } 12666 12667 return (which < 0) ? player[playerActive] : player[which]; 12668 } 12669 12670 const PlayerContext *TV::GetPlayerHaveLock( 12671 const PlayerContext *locked_context, 12672 int which, const char *file, int location) const 12673 { 12674 if (!locked_context || (which >= (int)player.size())) 12675 { 12676 LOG(VB_GENERAL, LOG_WARNING, LOC + 12677 QString("GetPlayerHaveLock(0x%1,%2,%3,%4) returning NULL size(%5)") 12678 .arg((uint64_t)locked_context, 0, 16) 12679 .arg(which).arg(file).arg(location).arg(player.size())); 12680 return NULL; 12681 } 12682 12683 return (which < 0) ? player[playerActive] : player[which]; 12684 } 12685 12686 void TV::ReturnPlayerLock(PlayerContext *&ctx) 12687 { 12688 playerLock.unlock(); 12689 ctx = NULL; 12690 } 12691 12692 void TV::ReturnPlayerLock(const PlayerContext *&ctx) const 12693 { 12694 playerLock.unlock(); 12695 ctx = NULL; 12696 } 12697 12698 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1.7.6.1