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
00301
00302 sendPlaybackStart();
00303 GetMythMainWindow()->PauseIdleTimer(true);
00304
00305
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
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
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");
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
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
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
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
00850 REG_KEY("TV Playback", ACTION_TOGGLEVISUALISATION,
00851 QT_TRANSLATE_NOOP("MythControls", "Toggle audio visualisation"), "");
00852
00853
00854 REG_KEY("TV Playback", ACTION_TOGGLEOSDDEBUG,
00855 QT_TRANSLATE_NOOP("MythControls", "Toggle OSD playback information"), "");
00856
00857
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
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
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 :
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
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
00947 chanEditMapLock(QMutex::Recursive),
00948 ddMapSourceId(0), ddMapLoader(new DDLoader(this)),
00949
00950 sleep_index(0), sleepTimerId(0), sleepDialogTimerId(0),
00951
00952 idleTimerId(0), idleDialogTimerId(0),
00953
00954 ccInputMode(false),
00955
00956 asInputMode(false),
00957
00958 queuedChanNum(""),
00959 lockTimerOn(false),
00960
00961 browsehelper(NULL),
00962
00963 lastProgram(NULL),
00964 inPlaylist(false), underNetworkControl(false),
00965
00966 jumpToProgramPIPState(kPIPOff),
00967 jumpToProgram(false),
00968
00969 playerActive(-1),
00970 noHardwareDecoders(false),
00971
00972 switchToRec(NULL),
00973
00974 lcdTitle(""), lcdSubtitle(""), lcdCallsign(""),
00975
00976 myWindow(NULL), weDisabledGUI(false),
00977 disableDrawUnusedRects(false),
00978 isEmbedded(false), ignoreKeyPresses(false),
00979
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
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
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
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
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
01180 int maxWidth = 1920, maxHeight = 1440;
01181 if (switchMode)
01182 {
01183 DisplayRes *display_res = DisplayRes::GetDisplayRes();
01184 if(display_res)
01185 {
01186
01187
01188
01189 maxWidth = display_res->GetMaxWidth();
01190 maxHeight = display_res->GetMaxHeight();
01191
01192
01193
01194 if (fullscreen)
01195 {
01196 player_bounds.setSize(QSize(maxWidth, maxHeight));
01197
01198
01199 GetMythMainWindow()->setGeometry(player_bounds);
01200 GetMythMainWindow()->ResizePainterWindow(player_bounds.size());
01201 }
01202 }
01203 }
01204
01205
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
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
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
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
01661 testrec = switchToRec;
01662 switchToRec = NULL;
01663 }
01664 else
01665 {
01666
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
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
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
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
01792 bool busy_input_grps_loaded = false;
01793 vector<uint> busy_input_grps;
01794 TunedInputInfo busy_input;
01795 RemoteIsBusy(cardid, busy_input);
01796
01797
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
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
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
01857
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
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
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
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
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;
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
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
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 {
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
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
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
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
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
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
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
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
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
03464 if ((e->type() == QEvent::Resize))
03465 return (GetMythMainWindow()!= o) ? false : event(e);
03466
03467
03468
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
03741
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
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
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
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
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
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);
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 false,
04198 true);
04199 }
04200 else if (has_action(ACTION_JUMPSTART,actions))
04201 {
04202 DoSeek(ctx, 0, tr("Jump to Beginning"),
04203 false,
04204 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
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 {
04320 PxPTeardownView(ctx);
04321 return handled;
04322 }
04323 else
04324 {
04325
04326
04327
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);
04438 else if (has_action(ACTION_TOGGLESUBTITLEZOOM, actions))
04439 ChangeSubtitleZoom(ctx, 0);
04440 else if (has_action(ACTION_TOGGLEVISUALISATION, actions))
04441 EnableVisualisation(ctx, false, true );
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;
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 false,
04858 true);
04859 else if (tokens[2] == "FORWARD")
04860 DoSeek(ctx, ctx->fftime, tr("Skip Ahead"),
04861 true,
04862 true);
04863 else if (tokens[2] == "BACKWARD")
04864 DoSeek(ctx, -ctx->rewtime, tr("Skip Back"),
04865 true,
04866 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
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
05099 swap(player[0],player[1]);
05100 player[0]->SetPIPState(kPIPOff);
05101
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
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);
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
05393
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 {
05662 LOG(VB_GENERAL, LOG_ERR, loc +
05663 "Failed to restart new pip context (was main context)");
05664 ForceNextStateNone(pipctx);
05665 }
05666 }
05667
05668
05669
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
05739
05740 mctx_mute = kMuteOff;
05741
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 true,
05946 !(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 true,
05959 !(flags & kIgnoreCutlist));
05960 }
05961 else
05962 {
05963 if (smartForward & doSmartForward)
05964 DoSeek(actx, actx->rewtime, tr("Skip Ahead"),
05965 true,
05966 !(flags & kIgnoreCutlist));
05967 else
05968 DoSeek(actx, actx->fftime, tr("Skip Ahead"),
05969 true,
05970 !(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 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 true, honorCutlist);
06038 else if (whence == ARBSEEK_REWIND)
06039 DoSeek(ctx, -time, tr("Jump Back"),
06040 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 (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
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
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
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
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
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
06566
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
06584 if (inputid)
06585 inputname = CardUtil::GetInputName(inputid);
06586
06587
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
06613 if (!testrec)
06614 testrec = RemoteRequestNextFreeRecorder(ctx->GetCardID());
06615
06616 if (testrec && testrec->IsValidRecorder())
06617 {
06618
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
06632 ctx->buffer->IgnoreLiveEOF(true);
06633 ctx->buffer->StopReads();
06634 ctx->player->PauseDecoder();
06635
06636
06637 ctx->buffer->Pause();
06638 ctx->buffer->WaitForPause();
06639 ctx->StopPlaying();
06640 ctx->recorder->StopLiveTV();
06641 ctx->SetPlayer(NULL);
06642
06643
06644 ctx->lastSignalUIInfo.clear();
06645 lockTimerOn = false;
06646
06647 ctx->SetRecorder(testrec);
06648 ctx->recorder->Setup();
06649
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
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
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
06783
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
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
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
06867
06868
06869 if (StateIsLiveTV(GetState(ctx)) && !ccInputMode && !asInputMode &&
06870 browsehelper->IsBrowsing())
06871 {
06872 commitSmart = ProcessSmartChannel(ctx, inputStr);
06873 }
06874
06875
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
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
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
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
06944 QMutexLocker locker(&timerIdLock);
06945 queuedChanNum = "";
06946 }
06947 else if (!needed_spacer.isEmpty())
06948 {
06949
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
06991 DoArbSeek(ctx, ARBSEEK_FORWARD, 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
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
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
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
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;
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
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
07235 delete testrec;
07236
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
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
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
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
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
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
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);
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
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
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
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
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
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
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
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
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
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
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
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
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
08251 disableDrawUnusedRects = pause_active;
08252
08253
08254
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
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
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
08506 if (++sleep_index == sleep_times.size())
08507 sleep_index = 0;
08508
08509
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
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, 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
08846 if (tokens.size() >= 4)
08847 {
08848 cardnum = tokens[1].toUInt();
08849 seconds = tokens[2].toInt();
08850
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
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);
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
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
09758 chanEditMap.clear();
09759 ctx->recorder->GetChannelInfo(chanEditMap);
09760
09761
09762 uint sourceid = chanEditMap["sourceid"].toUInt();
09763
09764
09765 ChannelEditAutoFill(ctx, chanEditMap);
09766
09767
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
09838 ChannelEditXDSFill(ctx, infoMap);
09839
09840
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
09853 for (uint i = 0; i < 4; i++)
09854 chg[keys[i]] = infoMap[keys[i]] != chanEditMap[keys[i]];
09855
09856
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
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)
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
09917
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
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
09944
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
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
10036 LoadDDMap(sourceid);
10037
10038
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 ;
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;
10223 }
10224
10225 StopFFRew(actx);
10226
10227 if (ContextIsPaused(actx, __FILE__, __LINE__))
10228 DoTogglePause(actx, true);
10229
10230 ChangeTimeStretch(actx, 0, !floatRead);
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 );
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
10299
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
10320
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
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
10345
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);
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())
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
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
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
10643
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())
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
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
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)
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
11208
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
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
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
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
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
11597 if (level < 0 || level > 1)
11598 {
11599 level = 0;
11600
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
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
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
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
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 )
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 true,
12106 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 true,
12118 true);
12119 }
12120
12121
12122
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 true,
12146 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
12161
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 true,
12191 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
12206
12207
12208 bool TV::IsBookmarkAllowed(const PlayerContext *ctx) const
12209 {
12210 ctx->LockPlayingInfo(__FILE__, __LINE__);
12211
12212
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
12232
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
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
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
12376
12377
12378
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