MythTV  0.27pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
tv_play.cpp
Go to the documentation of this file.
1 #include <cstdlib>
2 #include <cstdarg>
3 #include <cstring>
4 #include <cmath>
5 #include <unistd.h>
6 #include <stdint.h>
7 
8 #include <algorithm>
9 using namespace std;
10 
11 #include <QCoreApplication>
12 #include <QKeyEvent>
13 #include <QRunnable>
14 #include <QRegExp>
15 #include <QTimer>
16 #include <QEvent>
17 #include <QFile>
18 #include <QDir>
19 
20 #include "signalhandling.h"
21 #include "mythdb.h"
22 #include "tv_play.h"
23 #include "tv_rec.h"
24 #include "mythcorecontext.h"
25 #include "remoteencoder.h"
26 #include "remoteutil.h"
27 #include "tvremoteutil.h"
28 #include "mythplayer.h"
29 #include "subtitlescreen.h"
30 #include "DetectLetterbox.h"
31 #include "programinfo.h"
32 #include "vsync.h"
33 #include "lcddevice.h"
34 #include "jobqueue.h"
35 #include "audiooutput.h"
36 #include "DisplayRes.h"
37 #include "signalmonitorvalue.h"
38 #include "scheduledrecording.h"
39 #include "recordingrule.h"
40 #include "mythmiscutil.h"
41 #include "previewgenerator.h"
42 #include "mythconfig.h"
43 #include "livetvchain.h"
44 #include "playgroup.h"
45 #include "datadirect.h"
46 #include "sourceutil.h"
47 #include "cardutil.h"
48 #include "channelutil.h"
49 #include "compat.h"
50 #include "mythuihelper.h"
51 #include "mythdialogbox.h"
52 #include "mythmainwindow.h"
53 #include "mythscreenstack.h"
54 #include "mythscreentype.h"
55 #include "tv_play_win.h"
56 #include "recordinginfo.h"
57 #include "mythsystemevent.h"
58 #include "videometadatautil.h"
59 #include "mythdirs.h"
60 #include "tvbrowsehelper.h"
61 #include "mythlogging.h"
62 #include "mythuistatetracker.h"
63 #include "DVD/dvdringbuffer.h"
64 #include "Bluray/bdringbuffer.h"
65 
66 #if ! HAVE_ROUND
67 #define round(x) ((int) ((x) + 0.5))
68 #endif
69 
70 #define DEBUG_CHANNEL_PREFIX 0
71 #define DEBUG_ACTIONS 0
73 #define LOC QString("TV: ")
74 
75 #define GetPlayer(X,Y) GetPlayerHaveLock(X, Y, __FILE__ , __LINE__)
76 #define GetOSDLock(X) GetOSDL(X, __FILE__, __LINE__)
77 
78 #define SetOSDText(CTX, GROUP, FIELD, TEXT, TIMEOUT) { \
79  OSD *osd = GetOSDLock(CTX); \
80  if (osd) \
81  { \
82  QHash<QString,QString> map; \
83  map.insert(FIELD,TEXT); \
84  osd->SetText(GROUP, map, TIMEOUT); \
85  } \
86  ReturnOSDLock(CTX, osd); }
87 
88 #define SetOSDMessage(CTX, MESSAGE) \
89  SetOSDText(CTX, "osd_message", "message_text", MESSAGE, kOSDTimeout_Med)
90 
91 #define HideOSDWindow(CTX, WINDOW) { \
92  OSD *osd = GetOSDLock(CTX); \
93  if (osd) \
94  osd->HideWindow(WINDOW); \
95  ReturnOSDLock(CTX, osd); }
96 
97 const int TV::kInitFFRWSpeed = 0;
98 const uint TV::kInputKeysMax = 6;
99 const uint TV::kNextSource = 1;
100 const uint TV::kPreviousSource = 2;
101 const uint TV::kMaxPIPCount = 4;
102 const uint TV::kMaxPBPCount = 2;
103 
104 
105 const uint TV::kInputModeTimeout = 5000;
106 const uint TV::kLCDTimeout = 1000;
107 const uint TV::kBrowseTimeout = 30000;
108 const uint TV::kKeyRepeatTimeout = 300;
109 const uint TV::kPrevChanTimeout = 750;
110 const uint TV::kSleepTimerDialogTimeout = 45000;
111 const uint TV::kIdleTimerDialogTimeout = 45000;
112 const uint TV::kVideoExitDialogTimeout = 120000;
113 
116 const uint TV::kEmbedCheckFrequency = 250;
119 #ifdef USING_VALGRIND
121 #else
123 #endif
124 
129 QStringList TV::lastProgramStringList = QStringList();
130 
135 
140 
145 
150 
155 
157 class DDLoader : public QRunnable
158 {
159  public:
160  DDLoader(TV *parent) : m_parent(parent), m_sourceid(0)
161  {
162  setAutoDelete(false);
163  }
164 
165  void SetParent(TV *parent) { m_parent = parent; }
166  void SetSourceID(uint sourceid) { m_sourceid = sourceid; }
167 
168  virtual void run(void)
169  {
170  if (m_parent)
171  m_parent->RunLoadDDMap(m_sourceid);
172  else
174 
175  QMutexLocker locker(&m_lock);
176  m_sourceid = 0;
177  m_wait.wakeAll();
178  }
179 
180  void wait(void)
181  {
182  QMutexLocker locker(&m_lock);
183  while (m_sourceid)
184  m_wait.wait(locker.mutex());
185  }
186 
187  private:
190  QMutex m_lock;
191  QWaitCondition m_wait;
192 };
193 
198 {
199  int count = 0;
200 
201  MSqlQuery query(MSqlQuery::InitCon());
202  query.prepare("SELECT COUNT(cardid) FROM capturecard;");
203  if (query.exec() && query.isActive() && query.size() && query.next())
204  count = query.value(0).toInt();
205 
206  LOG(VB_RECORD, LOG_INFO,
207  "ConfiguredTunerCards() = " + QString::number(count));
208 
209  return count;
210 }
211 
212 static void multi_lock(QMutex *mutex0, ...)
213 {
214  vector<QMutex*> mutex;
215  mutex.push_back(mutex0);
216 
217  va_list argp;
218  va_start(argp, mutex0);
219  QMutex *cur = va_arg(argp, QMutex*);
220  while (cur)
221  {
222  mutex.push_back(cur);
223  cur = va_arg(argp, QMutex*);
224  }
225  va_end(argp);
226 
227  for (bool success = false; !success;)
228  {
229  success = true;
230  for (uint i = 0; success && (i < mutex.size()); i++)
231  {
232  if (!(success = mutex[i]->tryLock()))
233  {
234  for (uint j = 0; j < i; j++)
235  mutex[j]->unlock();
236  usleep(25 * 1000);
237  }
238  }
239  }
240 }
241 
242 QMutex* TV::gTVLock = new QMutex();
243 TV* TV::gTV = NULL;
244 
245 bool TV::IsTVRunning(void)
246 {
247  QMutexLocker locker(gTVLock);
248  return gTV;
249 }
250 
251 TV* TV::GetTV(void)
252 {
253  QMutexLocker locker(gTVLock);
254  if (gTV)
255  {
256  LOG(VB_GENERAL, LOG_WARNING, LOC + "Already have a TV object.");
257  return NULL;
258  }
259  gTV = new TV();
260  return gTV;
261 }
262 
263 void TV::ReleaseTV(TV* tv)
264 {
265  QMutexLocker locker(gTVLock);
266  if (!tv || !gTV || (gTV != tv))
267  {
268  LOG(VB_GENERAL, LOG_ERR, LOC + "ReleaseTV - programmer error.");
269  return;
270  }
271 
272  delete gTV;
273  gTV = NULL;
274 }
275 
277 {
278  if (TV::IsTVRunning())
279  {
280  QMutexLocker lock(gTVLock);
281 
282  PlayerContext *ctx = gTV->GetPlayerReadLock(0, __FILE__, __LINE__);
283  PrepareToExitPlayer(ctx, __LINE__);
284  SetExitPlayer(true, true);
285  ReturnPlayerLock(ctx);
286  }
287 }
288 
292 bool TV::StartTV(ProgramInfo *tvrec, uint flags)
293 {
294  TV *tv = GetTV();
295  if (!tv)
296  {
298  return false;
299  }
300 
301  LOG(VB_PLAYBACK, LOG_INFO, LOC + "StartTV() -- begin");
302  bool startInGuide = flags & kStartTVInGuide;
303  bool inPlaylist = flags & kStartTVInPlayList;
304  bool initByNetworkCommand = flags & kStartTVByNetworkCommand;
305  bool quitAll = false;
306  bool showDialogs = true;
307  bool playCompleted = false;
308  ProgramInfo *curProgram = NULL;
309  bool startSysEventSent = false;
310 
311  if (tvrec)
312  {
313  curProgram = new ProgramInfo(*tvrec);
314  curProgram->SetIgnoreBookmark(flags & kStartTVIgnoreBookmark);
315  }
316 
317  // Must be before Init() otherwise we swallow the PLAYBACK_START event
318  // with the event filter
321 
322  // Initialize TV
323  if (!tv->Init())
324  {
325  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed initializing TV");
326  ReleaseTV(tv);
327  sendPlaybackEnd();
329  delete curProgram;
331  return false;
332  }
333 
334  if (!lastProgramStringList.empty())
335  {
336  ProgramInfo pginfo(lastProgramStringList);
337  if (pginfo.HasPathname() || pginfo.GetChanID())
338  tv->SetLastProgram(&pginfo);
339  }
340 
341  if (curProgram)
342  {
343  startSysEventSent = true;
344  SendMythSystemPlayEvent("PLAY_STARTED", curProgram);
345  }
346 
347  // Notify others that we are about to play
349 
350  QString playerError = QString::null;
351  while (!quitAll)
352  {
353  if (curProgram)
354  {
355  LOG(VB_PLAYBACK, LOG_INFO, LOC + "tv->Playback() -- begin");
356  if (!tv->Playback(*curProgram))
357  {
358  quitAll = true;
359  }
360  else if (!startSysEventSent)
361  {
362  startSysEventSent = true;
363  SendMythSystemPlayEvent("PLAY_STARTED", curProgram);
364  }
365 
366  LOG(VB_PLAYBACK, LOG_INFO, LOC + "tv->Playback() -- end");
367  }
368  else if (RemoteGetFreeRecorderCount())
369  {
370  LOG(VB_PLAYBACK, LOG_INFO, LOC + "tv->LiveTV() -- begin");
371  if (!tv->LiveTV(showDialogs))
372  {
373  tv->SetExitPlayer(true, true);
374  quitAll = true;
375  }
376  else if (!startSysEventSent)
377  {
378  startSysEventSent = true;
379  gCoreContext->SendSystemEvent("LIVETV_STARTED");
380  }
381 
382  if (!quitAll && (startInGuide || tv->StartLiveTVInGuide()))
383  tv->DoEditSchedule();
384 
385  LOG(VB_PLAYBACK, LOG_INFO, LOC + "tv->LiveTV() -- end");
386  }
387  else
388  {
389  if (!ConfiguredTunerCards())
390  LOG(VB_GENERAL, LOG_ERR, LOC + "No tuners configured");
391  else
392  LOG(VB_GENERAL, LOG_ERR, LOC + "No tuners free for live tv");
393  quitAll = true;
394  continue;
395  }
396 
397  tv->setInPlayList(inPlaylist);
398  tv->setUnderNetworkControl(initByNetworkCommand);
399 
401 
402  // Process Events
403  LOG(VB_GENERAL, LOG_INFO, LOC + "Entering main playback loop.");
404  tv->PlaybackLoop();
405  LOG(VB_GENERAL, LOG_INFO, LOC + "Exiting main playback loop.");
406 
407  if (tv->getJumpToProgram())
408  {
409  ProgramInfo *nextProgram = tv->GetLastProgram();
410 
411  tv->SetLastProgram(curProgram);
412  if (curProgram)
413  delete curProgram;
414 
415  curProgram = nextProgram;
416 
417  SendMythSystemPlayEvent("PLAY_CHANGED", curProgram);
418  continue;
419  }
420 
421  const PlayerContext *mctx =
422  tv->GetPlayerReadLock(0, __FILE__, __LINE__);
423  quitAll = tv->wantsToQuit || (mctx && mctx->errored);
424  if (mctx)
425  {
426  mctx->LockDeletePlayer(__FILE__, __LINE__);
427  if (mctx->player && mctx->player->IsErrored())
428  playerError = mctx->player->GetError();
429  mctx->UnlockDeletePlayer(__FILE__, __LINE__);
430  }
431  tv->ReturnPlayerLock(mctx);
432  }
433 
434  LOG(VB_PLAYBACK, LOG_INFO, LOC + "StartTV -- process events 2 begin");
435  qApp->processEvents();
436  LOG(VB_PLAYBACK, LOG_INFO, LOC + "StartTV -- process events 2 end");
437 
438  // check if the show has reached the end.
439  if (tvrec && tv->getEndOfRecording())
440  playCompleted = true;
441 
442  bool allowrerecord = tv->getAllowRerecord();
443  bool deleterecording = tv->requestDelete;
444 
446 
447  ReleaseTV(tv);
448 
449  if (curProgram)
450  {
451  SendMythSystemPlayEvent("PLAY_STOPPED", curProgram);
452 
453  if (deleterecording)
454  {
455  QStringList list;
456  list.push_back(QString::number(curProgram->GetChanID()));
457  list.push_back(curProgram->GetRecordingStartTime(MythDate::ISODate));
458  list.push_back("0"); // do not force delete
459  list.push_back(allowrerecord ? "1" : "0");
460  MythEvent me("LOCAL_PBB_DELETE_RECORDINGS", list);
461  gCoreContext->dispatch(me);
462  }
463  else if (curProgram->IsRecording())
464  {
465  lastProgramStringList.clear();
466  curProgram->ToStringList(lastProgramStringList);
467  }
468 
469  delete curProgram;
470  }
471  else
472  gCoreContext->SendSystemEvent("PLAY_STOPPED");
473 
474  if (!playerError.isEmpty())
475  {
476  MythScreenStack *ss = GetMythMainWindow()->GetStack("popup stack");
478  ss, playerError, false);
479  if (!dlg->Create())
480  delete dlg;
481  else
482  ss->AddScreen(dlg);
483  }
484 
485  sendPlaybackEnd();
487 
488  LOG(VB_PLAYBACK, LOG_INFO, LOC + "StartTV -- end");
489 
490  return playCompleted;
491 }
492 
497 void TV::SetFuncPtr(const char *string, void *lptr)
498 {
499  QString name(string);
500  if (name == "playbackbox")
501  RunPlaybackBoxPtr = (EMBEDRETURNVOID)lptr;
502  else if (name == "viewscheduled")
503  RunViewScheduledPtr = (EMBEDRETURNVOID)lptr;
504  else if (name == "programguide")
505  RunProgramGuidePtr = (EMBEDRETURNVOIDEPG)lptr;
506  else if (name == "programfinder")
507  RunProgramFinderPtr = (EMBEDRETURNVOIDFINDER)lptr;
508  else if (name == "scheduleeditor")
509  RunScheduleEditorPtr = (EMBEDRETURNVOIDSCHEDIT)lptr;
510 }
511 
512 void TV::InitKeys(void)
513 {
514  REG_KEY("TV Frontend", ACTION_PLAYBACK, QT_TRANSLATE_NOOP("MythControls",
515  "Play Program"), "P");
516  REG_KEY("TV Frontend", ACTION_STOP, QT_TRANSLATE_NOOP("MythControls",
517  "Stop Program"), "");
518  REG_KEY("TV Frontend", ACTION_TOGGLERECORD, QT_TRANSLATE_NOOP("MythControls",
519  "Toggle recording status of current program"), "R");
520  REG_KEY("TV Frontend", ACTION_DAYLEFT, QT_TRANSLATE_NOOP("MythControls",
521  "Page the program guide back one day"), "Home");
522  REG_KEY("TV Frontend", ACTION_DAYRIGHT, QT_TRANSLATE_NOOP("MythControls",
523  "Page the program guide forward one day"), "End");
524  REG_KEY("TV Frontend", ACTION_PAGELEFT, QT_TRANSLATE_NOOP("MythControls",
525  "Page the program guide left"), ",,<");
526  REG_KEY("TV Frontend", ACTION_PAGERIGHT, QT_TRANSLATE_NOOP("MythControls",
527  "Page the program guide right"), ">,.");
528  REG_KEY("TV Frontend", ACTION_TOGGLEFAV, QT_TRANSLATE_NOOP("MythControls",
529  "Toggle the current channel as a favorite"), "?");
530  REG_KEY("TV Frontend", ACTION_TOGGLEPGORDER, QT_TRANSLATE_NOOP("MythControls",
531  "Reverse the channel order in the program guide"), "");
532  REG_KEY("TV Frontend", ACTION_GUIDE, QT_TRANSLATE_NOOP("MythControls",
533  "Show the Program Guide"), "S");
534  REG_KEY("TV Frontend", ACTION_FINDER, QT_TRANSLATE_NOOP("MythControls",
535  "Show the Program Finder"), "#");
536  REG_KEY("TV Frontend", "NEXTFAV", QT_TRANSLATE_NOOP("MythControls",
537  "Cycle through channel groups and all channels in the "
538  "program guide."), "/");
539  REG_KEY("TV Frontend", "CHANUPDATE", QT_TRANSLATE_NOOP("MythControls",
540  "Switch channels without exiting guide in Live TV mode."), "X");
541  REG_KEY("TV Frontend", ACTION_VOLUMEDOWN, QT_TRANSLATE_NOOP("MythControls",
542  "Volume down"), "[,{,F10,Volume Down");
543  REG_KEY("TV Frontend", ACTION_VOLUMEUP, QT_TRANSLATE_NOOP("MythControls",
544  "Volume up"), "],},F11,Volume Up");
545  REG_KEY("TV Frontend", ACTION_MUTEAUDIO, QT_TRANSLATE_NOOP("MythControls",
546  "Mute"), "|,\\,F9,Volume Mute");
547  REG_KEY("TV Frontend", "CYCLEAUDIOCHAN", QT_TRANSLATE_NOOP("MythControls",
548  "Cycle audio channels"), "");
549  REG_KEY("TV Frontend", "RANKINC", QT_TRANSLATE_NOOP("MythControls",
550  "Increase program or channel rank"), "Right");
551  REG_KEY("TV Frontend", "RANKDEC", QT_TRANSLATE_NOOP("MythControls",
552  "Decrease program or channel rank"), "Left");
553  REG_KEY("TV Frontend", "UPCOMING", QT_TRANSLATE_NOOP("MythControls",
554  "List upcoming episodes"), "O");
555  REG_KEY("TV Frontend", ACTION_VIEWSCHEDULED, QT_TRANSLATE_NOOP("MythControls",
556  "List scheduled upcoming episodes"), "");
557  REG_KEY("TV Frontend", "DETAILS", QT_TRANSLATE_NOOP("MythControls",
558  "Show details"), "U");
559  REG_KEY("TV Frontend", "VIEWCARD", QT_TRANSLATE_NOOP("MythControls",
560  "Switch Capture Card view"), "Y");
561  REG_KEY("TV Frontend", "VIEWINPUT", QT_TRANSLATE_NOOP("MythControls",
562  "Switch Capture Card view"), "C");
563  REG_KEY("TV Frontend", "CUSTOMEDIT", QT_TRANSLATE_NOOP("MythControls",
564  "Edit Custom Record Rule"), "");
565  REG_KEY("TV Frontend", "CHANGERECGROUP", QT_TRANSLATE_NOOP("MythControls",
566  "Change Recording Group"), "");
567  REG_KEY("TV Frontend", "CHANGEGROUPVIEW", QT_TRANSLATE_NOOP("MythControls",
568  "Change Group View"), "");
569 
570  REG_KEY("TV Playback", "BACK", QT_TRANSLATE_NOOP("MythControls",
571  "Exit or return to DVD menu"), "Esc");
572  REG_KEY("TV Playback", ACTION_CLEAROSD, QT_TRANSLATE_NOOP("MythControls",
573  "Clear OSD"), "Backspace");
574  REG_KEY("TV Playback", ACTION_PAUSE, QT_TRANSLATE_NOOP("MythControls",
575  "Pause"), "P");
576  REG_KEY("TV Playback", ACTION_SEEKFFWD, QT_TRANSLATE_NOOP("MythControls",
577  "Fast Forward"), "Right");
578  REG_KEY("TV Playback", ACTION_SEEKRWND, QT_TRANSLATE_NOOP("MythControls",
579  "Rewind"), "Left");
580  REG_KEY("TV Playback", ACTION_SEEKARB, QT_TRANSLATE_NOOP("MythControls",
581  "Arbitrary Seek"), "*");
582  REG_KEY("TV Playback", ACTION_SEEKABSOLUTE, QT_TRANSLATE_NOOP("MythControls",
583  "Seek to a position in seconds"), "");
584  REG_KEY("TV Playback", ACTION_CHANNELUP, QT_TRANSLATE_NOOP("MythControls",
585  "Channel up"), "Up");
586  REG_KEY("TV Playback", ACTION_CHANNELDOWN, QT_TRANSLATE_NOOP("MythControls",
587  "Channel down"), "Down");
588  REG_KEY("TV Playback", "NEXTFAV", QT_TRANSLATE_NOOP("MythControls",
589  "Switch to the next favorite channel"), "/");
590  REG_KEY("TV Playback", "PREVCHAN", QT_TRANSLATE_NOOP("MythControls",
591  "Switch to the previous channel"), "H");
592  REG_KEY("TV Playback", ACTION_JUMPFFWD, QT_TRANSLATE_NOOP("MythControls",
593  "Jump ahead"), "PgDown");
594  REG_KEY("TV Playback", ACTION_JUMPRWND, QT_TRANSLATE_NOOP("MythControls",
595  "Jump back"), "PgUp");
596  REG_KEY("TV Playback", "INFOWITHCUTLIST", QT_TRANSLATE_NOOP("MythControls",
597  "Info utilizing cutlist"), "");
598  REG_KEY("TV Playback", ACTION_JUMPBKMRK, QT_TRANSLATE_NOOP("MythControls",
599  "Jump to bookmark"), "K");
600  REG_KEY("TV Playback", "FFWDSTICKY", QT_TRANSLATE_NOOP("MythControls",
601  "Fast Forward (Sticky) or Forward one second while paused"), ">,.");
602  REG_KEY("TV Playback", "RWNDSTICKY", QT_TRANSLATE_NOOP("MythControls",
603  "Rewind (Sticky) or Rewind one second while paused"), ",,<");
604  REG_KEY("TV Playback", "NEXTSOURCE", QT_TRANSLATE_NOOP("MythControls",
605  "Next Video Source"), "Y");
606  REG_KEY("TV Playback", "PREVSOURCE", QT_TRANSLATE_NOOP("MythControls",
607  "Previous Video Source"), "");
608  REG_KEY("TV Playback", "NEXTINPUT", QT_TRANSLATE_NOOP("MythControls",
609  "Next Input"), "C");
610  REG_KEY("TV Playback", "NEXTCARD", QT_TRANSLATE_NOOP("MythControls",
611  "Next Card"), "");
612  REG_KEY("TV Playback", "SKIPCOMMERCIAL", QT_TRANSLATE_NOOP("MythControls",
613  "Skip Commercial"), "Z,End");
614  REG_KEY("TV Playback", "SKIPCOMMBACK", QT_TRANSLATE_NOOP("MythControls",
615  "Skip Commercial (Reverse)"), "Q,Home");
616  REG_KEY("TV Playback", ACTION_JUMPSTART, QT_TRANSLATE_NOOP("MythControls",
617  "Jump to the start of the recording."), "Ctrl+B");
618  REG_KEY("TV Playback", "TOGGLEBROWSE", QT_TRANSLATE_NOOP("MythControls",
619  "Toggle channel browse mode"), "O");
620  REG_KEY("TV Playback", ACTION_TOGGLERECORD, QT_TRANSLATE_NOOP("MythControls",
621  "Toggle recording status of current program"), "R");
622  REG_KEY("TV Playback", ACTION_TOGGLEFAV, QT_TRANSLATE_NOOP("MythControls",
623  "Toggle the current channel as a favorite"), "?");
624  REG_KEY("TV Playback", ACTION_VOLUMEDOWN, QT_TRANSLATE_NOOP("MythControls",
625  "Volume down"), "[,{,F10,Volume Down");
626  REG_KEY("TV Playback", ACTION_VOLUMEUP, QT_TRANSLATE_NOOP("MythControls",
627  "Volume up"), "],},F11,Volume Up");
628  REG_KEY("TV Playback", ACTION_MUTEAUDIO, QT_TRANSLATE_NOOP("MythControls",
629  "Mute"), "|,\\,F9,Volume Mute");
630  REG_KEY("TV Playback", ACTION_SETVOLUME, QT_TRANSLATE_NOOP("MythControls",
631  "Set the volume"), "");
632  REG_KEY("TV Playback", "CYCLEAUDIOCHAN", QT_TRANSLATE_NOOP("MythControls",
633  "Cycle audio channels"), "");
634  REG_KEY("TV Playback", ACTION_TOGGLEUPMIX, QT_TRANSLATE_NOOP("MythControls",
635  "Toggle audio upmixer"), "Ctrl+U");
636  REG_KEY("TV Playback", "TOGGLEPIPMODE", QT_TRANSLATE_NOOP("MythControls",
637  "Toggle Picture-in-Picture view"), "V");
638  REG_KEY("TV Playback", "TOGGLEPBPMODE", QT_TRANSLATE_NOOP("MythControls",
639  "Toggle Picture-by-Picture view"), "Ctrl+V");
640  REG_KEY("TV Playback", "CREATEPIPVIEW", QT_TRANSLATE_NOOP("MythControls",
641  "Create Picture-in-Picture view"), "");
642  REG_KEY("TV Playback", "CREATEPBPVIEW", QT_TRANSLATE_NOOP("MythControls",
643  "Create Picture-by-Picture view"), "");
644  REG_KEY("TV Playback", "NEXTPIPWINDOW", QT_TRANSLATE_NOOP("MythControls",
645  "Toggle active PIP/PBP window"), "B");
646  REG_KEY("TV Playback", "SWAPPIP", QT_TRANSLATE_NOOP("MythControls",
647  "Swap PBP/PIP Windows"), "N");
648  REG_KEY("TV Playback", "TOGGLEPIPSTATE", QT_TRANSLATE_NOOP("MythControls",
649  "Change PxP view"), "");
650  REG_KEY("TV Playback", "TOGGLEASPECT", QT_TRANSLATE_NOOP("MythControls",
651  "Toggle the video aspect ratio"), "Ctrl+W");
652  REG_KEY("TV Playback", "TOGGLEFILL", QT_TRANSLATE_NOOP("MythControls",
653  "Next Preconfigured Zoom mode"), "W");
654  REG_KEY("TV Playback", ACTION_TOGGLESUBS, QT_TRANSLATE_NOOP("MythControls",
655  "Toggle any captions"), "T");
656  REG_KEY("TV Playback", ACTION_ENABLESUBS, QT_TRANSLATE_NOOP("MythControls",
657  "Enable any captions"), "");
658  REG_KEY("TV Playback", ACTION_DISABLESUBS, QT_TRANSLATE_NOOP("MythControls",
659  "Disable any captions"), "");
660  REG_KEY("TV Playback", "TOGGLETTC", QT_TRANSLATE_NOOP("MythControls",
661  "Toggle Teletext Captions"),"");
662  REG_KEY("TV Playback", "TOGGLESUBTITLE", QT_TRANSLATE_NOOP("MythControls",
663  "Toggle Subtitles"), "");
664  REG_KEY("TV Playback", "TOGGLECC608", QT_TRANSLATE_NOOP("MythControls",
665  "Toggle VBI CC"), "");
666  REG_KEY("TV Playback", "TOGGLECC708", QT_TRANSLATE_NOOP("MythControls",
667  "Toggle ATSC CC"), "");
668  REG_KEY("TV Playback", "TOGGLETTM", QT_TRANSLATE_NOOP("MythControls",
669  "Toggle Teletext Menu"), "");
670  REG_KEY("TV Playback", ACTION_TOGGLEEXTTEXT, QT_TRANSLATE_NOOP("MythControls",
671  "Toggle External Subtitles"), "");
672  REG_KEY("TV Playback", ACTION_ENABLEEXTTEXT, QT_TRANSLATE_NOOP("MythControls",
673  "Enable External Subtitles"), "");
674  REG_KEY("TV Playback", ACTION_DISABLEEXTTEXT, QT_TRANSLATE_NOOP("MythControls",
675  "Disable External Subtitles"), "");
676  REG_KEY("TV Playback", "TOGGLERAWTEXT", QT_TRANSLATE_NOOP("MythControls",
677  "Toggle Text Subtitles"), "");
678 
679  REG_KEY("TV Playback", "SELECTAUDIO_0", QT_TRANSLATE_NOOP("MythControls",
680  "Play audio track 1"), "");
681  REG_KEY("TV Playback", "SELECTAUDIO_1", QT_TRANSLATE_NOOP("MythControls",
682  "Play audio track 2"), "");
683  REG_KEY("TV Playback", "SELECTSUBTITLE_0",QT_TRANSLATE_NOOP("MythControls",
684  "Display subtitle 1"), "");
685  REG_KEY("TV Playback", "SELECTSUBTITLE_1",QT_TRANSLATE_NOOP("MythControls",
686  "Display subtitle 2"), "");
687  REG_KEY("TV Playback", "SELECTRAWTEXT_0",QT_TRANSLATE_NOOP("MythControls",
688  "Display Text Subtitle 1"), "");
689  REG_KEY("TV Playback", "SELECTCC608_0", QT_TRANSLATE_NOOP("MythControls",
690  "Display VBI CC1"), "");
691  REG_KEY("TV Playback", "SELECTCC608_1", QT_TRANSLATE_NOOP("MythControls",
692  "Display VBI CC2"), "");
693  REG_KEY("TV Playback", "SELECTCC608_2", QT_TRANSLATE_NOOP("MythControls",
694  "Display VBI CC3"), "");
695  REG_KEY("TV Playback", "SELECTCC608_3", QT_TRANSLATE_NOOP("MythControls",
696  "Display VBI CC4"), "");
697  REG_KEY("TV Playback", "SELECTCC708_0", QT_TRANSLATE_NOOP("MythControls",
698  "Display ATSC CC1"), "");
699  REG_KEY("TV Playback", "SELECTCC708_1", QT_TRANSLATE_NOOP("MythControls",
700  "Display ATSC CC2"), "");
701  REG_KEY("TV Playback", "SELECTCC708_2", QT_TRANSLATE_NOOP("MythControls",
702  "Display ATSC CC3"), "");
703  REG_KEY("TV Playback", "SELECTCC708_3", QT_TRANSLATE_NOOP("MythControls",
704  "Display ATSC CC4"), "");
705  REG_KEY("TV Playback", ACTION_ENABLEFORCEDSUBS, QT_TRANSLATE_NOOP("MythControls",
706  "Enable Forced Subtitles"), "");
707  REG_KEY("TV Playback", ACTION_DISABLEFORCEDSUBS, QT_TRANSLATE_NOOP("MythControls",
708  "Disable Forced Subtitles"), "");
709 
710  REG_KEY("TV Playback", "NEXTAUDIO", QT_TRANSLATE_NOOP("MythControls",
711  "Next audio track"), "+");
712  REG_KEY("TV Playback", "PREVAUDIO", QT_TRANSLATE_NOOP("MythControls",
713  "Previous audio track"), "-");
714  REG_KEY("TV Playback", "NEXTSUBTITLE", QT_TRANSLATE_NOOP("MythControls",
715  "Next subtitle track"), "");
716  REG_KEY("TV Playback", "PREVSUBTITLE", QT_TRANSLATE_NOOP("MythControls",
717  "Previous subtitle track"), "");
718  REG_KEY("TV Playback", "NEXTRAWTEXT", QT_TRANSLATE_NOOP("MythControls",
719  "Next Text track"), "");
720  REG_KEY("TV Playback", "PREVRAWTEXT", QT_TRANSLATE_NOOP("MythControls",
721  "Previous Text track"), "");
722  REG_KEY("TV Playback", "NEXTCC608", QT_TRANSLATE_NOOP("MythControls",
723  "Next VBI CC track"), "");
724  REG_KEY("TV Playback", "PREVCC608", QT_TRANSLATE_NOOP("MythControls",
725  "Previous VBI CC track"), "");
726  REG_KEY("TV Playback", "NEXTCC708", QT_TRANSLATE_NOOP("MythControls",
727  "Next ATSC CC track"), "");
728  REG_KEY("TV Playback", "PREVCC708", QT_TRANSLATE_NOOP("MythControls",
729  "Previous ATSC CC track"), "");
730  REG_KEY("TV Playback", "NEXTCC", QT_TRANSLATE_NOOP("MythControls",
731  "Next of any captions"), "");
732 
733  REG_KEY("TV Playback", "NEXTSCAN", QT_TRANSLATE_NOOP("MythControls",
734  "Next video scan overidemode"), "");
735  REG_KEY("TV Playback", "QUEUETRANSCODE", QT_TRANSLATE_NOOP("MythControls",
736  "Queue the current recording for transcoding"), "X");
737  REG_KEY("TV Playback", "SPEEDINC", QT_TRANSLATE_NOOP("MythControls",
738  "Increase the playback speed"), "U");
739  REG_KEY("TV Playback", "SPEEDDEC", QT_TRANSLATE_NOOP("MythControls",
740  "Decrease the playback speed"), "J");
741  REG_KEY("TV Playback", "ADJUSTSTRETCH", QT_TRANSLATE_NOOP("MythControls",
742  "Turn on time stretch control"), "A");
743  REG_KEY("TV Playback", "STRETCHINC", QT_TRANSLATE_NOOP("MythControls",
744  "Increase time stretch speed"), "");
745  REG_KEY("TV Playback", "STRETCHDEC", QT_TRANSLATE_NOOP("MythControls",
746  "Decrease time stretch speed"), "");
747  REG_KEY("TV Playback", "TOGGLESTRETCH", QT_TRANSLATE_NOOP("MythControls",
748  "Toggle time stretch speed"), "");
749  REG_KEY("TV Playback", ACTION_TOGGELAUDIOSYNC,
750  QT_TRANSLATE_NOOP("MythControls",
751  "Turn on audio sync adjustment controls"), "");
752  REG_KEY("TV Playback", ACTION_SETAUDIOSYNC,
753  QT_TRANSLATE_NOOP("MythControls",
754  "Set the audio sync adjustment"), "");
755  REG_KEY("TV Playback", "TOGGLEPICCONTROLS",
756  QT_TRANSLATE_NOOP("MythControls", "Playback picture adjustments"),
757  "F");
758  REG_KEY("TV Playback", ACTION_TOGGLENIGHTMODE,
759  QT_TRANSLATE_NOOP("MythControls", "Toggle night mode"), "Ctrl+F");
760  REG_KEY("TV Playback", ACTION_SETBRIGHTNESS,
761  QT_TRANSLATE_NOOP("MythControls", "Set the picture brightness"), "");
762  REG_KEY("TV Playback", ACTION_SETCONTRAST,
763  QT_TRANSLATE_NOOP("MythControls", "Set the picture contrast"), "");
764  REG_KEY("TV Playback", ACTION_SETCOLOUR,
765  QT_TRANSLATE_NOOP("MythControls", "Set the picture color"), "");
766  REG_KEY("TV Playback", ACTION_SETHUE,
767  QT_TRANSLATE_NOOP("MythControls", "Set the picture hue"), "");
768  REG_KEY("TV Playback", ACTION_TOGGLESTUDIOLEVELS,
769  QT_TRANSLATE_NOOP("MythControls", "Playback picture adjustments"),
770  "");
771  REG_KEY("TV Playback", ACTION_TOGGLECHANCONTROLS,
772  QT_TRANSLATE_NOOP("MythControls", "Recording picture adjustments "
773  "for this channel"), "Ctrl+G");
774  REG_KEY("TV Playback", ACTION_TOGGLERECCONTROLS,
775  QT_TRANSLATE_NOOP("MythControls", "Recording picture adjustments "
776  "for this recorder"), "G");
777  REG_KEY("TV Playback", "CYCLECOMMSKIPMODE",
778  QT_TRANSLATE_NOOP("MythControls", "Cycle Commercial Skip mode"),
779  "");
780  REG_KEY("TV Playback", ACTION_GUIDE, QT_TRANSLATE_NOOP("MythControls",
781  "Show the Program Guide"), "S");
782  REG_KEY("TV Playback", ACTION_FINDER, QT_TRANSLATE_NOOP("MythControls",
783  "Show the Program Finder"), "#");
784  REG_KEY("TV Playback", ACTION_TOGGLESLEEP, QT_TRANSLATE_NOOP("MythControls",
785  "Toggle the Sleep Timer"), "F8");
786  REG_KEY("TV Playback", ACTION_PLAY, QT_TRANSLATE_NOOP("MythControls", "Play"),
787  "Ctrl+P");
788  REG_KEY("TV Playback", ACTION_JUMPPREV, QT_TRANSLATE_NOOP("MythControls",
789  "Jump to previously played recording"), "");
790  REG_KEY("TV Playback", ACTION_JUMPREC, QT_TRANSLATE_NOOP("MythControls",
791  "Display menu of recorded programs to jump to"), "");
792  REG_KEY("TV Playback", ACTION_VIEWSCHEDULED, QT_TRANSLATE_NOOP("MythControls",
793  "Display scheduled recording list"), "");
794  REG_KEY("TV Playback", ACTION_SIGNALMON, QT_TRANSLATE_NOOP("MythControls",
795  "Monitor Signal Quality"), "Alt+F7");
796  REG_KEY("TV Playback", ACTION_JUMPTODVDROOTMENU,
797  QT_TRANSLATE_NOOP("MythControls", "Jump to the DVD Root Menu"), "");
798  REG_KEY("TV Playback", ACTION_JUMPTOPOPUPMENU,
799  QT_TRANSLATE_NOOP("MythControls", "Jump to the Popup Menu"), "");
800  REG_KEY("TV Playback", ACTION_JUMPTODVDCHAPTERMENU,
801  QT_TRANSLATE_NOOP("MythControls", "Jump to the DVD Chapter Menu"), "");
802  REG_KEY("TV Playback", ACTION_JUMPTODVDTITLEMENU,
803  QT_TRANSLATE_NOOP("MythControls", "Jump to the DVD Title Menu"), "");
804  REG_KEY("TV Playback", ACTION_EXITSHOWNOPROMPTS,
805  QT_TRANSLATE_NOOP("MythControls", "Exit Show without any prompts"),
806  "");
807  REG_KEY("TV Playback", ACTION_JUMPCHAPTER, QT_TRANSLATE_NOOP("MythControls",
808  "Jump to a chapter"), "");
809  REG_KEY("TV Playback", ACTION_SWITCHTITLE, QT_TRANSLATE_NOOP("MythControls",
810  "Switch title"), "");
811  REG_KEY("TV Playback", ACTION_SWITCHANGLE, QT_TRANSLATE_NOOP("MythControls",
812  "Switch angle"), "");
813  REG_KEY("TV Playback", ACTION_ZOOMUP, QT_TRANSLATE_NOOP("MythControls",
814  "Zoom mode - shift up"), "");
815  REG_KEY("TV Playback", ACTION_ZOOMDOWN, QT_TRANSLATE_NOOP("MythControls",
816  "Zoom mode - shift down"), "");
817  REG_KEY("TV Playback", ACTION_ZOOMLEFT, QT_TRANSLATE_NOOP("MythControls",
818  "Zoom mode - shift left"), "");
819  REG_KEY("TV Playback", ACTION_ZOOMRIGHT, QT_TRANSLATE_NOOP("MythControls",
820  "Zoom mode - shift right"), "");
821  REG_KEY("TV Playback", ACTION_ZOOMASPECTUP,
822  QT_TRANSLATE_NOOP("MythControls",
823  "Zoom mode - increase aspect ratio"), "");
824  REG_KEY("TV Playback", ACTION_ZOOMASPECTDOWN,
825  QT_TRANSLATE_NOOP("MythControls",
826  "Zoom mode - decrease aspect ratio"), "");
827  REG_KEY("TV Playback", ACTION_ZOOMIN, QT_TRANSLATE_NOOP("MythControls",
828  "Zoom mode - zoom in"), "");
829  REG_KEY("TV Playback", ACTION_ZOOMOUT, QT_TRANSLATE_NOOP("MythControls",
830  "Zoom mode - zoom out"), "");
831  REG_KEY("TV Playback", ACTION_ZOOMQUIT, QT_TRANSLATE_NOOP("MythControls",
832  "Zoom mode - quit and abandon changes"), "");
833  REG_KEY("TV Playback", ACTION_ZOOMCOMMIT, QT_TRANSLATE_NOOP("MythControls",
834  "Zoom mode - commit changes"), "");
835 
836  /* Interactive Television keys */
837  REG_KEY("TV Playback", ACTION_MENURED, QT_TRANSLATE_NOOP("MythControls",
838  "Menu Red"), "F2");
839  REG_KEY("TV Playback", ACTION_MENUGREEN, QT_TRANSLATE_NOOP("MythControls",
840  "Menu Green"), "F3");
841  REG_KEY("TV Playback", ACTION_MENUYELLOW, QT_TRANSLATE_NOOP("MythControls",
842  "Menu Yellow"), "F4");
843  REG_KEY("TV Playback", ACTION_MENUBLUE, QT_TRANSLATE_NOOP("MythControls",
844  "Menu Blue"), "F5");
845  REG_KEY("TV Playback", ACTION_TEXTEXIT, QT_TRANSLATE_NOOP("MythControls",
846  "Menu Exit"), "F6");
847  REG_KEY("TV Playback", ACTION_MENUTEXT, QT_TRANSLATE_NOOP("MythControls",
848  "Menu Text"), "F7");
849  REG_KEY("TV Playback", ACTION_MENUEPG, QT_TRANSLATE_NOOP("MythControls",
850  "Menu EPG"), "F12");
851 
852  /* Editing keys */
853  REG_KEY("TV Editing", ACTION_CLEARMAP, QT_TRANSLATE_NOOP("MythControls",
854  "Clear editing cut points"), "C,Q,Home");
855  REG_KEY("TV Editing", ACTION_INVERTMAP, QT_TRANSLATE_NOOP("MythControls",
856  "Invert Begin/End cut points"),"I");
857  REG_KEY("TV Editing", ACTION_SAVEMAP, QT_TRANSLATE_NOOP("MythControls",
858  "Save cuts"),"");
859  REG_KEY("TV Editing", ACTION_LOADCOMMSKIP,QT_TRANSLATE_NOOP("MythControls",
860  "Load cuts from detected commercials"), "Z,End");
861  REG_KEY("TV Editing", ACTION_NEXTCUT, QT_TRANSLATE_NOOP("MythControls",
862  "Jump to the next cut point"), "PgDown");
863  REG_KEY("TV Editing", ACTION_PREVCUT, QT_TRANSLATE_NOOP("MythControls",
864  "Jump to the previous cut point"), "PgUp");
865  REG_KEY("TV Editing", ACTION_BIGJUMPREW, QT_TRANSLATE_NOOP("MythControls",
866  "Jump back 10x the normal amount"), ",,<");
867  REG_KEY("TV Editing", ACTION_BIGJUMPFWD, QT_TRANSLATE_NOOP("MythControls",
868  "Jump forward 10x the normal amount"), ">,.");
869 
870  /* Teletext keys */
871  REG_KEY("Teletext Menu", ACTION_NEXTPAGE, QT_TRANSLATE_NOOP("MythControls",
872  "Next Page"), "Down");
873  REG_KEY("Teletext Menu", ACTION_PREVPAGE, QT_TRANSLATE_NOOP("MythControls",
874  "Previous Page"), "Up");
875  REG_KEY("Teletext Menu", ACTION_NEXTSUBPAGE, QT_TRANSLATE_NOOP("MythControls",
876  "Next Subpage"), "Right");
877  REG_KEY("Teletext Menu", ACTION_PREVSUBPAGE, QT_TRANSLATE_NOOP("MythControls",
878  "Previous Subpage"), "Left");
879  REG_KEY("Teletext Menu", ACTION_TOGGLETT, QT_TRANSLATE_NOOP("MythControls",
880  "Toggle Teletext"), "T");
881  REG_KEY("Teletext Menu", ACTION_MENURED, QT_TRANSLATE_NOOP("MythControls",
882  "Menu Red"), "F2");
883  REG_KEY("Teletext Menu", ACTION_MENUGREEN, QT_TRANSLATE_NOOP("MythControls",
884  "Menu Green"), "F3");
885  REG_KEY("Teletext Menu", ACTION_MENUYELLOW, QT_TRANSLATE_NOOP("MythControls",
886  "Menu Yellow"), "F4");
887  REG_KEY("Teletext Menu", ACTION_MENUBLUE, QT_TRANSLATE_NOOP("MythControls",
888  "Menu Blue"), "F5");
889  REG_KEY("Teletext Menu", ACTION_MENUWHITE, QT_TRANSLATE_NOOP("MythControls",
890  "Menu White"), "F6");
891  REG_KEY("Teletext Menu", ACTION_TOGGLEBACKGROUND,
892  QT_TRANSLATE_NOOP("MythControls", "Toggle Background"), "F7");
893  REG_KEY("Teletext Menu", ACTION_REVEAL, QT_TRANSLATE_NOOP("MythControls",
894  "Reveal hidden Text"), "F8");
895 
896  /* Visualisations */
897  REG_KEY("TV Playback", ACTION_TOGGLEVISUALISATION,
898  QT_TRANSLATE_NOOP("MythControls", "Toggle audio visualisation"), "");
899 
900  /* OSD playback information screen */
901  REG_KEY("TV Playback", ACTION_TOGGLEOSDDEBUG,
902  QT_TRANSLATE_NOOP("MythControls", "Toggle OSD playback information"), "");
903 
904  /* 3D/Frame compatible/Stereoscopic TV */
905  REG_KEY("TV Playback", ACTION_3DNONE,
906  QT_TRANSLATE_NOOP("MythControls", "No 3D"), "");
907  REG_KEY("TV Playback", ACTION_3DSIDEBYSIDE,
908  QT_TRANSLATE_NOOP("MythControls", "3D Side by Side"), "");
909  REG_KEY("TV Playback", ACTION_3DSIDEBYSIDEDISCARD,
910  QT_TRANSLATE_NOOP("MythControls", "Discard 3D Side by Side"), "");
911  REG_KEY("TV Playback", ACTION_3DTOPANDBOTTOM,
912  QT_TRANSLATE_NOOP("MythControls", "3D Top and Bottom"), "");
913  REG_KEY("TV Playback", ACTION_3DTOPANDBOTTOMDISCARD,
914  QT_TRANSLATE_NOOP("MythControls", "Discard 3D Top and Bottom"), "");
915 
916 /*
917  keys already used:
918 
919  Global: I M 0123456789
920  Playback: ABCDEFGH JK NOPQRSTUVWXYZ
921  Frontend: CD OP R U XY 01 3 7 9
922  Editing: C E I Q Z
923  Teletext: T
924 
925  Playback: <>,.?/|[]{}\+-*#^
926  Frontend: <>,.?/
927  Editing: <>,.
928 
929  Global: PgDown, PgUp, Right, Left, Home, End, Up, Down,
930  Playback: PgDown, PgUp, Right, Left, Home, End, Up, Down, Backspace,
931  Frontend: Right, Left, Home, End
932  Editing: PgDown, PgUp, Home, End
933  Teletext: Right, Left, Up, Down,
934 
935  Global: Return, Enter, Space, Esc
936 
937  Global: F1,
938  Playback: F7,F8,F9,F10,F11
939  Teletext F2,F3,F4,F5,F6,F7,F8
940  ITV F2,F3,F4,F5,F6,F7,F12
941 
942  Playback: Ctrl-B,Ctrl-G,Ctrl-Y,Ctrl-U
943 */
944 }
945 
946 void TV::ReloadKeys(void)
947 {
948  MythMainWindow *mainWindow = GetMythMainWindow();
949  mainWindow->ClearKeyContext("TV Frontend");
950  mainWindow->ClearKeyContext("TV Playback");
951  mainWindow->ClearKeyContext("TV Editing");
952  mainWindow->ClearKeyContext("Teletext Menu");
953  InitKeys();
954 }
955 
959 TV::TV(void)
960  : // Configuration variables from database
961  baseFilters(""),
962  db_channel_format("<num> <sign>"),
963  db_idle_timeout(0),
964  db_playback_exit_prompt(0), db_autoexpire_default(0),
965  db_auto_set_watched(false), db_end_of_rec_exit_prompt(false),
966  db_jump_prefer_osd(true), db_use_gui_size_for_tv(false),
967  db_start_in_guide(false), db_toggle_bookmark(false),
968  db_run_jobs_on_remote(false), db_continue_embedded(false),
969  db_use_fixed_size(true), db_browse_always(false),
970  db_browse_all_tuners(false),
971  db_use_channel_groups(false), db_remember_last_channel_group(false),
972 
973  tryUnflaggedSkip(false),
974  smartForward(false),
975  ff_rew_repos(1.0f), ff_rew_reverse(false),
976  jumped_back(false), // XXX unused, remove this field
977  vbimode(VBIMode::None),
978  // State variables
979  switchToInputId(0),
980  wantsToQuit(true),
981  stretchAdjustment(false),
982  audiosyncAdjustment(false),
983  subtitleZoomAdjustment(false),
984  subtitleDelayAdjustment(false),
985  editmode(false), zoomMode(false),
986  sigMonMode(false),
987  endOfRecording(false),
988  requestDelete(false), allowRerecord(false),
989  doSmartForward(false),
990  queuedTranscode(false),
991  adjustingPicture(kAdjustingPicture_None),
992  adjustingPictureAttribute(kPictureAttribute_None),
993  askAllowLock(QMutex::Recursive),
994  // Channel Editing
995  chanEditMapLock(QMutex::Recursive),
996  ddMapSourceId(0), ddMapLoader(new DDLoader(this)),
997  // Sleep Timer
998  sleep_index(0), sleepTimerId(0), sleepDialogTimerId(0),
999  // Idle Timer
1000  idleTimerId(0), idleDialogTimerId(0),
1001  // CC/Teletext input state variables
1002  ccInputMode(false),
1003  // Arbritary seek input state variables
1004  asInputMode(false),
1005  // Channel changing state variables
1006  queuedChanNum(""),
1007  lockTimerOn(false),
1008  // channel browsing
1009  browsehelper(NULL),
1010  // Program Info for currently playing video
1011  lastProgram(NULL),
1012  inPlaylist(false), underNetworkControl(false),
1013  // Jump to program stuff
1014  jumpToProgramPIPState(kPIPOff),
1015  jumpToProgram(false),
1016  // Video Player currently receiving UI input
1017  playerActive(-1),
1018  noHardwareDecoders(false),
1019  //Recorder switching info
1020  switchToRec(NULL),
1021  // LCD Info
1022  lcdTitle(""), lcdSubtitle(""), lcdCallsign(""),
1023  // Window info (GUI is optional, transcoding, preview img, etc)
1024  myWindow(NULL), weDisabledGUI(false),
1025  disableDrawUnusedRects(false),
1026  isEmbedded(false), ignoreKeyPresses(false),
1027  // Timers
1028  lcdTimerId(0), lcdVolumeTimerId(0),
1029  networkControlTimerId(0), jumpMenuTimerId(0),
1030  pipChangeTimerId(0),
1031  switchToInputTimerId(0), ccInputTimerId(0),
1032  asInputTimerId(0), queueInputTimerId(0),
1033  browseTimerId(0), updateOSDPosTimerId(0),
1034  updateOSDDebugTimerId(0),
1035  endOfPlaybackTimerId(0), embedCheckTimerId(0),
1036  endOfRecPromptTimerId(0), videoExitDialogTimerId(0),
1037  pseudoChangeChanTimerId(0), speedChangeTimerId(0),
1038  errorRecoveryTimerId(0), exitPlayerTimerId(0)
1039 {
1040  LOG(VB_GENERAL, LOG_INFO, LOC + "Creating TV object");
1041  ctorTime.start();
1042 
1043  setObjectName("TV");
1045 
1046  sleep_times.push_back(SleepTimerInfo(QObject::tr("Off"), 0));
1047  sleep_times.push_back(SleepTimerInfo(QObject::tr("30m"), 30*60));
1048  sleep_times.push_back(SleepTimerInfo(QObject::tr("1h"), 60*60));
1049  sleep_times.push_back(SleepTimerInfo(QObject::tr("1h30m"), 90*60));
1050  sleep_times.push_back(SleepTimerInfo(QObject::tr("2h"), 120*60));
1051 
1052  playerLock.lockForWrite();
1053  player.push_back(new PlayerContext(kPlayerInUseID));
1054  playerActive = 0;
1055  playerLock.unlock();
1056 
1057  InitFromDB();
1058 
1059  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Finished creating TV object");
1060 }
1061 
1062 void TV::InitFromDB(void)
1063 {
1064  QMap<QString,QString> kv;
1065  kv["LiveTVIdleTimeout"] = "0";
1066  kv["BrowseMaxForward"] = "240";
1067  kv["PlaybackExitPrompt"] = "0";
1068  kv["AutomaticSetWatched"] = "0";
1069  kv["EndOfRecordingExitPrompt"] = "0";
1070  kv["JumpToProgramOSD"] = "1";
1071  kv["GuiSizeForTV"] = "0";
1072  kv["WatchTVGuide"] = "0";
1073  kv["AltClearSavedPosition"] = "1";
1074  kv["JobsRunOnRecordHost"] = "0";
1075  kv["ContinueEmbeddedTVPlay"] = "0";
1076  kv["UseFixedWindowSize"] = "1";
1077  kv["PersistentBrowseMode"] = "0";
1078  kv["BrowseAllTuners"] = "0";
1079  kv["ChannelOrdering"] = "channum";
1080 
1081  kv["CustomFilters"] = "";
1082  kv["ChannelFormat"] = "<num> <sign>";
1083 
1084  kv["TryUnflaggedSkip"] = "0";
1085 
1086  kv["ChannelGroupDefault"] = "-1";
1087  kv["BrowseChannelGroup"] = "0";
1088  kv["SmartForward"] = "0";
1089  kv["FFRewReposTime"] = "100";
1090  kv["FFRewReverse"] = "1";
1091 
1092  kv["BrowseChannelGroup"] = "0";
1093  kv["ChannelGroupDefault"] = "-1";
1094  kv["ChannelGroupRememberLast"] = "0";
1095 
1096  kv["VbiFormat"] = "";
1097  kv["DecodeVBIFormat"] = "";
1098 
1099  int ff_rew_def[8] = { 3, 5, 10, 20, 30, 60, 120, 180 };
1100  for (uint i = 0; i < sizeof(ff_rew_def)/sizeof(ff_rew_def[0]); i++)
1101  kv[QString("FFRewSpeed%1").arg(i)] = QString::number(ff_rew_def[i]);
1102 
1103  MythDB::getMythDB()->GetSettings(kv);
1104 
1105  QString db_channel_ordering;
1106  uint db_browse_max_forward;
1107 
1108  // convert from minutes to ms.
1109  db_idle_timeout = kv["LiveTVIdleTimeout"].toInt() * 60 * 1000;
1110  db_browse_max_forward = kv["BrowseMaxForward"].toInt() * 60;
1111  db_playback_exit_prompt= kv["PlaybackExitPrompt"].toInt();
1112  db_auto_set_watched = kv["AutomaticSetWatched"].toInt();
1113  db_end_of_rec_exit_prompt = kv["EndOfRecordingExitPrompt"].toInt();
1114  db_jump_prefer_osd = kv["JumpToProgramOSD"].toInt();
1115  db_use_gui_size_for_tv = kv["GuiSizeForTV"].toInt();
1116  db_start_in_guide = kv["WatchTVGuide"].toInt();
1117  db_toggle_bookmark = kv["AltClearSavedPosition"].toInt();
1118  db_run_jobs_on_remote = kv["JobsRunOnRecordHost"].toInt();
1119  db_continue_embedded = kv["ContinueEmbeddedTVPlay"].toInt();
1120  db_use_fixed_size = kv["UseFixedWindowSize"].toInt();
1121  db_browse_always = kv["PersistentBrowseMode"].toInt();
1122  db_browse_all_tuners = kv["BrowseAllTuners"].toInt();
1123  db_channel_ordering = kv["ChannelOrdering"];
1124  baseFilters += kv["CustomFilters"];
1125  db_channel_format = kv["ChannelFormat"];
1126  tryUnflaggedSkip = kv["TryUnflaggedSkip"].toInt();
1127  smartForward = kv["SmartForward"].toInt();
1128  ff_rew_repos = kv["FFRewReposTime"].toFloat() * 0.01f;
1129  ff_rew_reverse = kv["FFRewReverse"].toInt();
1130 
1131  db_use_channel_groups = kv["BrowseChannelGroup"].toInt();
1132  db_remember_last_channel_group = kv["ChannelGroupRememberLast"].toInt();
1133  channelGroupId = kv["ChannelGroupDefault"].toInt();
1134 
1135  QString beVBI = kv["VbiFormat"];
1136  QString feVBI = kv["DecodeVBIFormat"];
1137 
1138  RecordingRule record;
1139  record.LoadTemplate("Default");
1141 
1143  {
1145  if (channelGroupId > -1)
1146  {
1148  0, true, "channum, callsign", channelGroupId);
1150  channelGroupChannelList, "channum", true);
1151  }
1152  }
1153 
1154  for (uint i = 0; i < sizeof(ff_rew_def)/sizeof(ff_rew_def[0]); i++)
1155  ff_rew_speeds.push_back(kv[QString("FFRewSpeed%1").arg(i)].toInt());
1156 
1157  // process it..
1159  this,
1160  db_browse_max_forward, db_browse_all_tuners,
1161  db_use_channel_groups, db_channel_ordering);
1162 
1163  vbimode = VBIMode::Parse(!feVBI.isEmpty() ? feVBI : beVBI);
1164 
1165  gCoreContext->addListener(this);
1167 
1168  QMutexLocker lock(&initFromDBLock);
1169  initFromDBDone = true;
1170  initFromDBWait.wakeAll();
1171 }
1172 
1179 bool TV::Init(bool createWindow)
1180 {
1181  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Init -- begin");
1182 
1183  if (createWindow)
1184  {
1185  bool fullscreen = !gCoreContext->GetNumSetting("GuiSizeForTV", 0);
1186  bool switchMode = gCoreContext->GetNumSetting("UseVideoModes", 0);
1187 
1188  saved_gui_bounds = QRect(GetMythMainWindow()->geometry().topLeft(),
1189  GetMythMainWindow()->size());
1190 
1191  // adjust for window manager wierdness.
1192  {
1193  int xbase, width, ybase, height;
1194  float wmult, hmult;
1195  GetMythUI()->GetScreenSettings(xbase, width, wmult,
1196  ybase, height, hmult);
1197  if ((abs(saved_gui_bounds.x()-xbase) < 3) &&
1198  (abs(saved_gui_bounds.y()-ybase) < 3))
1199  {
1200  saved_gui_bounds = QRect(QPoint(xbase, ybase),
1201  GetMythMainWindow()->size());
1202  }
1203  }
1204 
1205  // if width && height are zero users expect fullscreen playback
1206  if (!fullscreen)
1207  {
1208  int gui_width = 0, gui_height = 0;
1209  gCoreContext->GetResolutionSetting("Gui", gui_width, gui_height);
1210  fullscreen |= (0 == gui_width && 0 == gui_height);
1211  }
1212 
1214  if (fullscreen)
1215  {
1216  int xbase, width, ybase, height;
1217  GetMythUI()->GetScreenBounds(xbase, ybase, width, height);
1218  player_bounds = QRect(xbase, ybase, width, height);
1219  }
1220 
1221  // main window sizing
1222  int maxWidth = 1920, maxHeight = 1440;
1223  if (switchMode)
1224  {
1225  DisplayRes *display_res = DisplayRes::GetDisplayRes();
1226  if(display_res)
1227  {
1228  // The very first Resize needs to be the maximum possible
1229  // desired res, because X will mask off anything outside
1230  // the initial dimensions
1231  maxWidth = display_res->GetMaxWidth();
1232  maxHeight = display_res->GetMaxHeight();
1233 
1234  // bit of a hack, but it's ok if the window is too
1235  // big in fullscreen mode
1236  if (fullscreen)
1237  {
1238  player_bounds.setSize(QSize(maxWidth, maxHeight));
1239 
1240  // resize possibly avoids a bug on some systems
1241  GetMythMainWindow()->setGeometry(player_bounds);
1243  }
1244  }
1245  }
1246 
1247  // player window sizing
1249 
1250  myWindow = new TvPlayWindow(mainStack, "Playback");
1251 
1252  if (myWindow->Create())
1253  {
1254  mainStack->AddScreen(myWindow, false);
1255  LOG(VB_GENERAL, LOG_INFO, LOC + "Created TvPlayWindow.");
1256  }
1257  else
1258  {
1259  delete myWindow;
1260  myWindow = NULL;
1261  }
1262 
1263  MythMainWindow *mainWindow = GetMythMainWindow();
1264  if (mainWindow->GetPaintWindow())
1265  mainWindow->GetPaintWindow()->update();
1266  mainWindow->installEventFilter(this);
1267  qApp->processEvents();
1268  }
1269 
1270  {
1271  QMutexLocker locker(&initFromDBLock);
1272  while (!initFromDBDone)
1273  {
1274  qApp->processEvents();
1275  initFromDBWait.wait(&initFromDBLock, 50);
1276  }
1277  }
1278 
1279  PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
1280  mctx->ff_rew_state = 0;
1281  mctx->ff_rew_index = kInitFFRWSpeed;
1282  mctx->ff_rew_speed = 0;
1283  mctx->ts_normal = 1.0f;
1284  ReturnPlayerLock(mctx);
1285 
1286  sleep_index = 0;
1287 
1288  SetUpdateOSDPosition(false);
1289 
1290  const PlayerContext *ctx = GetPlayerReadLock(0, __FILE__, __LINE__);
1291  ClearInputQueues(ctx, false);
1292  ReturnPlayerLock(ctx);
1293 
1294  switchToRec = NULL;
1295  SetExitPlayer(false, false);
1296 
1298  lcdTimerId = StartTimer(1, __LINE__);
1300 
1301  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Init -- end");
1302  return true;
1303 }
1304 
1305 TV::~TV(void)
1306 {
1307  LOG(VB_PLAYBACK, LOG_INFO, "TV::~TV() -- begin");
1308 
1309  if (browsehelper)
1310  browsehelper->Stop();
1311 
1314 
1317 
1318  if (myWindow)
1319  {
1320  myWindow->Close();
1321  myWindow = NULL;
1322  }
1323 
1324  LOG(VB_PLAYBACK, LOG_INFO, "TV::~TV() -- lock");
1325 
1326  // restore window to gui size and position
1328  mwnd->setGeometry(saved_gui_bounds);
1329  mwnd->setFixedSize(saved_gui_bounds.size());
1330  mwnd->ResizePainterWindow(saved_gui_bounds.size());
1331  mwnd->show();
1333  mwnd->move(saved_gui_bounds.topLeft());
1334 
1335  if (lastProgram)
1336  delete lastProgram;
1337 
1338  if (LCD *lcd = LCD::Get())
1339  {
1340  lcd->setFunctionLEDs(FUNC_TV, false);
1341  lcd->setFunctionLEDs(FUNC_MOVIE, false);
1342  lcd->switchToTime();
1343  }
1344 
1345  if (ddMapLoader)
1346  {
1347  ddMapLoader->wait();
1348 
1349  if (ddMapSourceId)
1350  {
1351  ddMapLoader->SetParent(NULL);
1353  ddMapLoader->setAutoDelete(true);
1354  MThreadPool::globalInstance()->start(ddMapLoader, "DDLoadMapPost");
1355  }
1356  else
1357  {
1358  delete ddMapLoader;
1359  }
1360 
1361  ddMapSourceId = 0;
1362  ddMapLoader = NULL;
1363  }
1364 
1365  if (browsehelper)
1366  {
1367  delete browsehelper;
1368  browsehelper = NULL;
1369  }
1370 
1371  PlayerContext *mctx = GetPlayerWriteLock(0, __FILE__, __LINE__);
1372  while (!player.empty())
1373  {
1374  delete player.back();
1375  player.pop_back();
1376  }
1377  ReturnPlayerLock(mctx);
1378 
1379  if (browsehelper)
1380  {
1381  delete browsehelper;
1382  browsehelper = NULL;
1383  }
1384 
1385  LOG(VB_PLAYBACK, LOG_INFO, "TV::~TV() -- end");
1386 }
1387 
1392 {
1393  while (true)
1394  {
1395  qApp->processEvents();
1397  {
1398  wantsToQuit = true;
1399  return;
1400  }
1401 
1402  TVState state = GetState(0);
1403  if ((kState_Error == state) || (kState_None == state))
1404  return;
1405 
1406  if (kState_ChangingState == state)
1407  continue;
1408 
1409  int count = player.size();
1410  for (int i = 0; i < count; i++)
1411  {
1412  const PlayerContext *mctx = GetPlayerReadLock(i, __FILE__, __LINE__);
1413  if (mctx)
1414  {
1415  mctx->LockDeletePlayer(__FILE__, __LINE__);
1416  if (mctx->player && !mctx->player->IsErrored())
1417  {
1418  mctx->player->EventLoop();
1419  mctx->player->VideoLoop();
1420  }
1421  mctx->UnlockDeletePlayer(__FILE__, __LINE__);
1422  }
1423  ReturnPlayerLock(mctx);
1424  }
1425  }
1426 }
1427 
1431 void TV::UpdateChannelList(int groupID)
1432 {
1433  if (!db_use_channel_groups)
1434  return;
1435 
1436  QMutexLocker locker(&channelGroupLock);
1437  if (groupID == channelGroupId)
1438  return;
1439 
1440  ChannelInfoList list;
1441  if (groupID != -1)
1442  {
1443  list = ChannelUtil::GetChannels(
1444  0, true, "channum, callsign", groupID);
1445  ChannelUtil::SortChannels(list, "channum", true);
1446  }
1447 
1448  channelGroupId = groupID;
1449  channelGroupChannelList = list;
1450 
1452  gCoreContext->SaveSetting("ChannelGroupDefault", channelGroupId);
1453 }
1454 
1458 TVState TV::GetState(int player_idx) const
1459 {
1461  const PlayerContext *ctx = GetPlayerReadLock(player_idx, __FILE__, __LINE__);
1462  ret = GetState(ctx);
1463  ReturnPlayerLock(ctx);
1464  return ret;
1465 }
1466 
1467 // XXX what about subtitlezoom?
1468 void TV::GetStatus(void)
1469 {
1470  QVariantMap status;
1471 
1472  const PlayerContext *ctx = GetPlayerReadLock(-1, __FILE__, __LINE__);
1473 
1474  status.insert("state", StateToString(GetState(ctx)));
1475  ctx->LockPlayingInfo(__FILE__, __LINE__);
1476  if (ctx->playingInfo)
1477  {
1478  status.insert("title", ctx->playingInfo->GetTitle());
1479  status.insert("subtitle", ctx->playingInfo->GetSubtitle());
1480  status.insert("starttime",
1482  .toUTC().toString("yyyy-MM-ddThh:mm:ssZ"));
1483  status.insert("chanid",
1484  QString::number(ctx->playingInfo->GetChanID()));
1485  status.insert("programid", ctx->playingInfo->GetProgramID());
1486  status.insert("pathname", ctx->playingInfo->GetPathname());
1487  }
1488  ctx->UnlockPlayingInfo(__FILE__, __LINE__);
1489  osdInfo info;
1490  ctx->CalcPlayerSliderPosition(info);
1491  ctx->LockDeletePlayer(__FILE__, __LINE__);
1492  if (ctx->player)
1493  {
1494  if (!info.text["totalchapters"].isEmpty())
1495  {
1496  QList<long long> chapters;
1497  ctx->player->GetChapterTimes(chapters);
1498  QVariantList var;
1499  foreach (long long chapter, chapters)
1500  var << QVariant(chapter);
1501  status.insert("chaptertimes", var);
1502  }
1503 
1504  uint capmode = ctx->player->GetCaptionMode();
1505  QVariantMap tracks;
1506 
1507  QStringList list = ctx->player->GetTracks(kTrackTypeSubtitle);
1508  int currenttrack = -1;
1509  if (!list.isEmpty() && (kDisplayAVSubtitle == capmode))
1510  currenttrack = ctx->player->GetTrack(kTrackTypeSubtitle);
1511  for (int i = 0; i < list.size(); i++)
1512  {
1513  if (i == currenttrack)
1514  status.insert("currentsubtitletrack", list[i]);
1515  tracks.insert("SELECTSUBTITLE_" + QString::number(i), list[i]);
1516  }
1517 
1519  currenttrack = -1;
1520  if (!list.isEmpty() && (kDisplayTeletextCaptions == capmode))
1521  currenttrack = ctx->player->GetTrack(kTrackTypeTeletextCaptions);
1522  for (int i = 0; i < list.size(); i++)
1523  {
1524  if (i == currenttrack)
1525  status.insert("currentsubtitletrack", list[i]);
1526  tracks.insert("SELECTTTC_" + QString::number(i), list[i]);
1527  }
1528 
1529  list = ctx->player->GetTracks(kTrackTypeCC708);
1530  currenttrack = -1;
1531  if (!list.isEmpty() && (kDisplayCC708 == capmode))
1532  currenttrack = ctx->player->GetTrack(kTrackTypeCC708);
1533  for (int i = 0; i < list.size(); i++)
1534  {
1535  if (i == currenttrack)
1536  status.insert("currentsubtitletrack", list[i]);
1537  tracks.insert("SELECTCC708_" + QString::number(i), list[i]);
1538  }
1539 
1540  list = ctx->player->GetTracks(kTrackTypeCC608);
1541  currenttrack = -1;
1542  if (!list.isEmpty() && (kDisplayCC608 == capmode))
1543  currenttrack = ctx->player->GetTrack(kTrackTypeCC608);
1544  for (int i = 0; i < list.size(); i++)
1545  {
1546  if (i == currenttrack)
1547  status.insert("currentsubtitletrack", list[i]);
1548  tracks.insert("SELECTCC608_" + QString::number(i), list[i]);
1549  }
1550 
1551  list = ctx->player->GetTracks(kTrackTypeRawText);
1552  currenttrack = -1;
1553  if (!list.isEmpty() && (kDisplayRawTextSubtitle == capmode))
1554  currenttrack = ctx->player->GetTrack(kTrackTypeRawText);
1555  for (int i = 0; i < list.size(); i++)
1556  {
1557  if (i == currenttrack)
1558  status.insert("currentsubtitletrack", list[i]);
1559  tracks.insert("SELECTRAWTEXT_" + QString::number(i), list[i]);
1560  }
1561 
1562  if (ctx->player->HasTextSubtitles())
1563  {
1564  if (kDisplayTextSubtitle == capmode)
1565  status.insert("currentsubtitletrack", tr("External Subtitles"));
1566  tracks.insert(ACTION_ENABLEEXTTEXT, tr("External Subtitles"));
1567  }
1568 
1569  status.insert("totalsubtitletracks", tracks.size());
1570  if (!tracks.isEmpty())
1571  status.insert("subtitletracks", tracks);
1572 
1573  tracks.clear();
1574  list = ctx->player->GetTracks(kTrackTypeAudio);
1575  currenttrack = ctx->player->GetTrack(kTrackTypeAudio);
1576  for (int i = 0; i < list.size(); i++)
1577  {
1578  if (i == currenttrack)
1579  status.insert("currentaudiotrack", list[i]);
1580  tracks.insert("SELECTAUDIO_" + QString::number(i), list[i]);
1581  }
1582 
1583  status.insert("totalaudiotracks", tracks.size());
1584  if (!tracks.isEmpty())
1585  status.insert("audiotracks", tracks);
1586 
1587  status.insert("playspeed", ctx->player->GetPlaySpeed());
1588  status.insert("audiosyncoffset", (long long)ctx->player->GetAudioTimecodeOffset());
1589  if (ctx->player->GetAudio()->ControlsVolume())
1590  {
1591  status.insert("volume", ctx->player->GetVolume());
1592  status.insert("mute", ctx->player->GetMuteState());
1593  }
1594  if (ctx->player->GetVideoOutput())
1595  {
1596  VideoOutput *vo = ctx->player->GetVideoOutput();
1600  {
1601  status.insert("brightness",
1603  }
1604  if (supp & kPictureAttributeSupported_Brightness)
1605  {
1606  status.insert("contrast",
1608  }
1609  if (supp & kPictureAttributeSupported_Brightness)
1610  {
1611  status.insert("colour",
1613  }
1614  if (supp & kPictureAttributeSupported_Brightness)
1615  {
1616  status.insert("hue",
1618  }
1620  {
1621  status.insert("studiolevels",
1623  }
1624  }
1625  }
1626  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
1627 
1628  ReturnPlayerLock(ctx);
1629 
1630  QHashIterator<QString,QString> tit(info.text);
1631  while (tit.hasNext())
1632  {
1633  tit.next();
1634  status.insert(tit.key(), tit.value());
1635  }
1636 
1637  QHashIterator<QString,int> vit(info.values);
1638  while (vit.hasNext())
1639  {
1640  vit.next();
1641  status.insert(vit.key(), vit.value());
1642  }
1643 
1645 }
1646 
1651 {
1653  if (!actx->InStateChange())
1654  ret = actx->GetState();
1655  return ret;
1656 }
1657 
1662 bool TV::LiveTV(bool showDialogs)
1663 {
1664  requestDelete = false;
1665  allowRerecord = false;
1666  jumpToProgram = false;
1667 
1668  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
1669  if (actx->GetState() == kState_None &&
1670  RequestNextRecorder(actx, showDialogs))
1671  {
1672  actx->SetInitialTVState(true);
1673  HandleStateChange(actx, actx);
1674  switchToRec = NULL;
1675 
1676  // Start Idle Timer
1677  if (db_idle_timeout > 0)
1678  {
1679  idleTimerId = StartTimer(db_idle_timeout, __LINE__);
1680  LOG(VB_GENERAL, LOG_INFO, QString("Using Idle Timer. %1 minutes")
1681  .arg(db_idle_timeout*(1.0f/60000.0f)));
1682  }
1683 
1684  ReturnPlayerLock(actx);
1685  return true;
1686  }
1687  ReturnPlayerLock(actx);
1688  return false;
1689 }
1690 
1691 int TV::GetLastRecorderNum(int player_idx) const
1692 {
1693  const PlayerContext *ctx = GetPlayerReadLock(player_idx, __FILE__, __LINE__);
1694  int ret = ctx->GetCardID();
1695  ReturnPlayerLock(ctx);
1696  return ret;
1697 }
1698 
1699 bool TV::RequestNextRecorder(PlayerContext *ctx, bool showDialogs)
1700 {
1701  if (!ctx)
1702  return false;
1703 
1704  ctx->SetRecorder(NULL);
1705 
1706  RemoteEncoder *testrec = NULL;
1707  if (switchToRec)
1708  {
1709  // If this is set we, already got a new recorder in SwitchCards()
1710  testrec = switchToRec;
1711  switchToRec = NULL;
1712  }
1713  else
1714  {
1715  // When starting LiveTV we just get the next free recorder
1716  testrec = RemoteRequestNextFreeRecorder(-1);
1717  }
1718 
1719  if (!testrec)
1720  return false;
1721 
1722  if (!testrec->IsValidRecorder())
1723  {
1724  if (showDialogs)
1725  ShowNoRecorderDialog(ctx);
1726 
1727  delete testrec;
1728 
1729  return false;
1730  }
1731 
1732  ctx->SetRecorder(testrec);
1733 
1734  return true;
1735 }
1736 
1737 void TV::FinishRecording(int player_ctx)
1738 {
1739  PlayerContext *ctx = GetPlayerReadLock(player_ctx, __FILE__, __LINE__);
1740  if (StateIsRecording(GetState(ctx)) && ctx->recorder)
1741  ctx->recorder->FinishRecording();
1742  ReturnPlayerLock(ctx);
1743 }
1744 
1746  const QStringList &msg, int timeuntil,
1747  bool hasrec, bool haslater)
1748 {
1749 #if 0
1750  LOG(VB_GENERAL, LOG_DEBUG, LOC + "AskAllowRecording");
1751 #endif
1752  if (!StateIsLiveTV(GetState(ctx)))
1753  return;
1754 
1755  ProgramInfo *info = new ProgramInfo(msg);
1756  if (!info->GetChanID())
1757  {
1758  delete info;
1759  return;
1760  }
1761 
1762  QMutexLocker locker(&askAllowLock);
1763  QString key = info->MakeUniqueKey();
1764  if (timeuntil > 0)
1765  {
1766  // add program to list
1767 #if 0
1768  LOG(VB_GENERAL, LOG_DEBUG, LOC + "AskAllowRecording -- " +
1769  QString("adding '%1'").arg(info->title));
1770 #endif
1771  QDateTime expiry = MythDate::current().addSecs(timeuntil);
1772  askAllowPrograms[key] = AskProgramInfo(expiry, hasrec, haslater, info);
1773  }
1774  else
1775  {
1776  // remove program from list
1777  LOG(VB_GENERAL, LOG_INFO, LOC + "AskAllowRecording -- " +
1778  QString("removing '%1'").arg(info->GetTitle()));
1779  QMap<QString,AskProgramInfo>::iterator it = askAllowPrograms.find(key);
1780  if (it != askAllowPrograms.end())
1781  {
1782  delete (*it).info;
1783  askAllowPrograms.erase(it);
1784  }
1785  delete info;
1786  }
1787 
1788  ShowOSDAskAllow(ctx);
1789 }
1790 
1792 {
1793  QMutexLocker locker(&askAllowLock);
1794  if (!ctx->recorder)
1795  return;
1796 
1797  uint cardid = ctx->GetCardID();
1798 
1799  QString single_rec =
1800  tr("MythTV wants to record \"%1\" on %2 in %d seconds. "
1801  "Do you want to:");
1802 
1803  QString record_watch = tr("Record and watch while it records");
1804  QString let_record1 = tr("Let it record and go back to the Main Menu");
1805  QString let_recordm = tr("Let them record and go back to the Main Menu");
1806  QString record_later1 = tr("Record it later, I want to watch TV");
1807  QString record_laterm = tr("Record them later, I want to watch TV");
1808  QString do_not_record1= tr("Don't let it record, I want to watch TV");
1809  QString do_not_recordm= tr("Don't let them record, I want to watch TV");
1810 
1811  // eliminate timed out programs
1812  QDateTime timeNow = MythDate::current();
1813  QMap<QString,AskProgramInfo>::iterator it = askAllowPrograms.begin();
1814  QMap<QString,AskProgramInfo>::iterator next = it;
1815  while (it != askAllowPrograms.end())
1816  {
1817  next = it; ++next;
1818  if ((*it).expiry <= timeNow)
1819  {
1820 #if 0
1821  LOG(VB_GENERAL, LOG_DEBUG, LOC + "UpdateOSDAskAllowDialog -- " +
1822  QString("removing '%1'").arg((*it).info->title));
1823 #endif
1824  delete (*it).info;
1825  askAllowPrograms.erase(it);
1826  }
1827  it = next;
1828  }
1829  int timeuntil = 0;
1830  QString message = QString::null;
1831  uint conflict_count = askAllowPrograms.size();
1832 
1833  it = askAllowPrograms.begin();
1834  if ((1 == askAllowPrograms.size()) && ((*it).info->GetCardID() == cardid))
1835  {
1836  (*it).is_in_same_input_group = (*it).is_conflicting = true;
1837  }
1838  else if (!askAllowPrograms.empty())
1839  {
1840  // get the currently used input on our card
1841  bool busy_input_grps_loaded = false;
1842  vector<uint> busy_input_grps;
1843  TunedInputInfo busy_input;
1844  RemoteIsBusy(cardid, busy_input);
1845 
1846  // check if current input can conflict
1847  it = askAllowPrograms.begin();
1848  for (; it != askAllowPrograms.end(); ++it)
1849  {
1850  (*it).is_in_same_input_group =
1851  (cardid == (*it).info->GetCardID());
1852 
1853  if ((*it).is_in_same_input_group)
1854  continue;
1855 
1856  // is busy_input in same input group as recording
1857  if (!busy_input_grps_loaded)
1858  {
1859  busy_input_grps = CardUtil::GetInputGroups(busy_input.inputid);
1860  busy_input_grps_loaded = true;
1861  }
1862 
1863  vector<uint> input_grps =
1864  CardUtil::GetInputGroups((*it).info->GetInputID());
1865 
1866  for (uint i = 0; i < input_grps.size(); i++)
1867  {
1868  if (find(busy_input_grps.begin(), busy_input_grps.end(),
1869  input_grps[i]) != busy_input_grps.end())
1870  {
1871  (*it).is_in_same_input_group = true;
1872  break;
1873  }
1874  }
1875  }
1876 
1877  // check if inputs that can conflict are ok
1878  conflict_count = 0;
1879  it = askAllowPrograms.begin();
1880  for (; it != askAllowPrograms.end(); ++it)
1881  {
1882  if (!(*it).is_in_same_input_group)
1883  (*it).is_conflicting = false;
1884  else if (cardid == (uint)(*it).info->GetCardID())
1885  (*it).is_conflicting = true;
1886  else if (!CardUtil::IsTunerShared(cardid, (*it).info->GetCardID()))
1887  (*it).is_conflicting = true;
1888  else if ((busy_input.sourceid == (uint)(*it).info->GetSourceID()) &&
1889  (busy_input.mplexid == (uint)(*it).info->QueryMplexID()))
1890  (*it).is_conflicting = false;
1891  else
1892  (*it).is_conflicting = true;
1893 
1894  conflict_count += (*it).is_conflicting ? 1 : 0;
1895  }
1896  }
1897 
1898  it = askAllowPrograms.begin();
1899  for (; it != askAllowPrograms.end() && !(*it).is_conflicting; ++it);
1900 
1901  if (conflict_count == 0)
1902  {
1903  LOG(VB_GENERAL, LOG_INFO, LOC + "The scheduler wants to make "
1904  "a non-conflicting recording.");
1905  // TODO take down mplexid and inform user of problem
1906  // on channel changes.
1907  }
1908  else if (conflict_count == 1 && ((*it).info->GetCardID() == cardid))
1909  {
1910 #if 0
1911  LOG(VB_GENERAL, LOG_DEBUG, LOC + "UpdateOSDAskAllowDialog -- " +
1912  "kAskAllowOneRec");
1913 #endif
1914 
1915  it = askAllowPrograms.begin();
1916 
1917  QString channel = db_channel_format;
1918  channel
1919  .replace("<num>", (*it).info->GetChanNum())
1920  .replace("<sign>", (*it).info->GetChannelSchedulingID())
1921  .replace("<name>", (*it).info->GetChannelName());
1922 
1923  message = single_rec.arg((*it).info->GetTitle()).arg(channel);
1924 
1925  OSD *osd = GetOSDLock(ctx);
1926  if (osd)
1927  {
1928  browsehelper->BrowseEnd(ctx, false);
1929  timeuntil = MythDate::current().secsTo((*it).expiry) * 1000;
1930  osd->DialogShow(OSD_DLG_ASKALLOW, message, timeuntil);
1931  osd->DialogAddButton(record_watch, "DIALOG_ASKALLOW_WATCH_0",
1932  false, !((*it).has_rec));
1933  osd->DialogAddButton(let_record1, "DIALOG_ASKALLOW_EXIT_0");
1934  osd->DialogAddButton(((*it).has_later) ? record_later1 : do_not_record1,
1935  "DIALOG_ASKALLOW_CANCELRECORDING_0",
1936  false, ((*it).has_rec));
1937  }
1938  ReturnOSDLock(ctx, osd);
1939  }
1940  else
1941  {
1942  if (conflict_count > 1)
1943  {
1944  message = QObject::tr(
1945  "MythTV wants to record these programs in %d seconds:");
1946  message += "\n";
1947  }
1948 
1949  bool has_rec = false;
1950  it = askAllowPrograms.begin();
1951  for (; it != askAllowPrograms.end(); ++it)
1952  {
1953  if (!(*it).is_conflicting)
1954  continue;
1955 
1956  QString title = (*it).info->GetTitle();
1957  if ((title.length() < 10) && !(*it).info->GetSubtitle().isEmpty())
1958  title += ": " + (*it).info->GetSubtitle();
1959  if (title.length() > 20)
1960  title = title.left(17) + "...";
1961 
1962  QString channel = db_channel_format;
1963  channel
1964  .replace("<num>", (*it).info->GetChanNum())
1965  .replace("<sign>", (*it).info->GetChannelSchedulingID())
1966  .replace("<name>", (*it).info->GetChannelName());
1967 
1968  if (conflict_count > 1)
1969  {
1970  message += QObject::tr("\"%1\" on %2").arg(title).arg(channel);
1971  message += "\n";
1972  }
1973  else
1974  {
1975  message = single_rec.arg((*it).info->GetTitle()).arg(channel);
1976  has_rec = (*it).has_rec;
1977  }
1978  }
1979 
1980  if (conflict_count > 1)
1981  {
1982  message += "\n";
1983  message += QObject::tr("Do you want to:");
1984  }
1985 
1986  bool all_have_later = true;
1987  timeuntil = 9999999;
1988  it = askAllowPrograms.begin();
1989  for (; it != askAllowPrograms.end(); ++it)
1990  {
1991  if ((*it).is_conflicting)
1992  {
1993  all_have_later &= (*it).has_later;
1994  int tmp = MythDate::current().secsTo((*it).expiry);
1995  tmp *= 1000;
1996  timeuntil = min(timeuntil, max(tmp, 0));
1997  }
1998  }
1999  timeuntil = (9999999 == timeuntil) ? 0 : timeuntil;
2000 
2001  OSD *osd = GetOSDLock(ctx);
2002  if (osd && conflict_count > 1)
2003  {
2004  browsehelper->BrowseEnd(ctx, false);
2005  osd->DialogShow(OSD_DLG_ASKALLOW, message, timeuntil);
2006  osd->DialogAddButton(let_recordm, "DIALOG_ASKALLOW_EXIT_0",
2007  false, true);
2008  osd->DialogAddButton((all_have_later) ? record_laterm : do_not_recordm,
2009  "DIALOG_ASKALLOW_CANCELCONFLICTING_0");
2010  }
2011  else if (osd)
2012  {
2013  browsehelper->BrowseEnd(ctx, false);
2014  osd->DialogShow(OSD_DLG_ASKALLOW, message, timeuntil);
2015  osd->DialogAddButton(let_record1, "DIALOG_ASKALLOW_EXIT_0",
2016  false, !has_rec);
2017  osd->DialogAddButton((all_have_later) ? record_later1 : do_not_record1,
2018  "DIALOG_ASKALLOW_CANCELRECORDING_0",
2019  false, has_rec);
2020  }
2021  ReturnOSDLock(ctx, osd);
2022  }
2023 }
2024 
2026 {
2027  if (!DialogIsVisible(ctx, OSD_DLG_ASKALLOW))
2028  return;
2029 
2030  if (!askAllowLock.tryLock())
2031  {
2032  LOG(VB_GENERAL, LOG_ERR, "allowrecordingbox : askAllowLock is locked");
2033  return;
2034  }
2035 
2036  if (action == "CANCELRECORDING")
2037  {
2038  if (ctx->recorder)
2039  ctx->recorder->CancelNextRecording(true);
2040  }
2041  else if (action == "CANCELCONFLICTING")
2042  {
2043  QMap<QString,AskProgramInfo>::iterator it =
2044  askAllowPrograms.begin();
2045  for (; it != askAllowPrograms.end(); ++it)
2046  {
2047  if ((*it).is_conflicting)
2048  RemoteCancelNextRecording((*it).info->GetCardID(), true);
2049  }
2050  }
2051  else if (action == "WATCH")
2052  {
2053  if (ctx->recorder)
2054  ctx->recorder->CancelNextRecording(false);
2055  }
2056  else // if (action == "EXIT")
2057  {
2058  PrepareToExitPlayer(ctx, __LINE__);
2059  SetExitPlayer(true, true);
2060  }
2061 
2062  askAllowLock.unlock();
2063 }
2064 
2065 int TV::Playback(const ProgramInfo &rcinfo)
2066 {
2067  wantsToQuit = false;
2068  jumpToProgram = false;
2069  allowRerecord = false;
2070  requestDelete = false;
2071 
2072  PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
2073  if (mctx->GetState() != kState_None)
2074  {
2075  ReturnPlayerLock(mctx);
2076  return 0;
2077  }
2078 
2079  mctx->SetPlayingInfo(&rcinfo);
2080  mctx->SetInitialTVState(false);
2081  HandleStateChange(mctx, mctx);
2082 
2083  ReturnPlayerLock(mctx);
2084 
2085  if (LCD *lcd = LCD::Get())
2086  {
2087  lcd->switchToChannel(rcinfo.GetChannelSchedulingID(),
2088  rcinfo.GetTitle(), rcinfo.GetSubtitle());
2089  lcd->setFunctionLEDs((rcinfo.IsRecording())?FUNC_TV:FUNC_MOVIE, true);
2090  }
2091 
2092  return 1;
2093 }
2094 
2096 {
2097  return (state == kState_RecordingOnly ||
2098  state == kState_WatchingRecording);
2099 }
2100 
2102 {
2103  return (state == kState_WatchingPreRecorded ||
2104  state == kState_WatchingRecording ||
2105  state == kState_WatchingVideo ||
2106  state == kState_WatchingDVD ||
2107  state == kState_WatchingBD);
2108 }
2109 
2111 {
2112  return (state == kState_WatchingLiveTV);
2113 }
2114 
2116 {
2117  if (StateIsRecording(state))
2118  {
2119  if (state == kState_RecordingOnly)
2120  return kState_None;
2122  }
2123  return kState_Error;
2124 }
2125 
2126 #define TRANSITION(ASTATE,BSTATE) \
2127  ((ctxState == ASTATE) && (desiredNextState == BSTATE))
2128 
2129 #define SET_NEXT() do { nextState = desiredNextState; changed = true; } while(0)
2130 #define SET_LAST() do { nextState = ctxState; changed = true; } while(0)
2131 
2132 static QString tv_i18n(const QString &msg)
2133 {
2134  QByteArray msg_arr = msg.toLatin1();
2135  QString msg_i18n = QObject::tr(msg_arr.constData());
2136  QByteArray msg_i18n_arr = msg_i18n.toLatin1();
2137  return (msg_arr == msg_i18n_arr) ? msg_i18n : msg;
2138 }
2139 
2149 {
2150  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("HandleStateChange(%1) -- begin")
2151  .arg(find_player_index(ctx)));
2152 
2153  if (ctx->IsErrored())
2154  {
2155  LOG(VB_GENERAL, LOG_ERR, LOC +
2156  "HandleStateChange(): Called after fatal error detected.");
2157  return;
2158  }
2159 
2160  bool changed = false;
2161 
2162  ctx->LockState();
2163  TVState nextState = ctx->GetState();
2164  if (ctx->nextState.empty())
2165  {
2166  LOG(VB_GENERAL, LOG_WARNING, LOC +
2167  "HandleStateChange() Warning, called with no state to change to.");
2168  ctx->UnlockState();
2169  return;
2170  }
2171 
2172  TVState ctxState = ctx->GetState();
2173  TVState desiredNextState = ctx->DequeueNextState();
2174 
2175  LOG(VB_GENERAL, LOG_INFO, LOC +
2176  QString("Attempting to change from %1 to %2")
2177  .arg(StateToString(nextState))
2178  .arg(StateToString(desiredNextState)));
2179 
2180  if (desiredNextState == kState_Error)
2181  {
2182  LOG(VB_GENERAL, LOG_ERR, LOC + "HandleStateChange(): "
2183  "Attempting to set to an error state!");
2184  SetErrored(ctx);
2185  ctx->UnlockState();
2186  return;
2187  }
2188 
2189  bool ok = false;
2190  if (TRANSITION(kState_None, kState_WatchingLiveTV))
2191  {
2192  QString name = "";
2193 
2194  ctx->lastSignalUIInfo.clear();
2195 
2196  ctx->recorder->Setup();
2197 
2198  QDateTime timerOffTime = MythDate::current();
2199  lockTimerOn = false;
2200 
2201  SET_NEXT();
2202 
2203  uint chanid = gCoreContext->GetNumSetting("DefaultChanid", 0);
2204 
2205  if (chanid && !IsTunable(ctx, chanid))
2206  chanid = 0;
2207 
2208  QString channum = "";
2209 
2210  if (chanid)
2211  {
2212  QStringList reclist;
2213 
2214  MSqlQuery query(MSqlQuery::InitCon());
2215  query.prepare("SELECT channum FROM channel "
2216  "WHERE chanid = :CHANID");
2217  query.bindValue(":CHANID", chanid);
2218  if (query.exec() && query.isActive() && query.size() > 0 && query.next())
2219  channum = query.value(0).toString();
2220  else
2221  channum = QString::number(chanid);
2222 
2223  bool getit = ctx->recorder->ShouldSwitchToAnotherCard(
2224  QString::number(chanid));
2225 
2226  if (getit)
2227  reclist = ChannelUtil::GetValidRecorderList(chanid, channum);
2228 
2229  if (reclist.size())
2230  {
2231  RemoteEncoder *testrec = NULL;
2232  vector<uint> excluded_cardids;
2233  testrec = RemoteRequestFreeRecorderFromList(reclist,
2234  excluded_cardids);
2235  if (testrec && testrec->IsValidRecorder())
2236  {
2237  ctx->SetRecorder(testrec);
2238  ctx->recorder->Setup();
2239  }
2240  }
2241  else if (getit)
2242  chanid = 0;
2243  }
2244 
2245  LOG(VB_GENERAL, LOG_NOTICE, LOC + "Spawning LiveTV Recorder -- begin");
2246 
2247  if (chanid && !channum.isEmpty())
2248  ctx->recorder->SpawnLiveTV(ctx->tvchain->GetID(), false, channum);
2249  else
2250  ctx->recorder->SpawnLiveTV(ctx->tvchain->GetID(), false, "");
2251 
2252  LOG(VB_GENERAL, LOG_NOTICE, LOC + "Spawning LiveTV Recorder -- end");
2253 
2254  if (!ctx->ReloadTVChain())
2255  {
2256  LOG(VB_GENERAL, LOG_ERR, LOC +
2257  "HandleStateChange(): LiveTV not successfully started");
2258  RestoreScreenSaver(ctx);
2259  ctx->SetRecorder(NULL);
2260  SetErrored(ctx);
2261  SET_LAST();
2262  }
2263  else
2264  {
2265  ctx->LockPlayingInfo(__FILE__, __LINE__);
2266  QString playbackURL = ctx->playingInfo->GetPlaybackURL(true);
2267  ctx->UnlockPlayingInfo(__FILE__, __LINE__);
2268 
2269  bool opennow = (ctx->tvchain->GetCardType(-1) != "DUMMY");
2270 
2271  LOG(VB_GENERAL, LOG_INFO, LOC +
2272  QString("playbackURL(%1) cardtype(%2)")
2273  .arg(playbackURL).arg(ctx->tvchain->GetCardType(-1)));
2274 
2275  ctx->SetRingBuffer(
2277  playbackURL, false, true,
2278  opennow ? RingBuffer::kLiveTVOpenTimeout : -1));
2279 
2280  ctx->buffer->SetLiveMode(ctx->tvchain);
2281  }
2282 
2283 
2284  if (ctx->playingInfo && StartRecorder(ctx,-1))
2285  {
2286  ok = StartPlayer(mctx, ctx, desiredNextState);
2287  }
2288  if (!ok)
2289  {
2290  LOG(VB_GENERAL, LOG_ERR, LOC + "LiveTV not successfully started");
2291  RestoreScreenSaver(ctx);
2292  ctx->SetRecorder(NULL);
2293  SetErrored(ctx);
2294  SET_LAST();
2295  }
2296  else if (!ctx->IsPIP())
2297  {
2298  if (!lastLockSeenTime.isValid() ||
2299  (lastLockSeenTime < timerOffTime))
2300  {
2301  lockTimer.start();
2302  lockTimerOn = true;
2303  }
2304  }
2305 
2306  if (mctx != ctx)
2307  SetActive(ctx, find_player_index(ctx), false);
2308  }
2309  else if (TRANSITION(kState_WatchingLiveTV, kState_None))
2310  {
2311  SET_NEXT();
2312  RestoreScreenSaver(ctx);
2313  StopStuff(mctx, ctx, true, true, true);
2314 
2315  if ((mctx != ctx) && (GetPlayer(ctx,-1) == ctx))
2316  SetActive(mctx, 0, true);
2317  }
2319  {
2320  SET_NEXT();
2321  }
2322  else if (TRANSITION(kState_None, kState_WatchingPreRecorded) ||
2323  TRANSITION(kState_None, kState_WatchingVideo) ||
2324  TRANSITION(kState_None, kState_WatchingDVD) ||
2325  TRANSITION(kState_None, kState_WatchingBD) ||
2327  {
2328  ctx->LockPlayingInfo(__FILE__, __LINE__);
2329  QString playbackURL = ctx->playingInfo->GetPlaybackURL(true);
2330  ctx->UnlockPlayingInfo(__FILE__, __LINE__);
2331 
2332  ctx->SetRingBuffer(RingBuffer::Create(playbackURL, false));
2333 
2334  if (ctx->buffer && ctx->buffer->IsOpen())
2335  {
2336  if (desiredNextState == kState_WatchingRecording)
2337  {
2338  ctx->LockPlayingInfo(__FILE__, __LINE__);
2340  ctx->playingInfo);
2341  ctx->UnlockPlayingInfo(__FILE__, __LINE__);
2342 
2343  ctx->SetRecorder(rec);
2344 
2345  if (!ctx->recorder ||
2346  !ctx->recorder->IsValidRecorder())
2347  {
2348  LOG(VB_GENERAL, LOG_ERR, LOC +
2349  "Couldn't find recorder for in-progress recording");
2350  desiredNextState = kState_WatchingPreRecorded;
2351  ctx->SetRecorder(NULL);
2352  }
2353  else
2354  {
2355  ctx->recorder->Setup();
2356  }
2357  }
2358 
2359  ok = StartPlayer(mctx, ctx, desiredNextState);
2360 
2361  if (ok)
2362  {
2363  SET_NEXT();
2364 
2365  ctx->LockPlayingInfo(__FILE__, __LINE__);
2366  if (ctx->playingInfo->IsRecording())
2367  {
2368  QString message = "COMMFLAG_REQUEST ";
2369  message += ctx->playingInfo->MakeUniqueKey();
2370  gCoreContext->SendMessage(message);
2371  }
2372  ctx->UnlockPlayingInfo(__FILE__, __LINE__);
2373  }
2374  }
2375 
2376  if (!ok)
2377  {
2378  SET_LAST();
2379  SetErrored(ctx);
2380  }
2381  else if (mctx != ctx)
2382  {
2383  SetActive(ctx, find_player_index(ctx), false);
2384  }
2385  }
2386  else if (TRANSITION(kState_WatchingPreRecorded, kState_None) ||
2387  TRANSITION(kState_WatchingVideo, kState_None) ||
2388  TRANSITION(kState_WatchingDVD, kState_None) ||
2389  TRANSITION(kState_WatchingBD, kState_None) ||
2391  {
2392  SET_NEXT();
2393 
2394  RestoreScreenSaver(ctx);
2395  StopStuff(mctx, ctx, true, true, false);
2396 
2397  if ((mctx != ctx) && (GetPlayer(ctx,-1) == ctx))
2398  SetActive(mctx, 0, true);
2399  }
2400  else if (TRANSITION(kState_None, kState_None))
2401  {
2402  SET_NEXT();
2403  }
2404 
2405  // Print state changed message...
2406  if (!changed)
2407  {
2408  LOG(VB_GENERAL, LOG_ERR, LOC +
2409  QString("Unknown state transition: %1 to %2")
2410  .arg(StateToString(ctx->GetState()))
2411  .arg(StateToString(desiredNextState)));
2412  }
2413  else if (ctx->GetState() != nextState)
2414  {
2415  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Changing from %1 to %2")
2416  .arg(StateToString(ctx->GetState()))
2417  .arg(StateToString(nextState)));
2418  }
2419 
2420  // update internal state variable
2421  TVState lastState = ctx->GetState();
2422  ctx->playingState = nextState;
2423  ctx->UnlockState();
2424 
2425  if (mctx == ctx)
2426  {
2427  if (StateIsLiveTV(ctx->GetState()))
2428  {
2429  LOG(VB_GENERAL, LOG_INFO, LOC + "State is LiveTV & mctx == ctx");
2430  UpdateOSDInput(ctx);
2431  LOG(VB_GENERAL, LOG_INFO, LOC + "UpdateOSDInput done");
2432  UpdateLCD();
2433  LOG(VB_GENERAL, LOG_INFO, LOC + "UpdateLCD done");
2434  ITVRestart(ctx, true);
2435  LOG(VB_GENERAL, LOG_INFO, LOC + "ITVRestart done");
2436  }
2437  else if (StateIsPlaying(ctx->GetState()) && lastState == kState_None)
2438  {
2439  ctx->LockPlayingInfo(__FILE__, __LINE__);
2440  int count = PlayGroup::GetCount();
2441  QString msg = tr("%1 Settings")
2442  .arg(tv_i18n(ctx->playingInfo->GetPlaybackGroup()));
2443  ctx->UnlockPlayingInfo(__FILE__, __LINE__);
2444  if (count > 0)
2445  SetOSDMessage(ctx, msg);
2446  ITVRestart(ctx, false);
2447  }
2448 
2449  if (ctx->buffer && ctx->buffer->IsDVD())
2450  {
2451  UpdateLCD();
2452  }
2453 
2454  if (ctx->recorder)
2455  ctx->recorder->FrontendReady();
2456 
2457  QMutexLocker locker(&timerIdLock);
2462  {
2465  }
2466 
2470 
2471  if (StateIsPlaying(ctx->GetState()))
2472  {
2475 
2476  }
2477 
2478  }
2479 
2480  if (TRANSITION(kState_None, kState_WatchingPreRecorded) ||
2481  TRANSITION(kState_None, kState_WatchingVideo) ||
2482  TRANSITION(kState_None, kState_WatchingDVD) ||
2483  TRANSITION(kState_None, kState_WatchingBD) ||
2484  TRANSITION(kState_None, kState_WatchingRecording) ||
2485  TRANSITION(kState_None, kState_WatchingLiveTV))
2486  {
2487  if (!ctx->IsPIP())
2489  MythMainWindow *mainWindow = GetMythMainWindow();
2490  mainWindow->setBaseSize(player_bounds.size());
2491  mainWindow->setMinimumSize(
2492  (db_use_fixed_size) ? player_bounds.size() : QSize(16, 16));
2493  mainWindow->setMaximumSize(
2494  (db_use_fixed_size) ? player_bounds.size() :
2495  QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
2496  mainWindow->setGeometry(player_bounds);
2497  mainWindow->ResizePainterWindow(player_bounds.size());
2498  if (!weDisabledGUI)
2499  {
2500  weDisabledGUI = true;
2502  }
2503  DrawUnusedRects();
2504  // we no longer need the contents of myWindow
2505  if (myWindow)
2507 
2508  LOG(VB_GENERAL, LOG_INFO, LOC + "Main UI disabled.");
2509  }
2510 
2511  LOG(VB_PLAYBACK, LOG_INFO, LOC +
2512  QString("HandleStateChange(%1) -- end")
2513  .arg(find_player_index(ctx)));
2514 }
2515 #undef TRANSITION
2516 #undef SET_NEXT
2517 #undef SET_LAST
2518 
2524 bool TV::StartRecorder(PlayerContext *ctx, int maxWait)
2525 {
2526  RemoteEncoder *rec = ctx->recorder;
2527  maxWait = (maxWait <= 0) ? 40000 : maxWait;
2528  MythTimer t;
2529  t.start();
2530  bool recording = false, ok = true;
2531  if (!rec) {
2532  LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid Remote Encoder");
2533  SetErrored(ctx);
2534  return false;
2535  }
2536  while (!(recording = rec->IsRecording(&ok)) &&
2537  !exitPlayerTimerId && t.elapsed() < maxWait)
2538  {
2539  if (!ok)
2540  {
2541  LOG(VB_GENERAL, LOG_ERR, LOC + "StartRecorder() -- "
2542  "lost contact with backend");
2543  SetErrored(ctx);
2544  return false;
2545  }
2546  usleep(5000);
2547  }
2548 
2549  if (!recording || exitPlayerTimerId)
2550  {
2551  if (!exitPlayerTimerId)
2552  LOG(VB_GENERAL, LOG_ERR, LOC + "StartRecorder() -- "
2553  "timed out waiting for recorder to start");
2554  return false;
2555  }
2556 
2557  LOG(VB_PLAYBACK, LOG_INFO, LOC +
2558  QString("StartRecorder(): took %1 ms to start recorder.")
2559  .arg(t.elapsed()));
2560 
2561  return true;
2562 }
2563 
2578  bool stopRingBuffer, bool stopPlayer, bool stopRecorder)
2579 {
2580  LOG(VB_PLAYBACK, LOG_INFO,
2581  LOC + QString("StopStuff() for player ctx %1 -- begin")
2582  .arg(find_player_index(ctx)));
2583 
2584  SetActive(mctx, 0, false);
2585 
2586  if (ctx->buffer)
2587  ctx->buffer->IgnoreWaitStates(true);
2588 
2589  ctx->LockDeletePlayer(__FILE__, __LINE__);
2590  if (stopPlayer)
2591  ctx->StopPlaying();
2592  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
2593 
2594  if (stopRingBuffer)
2595  {
2596  LOG(VB_PLAYBACK, LOG_INFO, LOC + "StopStuff(): stopping ring buffer");
2597  if (ctx->buffer)
2598  {
2599  ctx->buffer->StopReads();
2600  ctx->buffer->Pause();
2601  ctx->buffer->WaitForPause();
2602  }
2603  }
2604 
2605  if (stopPlayer)
2606  {
2607  LOG(VB_PLAYBACK, LOG_INFO, LOC + "StopStuff(): stopping player");
2608  if (ctx == mctx)
2609  {
2610  for (uint i = 1; mctx && (i < player.size()); i++)
2611  StopStuff(mctx, GetPlayer(mctx,i), true, true, true);
2612  }
2613  }
2614 
2615  if (stopRecorder)
2616  {
2617  LOG(VB_PLAYBACK, LOG_INFO, LOC + "StopStuff(): stopping recorder");
2618  if (ctx->recorder)
2619  ctx->recorder->StopLiveTV();
2620  }
2621 
2622  LOG(VB_PLAYBACK, LOG_INFO, LOC + "StopStuff() -- end");
2623 }
2624 
2626 {
2627  int ctx_index = find_player_index(ctx);
2628 
2629  QString loc = LOC + QString("TeardownPlayer() player ctx %1")
2630  .arg(ctx_index);
2631 
2632  if (!mctx || !ctx || ctx_index < 0)
2633  {
2634  LOG(VB_GENERAL, LOG_ERR, loc + "-- error");
2635  return;
2636  }
2637 
2638  LOG(VB_PLAYBACK, LOG_INFO, loc);
2639 
2640  if (mctx != ctx)
2641  {
2642  if (ctx->HasPlayer())
2643  {
2644  PIPRemovePlayer(mctx, ctx);
2645  ctx->SetPlayer(NULL);
2646  }
2647 
2648  player.erase(player.begin() + ctx_index);
2649  delete ctx;
2650  if (mctx->IsPBP())
2651  PBPRestartMainPlayer(mctx);
2652  SetActive(mctx, playerActive, false);
2653  return;
2654  }
2655 
2656  ctx->TeardownPlayer();
2657 }
2658 
2659 void TV::timerEvent(QTimerEvent *te)
2660 {
2661  const int timer_id = te->timerId();
2662 
2663  PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
2664  if (mctx->IsErrored())
2665  {
2666  ReturnPlayerLock(mctx);
2667  return;
2668  }
2669  ReturnPlayerLock(mctx);
2670 
2671  bool ignore = false;
2672  {
2673  QMutexLocker locker(&timerIdLock);
2674  ignore =
2675  (stateChangeTimerId.size() &&
2676  stateChangeTimerId.find(timer_id) == stateChangeTimerId.end());
2677  }
2678  if (ignore)
2679  return; // Always handle state changes first...
2680 
2681  bool handled = true;
2682  if (timer_id == lcdTimerId)
2684  else if (timer_id == lcdVolumeTimerId)
2686  else if (timer_id == sleepTimerId)
2687  ShowOSDSleep();
2688  else if (timer_id == sleepDialogTimerId)
2690  else if (timer_id == idleTimerId)
2691  ShowOSDIdle();
2692  else if (timer_id == idleDialogTimerId)
2694  else if (timer_id == endOfPlaybackTimerId)
2696  else if (timer_id == embedCheckTimerId)
2698  else if (timer_id == endOfRecPromptTimerId)
2700  else if (timer_id == videoExitDialogTimerId)
2702  else if (timer_id == pseudoChangeChanTimerId)
2704  else if (timer_id == speedChangeTimerId)
2706  else if (timer_id == pipChangeTimerId)
2708  else
2709  handled = false;
2710 
2711  if (handled)
2712  return;
2713 
2714  // Check if it matches a stateChangeTimerId
2715  PlayerContext *ctx = NULL;
2716  {
2717  QMutexLocker locker(&timerIdLock);
2718  TimerContextMap::iterator it = stateChangeTimerId.find(timer_id);
2719  if (it != stateChangeTimerId.end())
2720  {
2721  KillTimer(timer_id);
2722  ctx = *it;
2723  stateChangeTimerId.erase(it);
2724  }
2725  }
2726 
2727  if (ctx)
2728  {
2729  PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
2730  bool still_exists = find_player_index(ctx) >= 0;
2731 
2732  while (still_exists && !ctx->nextState.empty())
2733  {
2734  HandleStateChange(mctx, ctx);
2735  if ((kState_None == ctx->GetState() ||
2736  kState_Error == ctx->GetState()) &&
2737  ((mctx != ctx) || jumpToProgram))
2738  {
2739  ReturnPlayerLock(mctx);
2740  mctx = GetPlayerWriteLock(0, __FILE__, __LINE__);
2741  TeardownPlayer(mctx, ctx);
2742  still_exists = false;
2743  }
2744  }
2745  ReturnPlayerLock(mctx);
2746  handled = true;
2747  }
2748 
2749  if (handled)
2750  return;
2751 
2752  // Check if it matches a signalMonitorTimerId
2753  ctx = NULL;
2754  {
2755  QMutexLocker locker(&timerIdLock);
2756  TimerContextMap::iterator it = signalMonitorTimerId.find(timer_id);
2757  if (it != signalMonitorTimerId.end())
2758  {
2759  KillTimer(timer_id);
2760  ctx = *it;
2761  signalMonitorTimerId.erase(it);
2762  }
2763  }
2764 
2765  if (ctx)
2766  {
2767  PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
2768  bool still_exists = find_player_index(ctx) >= 0;
2769 
2770  if (still_exists && !ctx->lastSignalMsg.empty())
2771  { // set last signal msg, so we get some feedback...
2772  UpdateOSDSignal(ctx, ctx->lastSignalMsg);
2773  ctx->lastSignalMsg.clear();
2774  }
2776 
2777  ReturnPlayerLock(mctx);
2778  handled = true;
2779  }
2780 
2781  if (handled)
2782  return;
2783 
2784  // Check if it matches a tvchainUpdateTimerId
2785  ctx = NULL;
2786  {
2787  QMutexLocker locker(&timerIdLock);
2788  TimerContextMap::iterator it = tvchainUpdateTimerId.find(timer_id);
2789  if (it != tvchainUpdateTimerId.end())
2790  {
2791  KillTimer(timer_id);
2792  ctx = *it;
2793  tvchainUpdateTimerId.erase(it);
2794  }
2795  }
2796 
2797  if (ctx)
2798  {
2799  PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
2800  bool still_exists = find_player_index(ctx) >= 0;
2801 
2802  if (still_exists)
2803  ctx->UpdateTVChain();
2804 
2805  ReturnPlayerLock(mctx);
2806  handled = true;
2807  }
2808 
2809  if (handled)
2810  return;
2811 
2812  // Check if it matches networkControlTimerId
2813  QString netCmd = QString::null;
2814  {
2815  QMutexLocker locker(&timerIdLock);
2816  if (timer_id == networkControlTimerId)
2817  {
2818  if (networkControlCommands.size())
2819  netCmd = networkControlCommands.dequeue();
2820  if (networkControlCommands.empty())
2821  {
2824  }
2825  }
2826  }
2827 
2828  if (!netCmd.isEmpty())
2829  {
2830  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
2831  ProcessNetworkControlCommand(actx, netCmd);
2832  ReturnPlayerLock(actx);
2833  handled = true;
2834  }
2835 
2836  if (handled)
2837  return;
2838 
2839  // Check if it matches exitPlayerTimerId
2840  if (timer_id == exitPlayerTimerId)
2841  {
2842  PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
2843 
2844  OSD *osd = GetOSDLock(mctx);
2845  if (osd)
2846  {
2847  osd->DialogQuit();
2848  osd->HideAll();
2849  }
2850  ReturnOSDLock(mctx, osd);
2851 
2852  if (jumpToProgram && lastProgram)
2853  {
2854  if (!lastProgram->IsFileReadable())
2855  {
2856  SetOSDMessage(mctx, tr("Last Program: %1 Doesn't Exist")
2857  .arg(lastProgram->GetTitle()));
2858  lastProgramStringList.clear();
2859  SetLastProgram(NULL);
2860  LOG(VB_PLAYBACK, LOG_ERR, LOC +
2861  "Last Program File does not exist");
2862  jumpToProgram = false;
2863  }
2864  else
2865  ForceNextStateNone(mctx);
2866  }
2867  else
2868  ForceNextStateNone(mctx);
2869 
2870  ReturnPlayerLock(mctx);
2871 
2872  QMutexLocker locker(&timerIdLock);
2874  exitPlayerTimerId = 0;
2875  handled = true;
2876  }
2877 
2878  if (handled)
2879  return;
2880 
2881  if (timer_id == jumpMenuTimerId)
2882  {
2883  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
2884  if (actx)
2885  FillOSDMenuJumpRec(actx);
2886  ReturnPlayerLock(actx);
2887 
2888  QMutexLocker locker(&timerIdLock);
2890  jumpMenuTimerId = 0;
2891  handled = true;
2892  }
2893 
2894  if (handled)
2895  return;
2896 
2897  if (timer_id == switchToInputTimerId)
2898  {
2899  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
2900  if (switchToInputId)
2901  {
2903  switchToInputId = 0;
2904  SwitchInputs(actx, tmp);
2905  }
2906  ReturnPlayerLock(actx);
2907 
2908  QMutexLocker locker(&timerIdLock);
2911  handled = true;
2912  }
2913 
2914  if (handled)
2915  return;
2916 
2917  if (timer_id == ccInputTimerId)
2918  {
2919  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
2920  // Clear closed caption input mode when timer expires
2921  if (ccInputMode)
2922  {
2923  ccInputMode = false;
2924  ClearInputQueues(actx, true);
2925  }
2926  ReturnPlayerLock(actx);
2927 
2928  QMutexLocker locker(&timerIdLock);
2930  ccInputTimerId = 0;
2931  handled = true;
2932  }
2933 
2934  if (handled)
2935  return;
2936 
2937  if (timer_id == asInputTimerId)
2938  {
2939  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
2940  // Clear closed caption input mode when timer expires
2941  if (asInputMode)
2942  {
2943  asInputMode = false;
2944  ClearInputQueues(actx, true);
2945  }
2946  ReturnPlayerLock(actx);
2947 
2948  QMutexLocker locker(&timerIdLock);
2950  asInputTimerId = 0;
2951  handled = true;
2952  }
2953 
2954  if (handled)
2955  return;
2956 
2957  if (timer_id == queueInputTimerId)
2958  {
2959  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
2960  // Commit input when the OSD fades away
2961  if (HasQueuedChannel())
2962  {
2963  OSD *osd = GetOSDLock(actx);
2964  if (osd && !osd->IsWindowVisible("osd_input"))
2965  CommitQueuedInput(actx);
2966  ReturnOSDLock(actx, osd);
2967  }
2968  ReturnPlayerLock(actx);
2969 
2970  QMutexLocker locker(&timerIdLock);
2971  if (!queuedChanID && queuedChanNum.isEmpty() && queueInputTimerId)
2972  {
2974  queueInputTimerId = 0;
2975  }
2976  handled = true;
2977  }
2978 
2979  if (handled)
2980  return;
2981 
2982  if (timer_id == browseTimerId)
2983  {
2984  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
2985  browsehelper->BrowseEnd(actx, false);
2986  ReturnPlayerLock(actx);
2987  handled = true;
2988  }
2989 
2990  if (handled)
2991  return;
2992 
2993  if (timer_id == updateOSDDebugTimerId)
2994  {
2995  bool update = false;
2996  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
2997  OSD *osd = GetOSDLock(actx);
2998  if (osd && osd->IsWindowVisible("osd_debug") &&
2999  (StateIsLiveTV(actx->GetState()) ||
3000  StateIsPlaying(actx->GetState())))
3001  {
3002  update = true;
3003  }
3004  else
3005  {
3006  QMutexLocker locker(&timerIdLock);
3009  actx->buffer->EnableBitrateMonitor(false);
3010  if (actx->player)
3011  actx->player->EnableFrameRateMonitor(false);
3012  }
3013  ReturnOSDLock(actx, osd);
3014  if (update)
3015  UpdateOSDDebug(actx);
3016  ReturnPlayerLock(actx);
3017  handled = true;
3018  }
3019  if (timer_id == updateOSDPosTimerId)
3020  {
3021  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
3022  OSD *osd = GetOSDLock(actx);
3023  if (osd && osd->IsWindowVisible("osd_status") &&
3024  (StateIsLiveTV(actx->GetState()) ||
3025  StateIsPlaying(actx->GetState())))
3026  {
3027  osdInfo info;
3028  if (actx->CalcPlayerSliderPosition(info))
3029  {
3030  osd->SetText("osd_status", info.text, kOSDTimeout_Ignore);
3031  osd->SetValues("osd_status", info.values, kOSDTimeout_Ignore);
3032  }
3033  }
3034  else
3035  SetUpdateOSDPosition(false);
3036  ReturnOSDLock(actx, osd);
3037  ReturnPlayerLock(actx);
3038  handled = true;
3039  }
3040 
3041  if (handled)
3042  return;
3043 
3044  if (timer_id == errorRecoveryTimerId)
3045  {
3046  bool error = false;
3047  PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
3048 
3049  if (mctx->IsPlayerErrored())
3050  {
3051  if (mctx->IsPlayerRecoverable())
3052  RestartMainPlayer(mctx);
3053 
3054  if (mctx->IsPlayerDecoderErrored())
3055  {
3056  LOG(VB_GENERAL, LOG_EMERG, LOC +
3057  QString("Serious hardware decoder error detected. "
3058  "Disabling hardware decoders."));
3059  noHardwareDecoders = true;
3060  for (uint i = 0; i < player.size(); i++)
3061  player[i]->SetNoHardwareDecoders();
3062  RestartMainPlayer(mctx);
3063  }
3064  }
3065 
3066  if (mctx->IsRecorderErrored() ||
3067  mctx->IsPlayerErrored() ||
3068  mctx->IsErrored())
3069  {
3070  SetExitPlayer(true, false);
3071  ForceNextStateNone(mctx);
3072  error = true;
3073  }
3074 
3075  for (uint i = 0; i < player.size(); i++)
3076  {
3077  PlayerContext *ctx = GetPlayer(mctx, i);
3078  if (error || ctx->IsErrored())
3079  ForceNextStateNone(ctx);
3080  }
3081  ReturnPlayerLock(mctx);
3082 
3083  QMutexLocker locker(&timerIdLock);
3088  }
3089 }
3090 
3092 {
3093  QString cmd = QString::null;
3094 
3095  {
3096  QMutexLocker locker(&timerIdLock);
3097  if (changePxP.empty())
3098  {
3099  if (pipChangeTimerId)
3101  pipChangeTimerId = 0;
3102  return true;
3103  }
3104  cmd = changePxP.dequeue();
3105  }
3106 
3107  PlayerContext *mctx = GetPlayerWriteLock(0, __FILE__, __LINE__);
3108  PlayerContext *actx = GetPlayer(mctx, -1);
3109 
3110  if (cmd == "TOGGLEPIPMODE")
3111  PxPToggleView(actx, false);
3112  else if (cmd == "TOGGLEPBPMODE")
3113  PxPToggleView(actx, true);
3114  else if (cmd == "CREATEPIPVIEW")
3115  PxPCreateView(actx, false);
3116  else if (cmd == "CREATEPBPVIEW")
3117  PxPCreateView(actx, true);
3118  else if (cmd == "SWAPPIP")
3119  {
3120  if (mctx != actx)
3121  PxPSwap(mctx, actx);
3122  else if (mctx && player.size() == 2)
3123  PxPSwap(mctx, GetPlayer(mctx,1));
3124  }
3125  else if (cmd == "TOGGLEPIPSTATE")
3126  PxPToggleType(mctx, !mctx->IsPBP());
3127 
3128  ReturnPlayerLock(mctx);
3129 
3130  QMutexLocker locker(&timerIdLock);
3131 
3132  if (pipChangeTimerId)
3134 
3135  if (changePxP.empty())
3136  pipChangeTimerId = 0;
3137  else
3138  pipChangeTimerId = StartTimer(20, __LINE__);
3139 
3140  return true;
3141 }
3142 
3144 {
3145  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
3146  LCD *lcd = LCD::Get();
3147  if (lcd)
3148  {
3149  float progress = 0.0f;
3150  QString lcd_time_string;
3151  bool showProgress = true;
3152 
3153  if (StateIsLiveTV(GetState(actx)))
3154  ShowLCDChannelInfo(actx);
3155 
3156  if (actx->buffer && actx->buffer->IsDVD())
3157  {
3158  ShowLCDDVDInfo(actx);
3159  showProgress = !actx->buffer->IsInDiscMenuOrStillFrame();
3160  }
3161 
3162  if (showProgress)
3163  {
3164  osdInfo info;
3165  if (actx->CalcPlayerSliderPosition(info)) {
3166  progress = info.values["position"] * 0.001f;
3167 
3168  lcd_time_string = info.text["playedtime"] + " / " + info.text["totaltime"];
3169  // if the string is longer than the LCD width, remove all spaces
3170  if (lcd_time_string.length() > (int)lcd->getLCDWidth())
3171  lcd_time_string.remove(' ');
3172  }
3173  }
3174  lcd->setChannelProgress(lcd_time_string, progress);
3175  }
3176  ReturnPlayerLock(actx);
3177 
3178  QMutexLocker locker(&timerIdLock);
3180  lcdTimerId = StartTimer(kLCDTimeout, __LINE__);
3181 
3182  return true;
3183 }
3184 
3186 {
3187  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
3188  LCD *lcd = LCD::Get();
3189  if (lcd)
3190  {
3191  ShowLCDChannelInfo(actx);
3193  }
3194  ReturnPlayerLock(actx);
3195 
3196  QMutexLocker locker(&timerIdLock);
3198 }
3199 
3200 int TV::StartTimer(int interval, int line)
3201 {
3202  int x = QObject::startTimer(interval);
3203  if (!x)
3204  {
3205  LOG(VB_GENERAL, LOG_ERR, LOC +
3206  QString("Failed to start timer on line %1 of %2")
3207  .arg(line).arg(__FILE__));
3208  }
3209  return x;
3210 }
3211 
3212 void TV::KillTimer(int id)
3213 {
3214  QObject::killTimer(id);
3215 }
3216 
3218 {
3219  ctx->ForceNextStateNone();
3220  ScheduleStateChange(ctx);
3221 }
3222 
3224 {
3225  QMutexLocker locker(&timerIdLock);
3226  stateChangeTimerId[StartTimer(1, __LINE__)] = ctx;
3227 }
3228 
3230 {
3231  if (!ctx)
3232  return;
3233  QMutexLocker locker(&timerIdLock);
3234  ctx->errored = true;
3236  errorRecoveryTimerId = StartTimer(1, __LINE__);
3237 }
3238 
3240  const ProgramInfo &p)
3241 {
3242  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Switching to program: %1")
3244  SetLastProgram(&p);
3245  PrepareToExitPlayer(ctx,__LINE__);
3246  jumpToProgram = true;
3247  SetExitPlayer(true, true);
3248 }
3249 
3251 {
3252  bool bm_basic =
3253  (bookmark == kBookmarkAlways ||
3254  (bookmark == kBookmarkAuto && db_playback_exit_prompt == 2));
3255  bool bookmark_it = bm_basic && IsBookmarkAllowed(ctx);
3256  ctx->LockDeletePlayer(__FILE__, line);
3257  if (ctx->player)
3258  {
3259  if (bookmark_it)
3260  SetBookmark(ctx,
3261  (ctx->player->IsNearEnd() || getEndOfRecording())
3262  && !StateIsRecording(GetState(ctx)));
3263  if (db_auto_set_watched)
3264  ctx->player->SetWatched();
3265  }
3266  ctx->UnlockDeletePlayer(__FILE__, line);
3267 }
3268 
3269 void TV::SetExitPlayer(bool set_it, bool wants_to)
3270 {
3271  QMutexLocker locker(&timerIdLock);
3272  if (set_it)
3273  {
3274  wantsToQuit = wants_to;
3275  if (!exitPlayerTimerId)
3276  exitPlayerTimerId = StartTimer(1, __LINE__);
3277  }
3278  else
3279  {
3280  if (exitPlayerTimerId)
3282  exitPlayerTimerId = 0;
3283  wantsToQuit = wants_to;
3284  }
3285 }
3286 
3287 void TV::SetUpdateOSDPosition(bool set_it)
3288 {
3289  QMutexLocker locker(&timerIdLock);
3290  if (set_it)
3291  {
3292  if (!updateOSDPosTimerId)
3293  updateOSDPosTimerId = StartTimer(500, __LINE__);
3294  }
3295  else
3296  {
3297  if (updateOSDPosTimerId)
3299  updateOSDPosTimerId = 0;
3300  }
3301 }
3302 
3304 {
3305  {
3306  QMutexLocker locker(&timerIdLock);
3310  }
3311 
3312  bool is_playing = false;
3313  PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
3314  for (uint i = 0; mctx && (i < player.size()); i++)
3315  {
3316  PlayerContext *ctx = GetPlayer(mctx, i);
3317  if (!StateIsPlaying(ctx->GetState()))
3318  continue;
3319 
3320  if (ctx->IsPlayerPlaying())
3321  {
3322  is_playing = true;
3323  continue;
3324  }
3325 
3326  // If the end of playback is destined to pop up the end of
3327  // recording delete prompt, then don't exit the player here.
3328  if (ctx->GetState() == kState_WatchingPreRecorded &&
3330  continue;
3331 
3332  ForceNextStateNone(ctx);
3333  if (mctx == ctx)
3334  {
3335  endOfRecording = true;
3336  PrepareToExitPlayer(mctx, __LINE__);
3337  SetExitPlayer(true, true);
3338  }
3339  }
3340  ReturnPlayerLock(mctx);
3341 
3342  if (is_playing)
3343  {
3344  QMutexLocker locker(&timerIdLock);
3347  }
3348 }
3349 
3351 {
3352  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
3353  if (!StateIsLiveTV(GetState(actx)))
3354  {
3355  actx->LockDeletePlayer(__FILE__, __LINE__);
3356  bool toggle = actx->player && actx->player->IsEmbedding() &&
3357  actx->player->IsNearEnd() && !actx->player->IsPaused();
3358  actx->UnlockDeletePlayer(__FILE__, __LINE__);
3359  if (toggle)
3360  DoTogglePause(actx, true);
3361  }
3362  ReturnPlayerLock(actx);
3363 }
3364 
3366 {
3369  {
3370  return;
3371  }
3372 
3373  PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
3374  OSD *osd = GetOSDLock(mctx);
3375  if (osd && osd->DialogVisible())
3376  {
3377  ReturnOSDLock(mctx, osd);
3378  ReturnPlayerLock(mctx);
3379  return;
3380  }
3381  ReturnOSDLock(mctx, osd);
3382 
3383  bool do_prompt;
3384  mctx->LockDeletePlayer(__FILE__, __LINE__);
3385  do_prompt = (mctx->GetState() == kState_WatchingPreRecorded &&
3386  mctx->player &&
3387  !mctx->player->IsEmbedding() &&
3388  !mctx->player->IsPlaying());
3389  mctx->UnlockDeletePlayer(__FILE__, __LINE__);
3390 
3391  if (do_prompt)
3392  ShowOSDPromptDeleteRecording(mctx, tr("End Of Recording"));
3393 
3394  ReturnPlayerLock(mctx);
3395 }
3396 
3398 {
3399  {
3400  QMutexLocker locker(&timerIdLock);
3404  }
3405 
3406  // disable dialog and exit playback after timeout
3407  PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
3408  OSD *osd = GetOSDLock(mctx);
3409  if (!osd || !osd->DialogVisible(OSD_DLG_VIDEOEXIT))
3410  {
3411  ReturnOSDLock(mctx, osd);
3412  ReturnPlayerLock(mctx);
3413  return;
3414  }
3415  if (osd)
3416  osd->DialogQuit();
3417  ReturnOSDLock(mctx, osd);
3418  DoTogglePause(mctx, true);
3419  ClearOSD(mctx);
3420  PrepareToExitPlayer(mctx, __LINE__);
3421  ReturnPlayerLock(mctx);
3422 
3423  requestDelete = false;
3424  SetExitPlayer(true, true);
3425 }
3426 
3428 {
3429  {
3430  QMutexLocker locker(&timerIdLock);
3433  }
3434 
3435  bool restartTimer = false;
3436  PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
3437  for (uint i = 0; mctx && (i < player.size()); i++)
3438  {
3439  PlayerContext *ctx = GetPlayer(mctx, i);
3441  continue;
3442 
3443  if (ctx->InStateChange())
3444  {
3445  restartTimer = true;
3446  continue;
3447  }
3448 
3449  LOG(VB_CHANNEL, LOG_INFO,
3450  QString("REC_PROGRAM -- channel change %1").arg(i));
3451 
3452  uint chanid = ctx->pseudoLiveTVRec->GetChanID();
3453  QString channum = ctx->pseudoLiveTVRec->GetChanNum();
3454  StringDeque tmp = ctx->prevChan;
3455 
3456  ctx->prevChan.clear();
3457  ChangeChannel(ctx, chanid, channum);
3458  ctx->prevChan = tmp;
3460  }
3461  ReturnPlayerLock(mctx);
3462 
3463  if (restartTimer)
3464  {
3465  QMutexLocker locker(&timerIdLock);
3467  pseudoChangeChanTimerId = StartTimer(25, __LINE__);
3468  }
3469 }
3470 
3471 void TV::SetSpeedChangeTimer(uint when, int line)
3472 {
3473  QMutexLocker locker(&timerIdLock);
3474  if (speedChangeTimerId)
3476  speedChangeTimerId = StartTimer(when, line);
3477 }
3478 
3480 {
3481  {
3482  QMutexLocker locker(&timerIdLock);
3483  if (speedChangeTimerId)
3486  }
3487 
3488  bool update_msg = false;
3489  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
3490  for (uint i = 0; actx && (i < player.size()); i++)
3491  {
3492  PlayerContext *ctx = GetPlayer(actx, i);
3493  update_msg |= ctx->HandlePlayerSpeedChangeFFRew() && (ctx == actx);
3494  }
3495  ReturnPlayerLock(actx);
3496 
3497  actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
3498  for (uint i = 0; actx && (i < player.size()); i++)
3499  {
3500  PlayerContext *ctx = GetPlayer(actx, i);
3501  update_msg |= ctx->HandlePlayerSpeedChangeEOF() && (ctx == actx);
3502  }
3503 
3504  if (actx && update_msg)
3505  {
3507  }
3508  ReturnPlayerLock(actx);
3509 }
3510 
3512 bool TV::eventFilter(QObject *o, QEvent *e)
3513 {
3514  // We want to intercept all resize events sent to the main window
3515  if ((e->type() == QEvent::Resize))
3516  return (GetMythMainWindow()!= o) ? false : event(e);
3517 
3518  // Intercept keypress events unless they need to be handled by a main UI
3519  // screen (e.g. GuideGrid, ProgramFinder)
3520  if (QEvent::KeyPress == e->type())
3521  return ignoreKeyPresses ? false : event(e);
3522 
3523  if (e->type() == MythEvent::MythEventMessage ||
3524  e->type() == MythEvent::MythUserMessage ||
3526  e->type() == MythMediaEvent::kEventType)
3527  {
3528  customEvent(e);
3529  return true;
3530  }
3531 
3532  switch (e->type())
3533  {
3534  case QEvent::Paint:
3535  case QEvent::UpdateRequest:
3536  case QEvent::Enter:
3537  {
3538  event(e);
3539  return false;
3540  }
3541  default:
3542  return false;
3543  }
3544 }
3545 
3547 bool TV::event(QEvent *e)
3548 {
3549  if (QEvent::Resize == e->type())
3550  {
3551  PlayerContext *mctx = GetPlayerReadLock(0, __FILE__, __LINE__);
3552  mctx->LockDeletePlayer(__FILE__, __LINE__);
3553  if (mctx->player)
3554  mctx->player->WindowResized(((const QResizeEvent*) e)->size());
3555  mctx->UnlockDeletePlayer(__FILE__, __LINE__);
3556  ReturnPlayerLock(mctx);
3557  return true;
3558  }
3559 
3560  if (QEvent::KeyPress == e->type())
3561  {
3562  bool handled = false;
3563  PlayerContext *actx = GetPlayerReadLock(-1, __FILE__, __LINE__);
3564  if (actx->HasPlayer())
3565  handled = ProcessKeypress(actx, (QKeyEvent *)e);
3566  ReturnPlayerLock(actx);
3567  if (handled)
3568  return true;
3569  }
3570 
3571  switch (e->type())
3572  {
3573  case QEvent::Paint:
3574  case QEvent::UpdateRequest:
3575  case QEvent::Enter:
3576  DrawUnusedRects();
3577  return true;
3578  default:
3579  break;
3580  }
3581 
3582  return QObject::event(e);
3583 }
3584 
3585 bool TV::HandleTrackAction(PlayerContext *ctx, const QString &action)
3586 {
3587  ctx->LockDeletePlayer(__FILE__, __LINE__);
3588  if (!ctx->player)
3589  {
3590  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
3591  return false;
3592  }
3593 
3594  bool handled = true;
3595 
3596  if (action == ACTION_TOGGLEEXTTEXT)
3598  else if (ACTION_ENABLEEXTTEXT == action)
3600  else if (ACTION_DISABLEEXTTEXT == action)
3602  else if (ACTION_ENABLEFORCEDSUBS == action)
3603  ctx->player->SetAllowForcedSubtitles(true);
3604  else if (ACTION_DISABLEFORCEDSUBS == action)
3605  ctx->player->SetAllowForcedSubtitles(false);
3606  else if (action == ACTION_ENABLESUBS)
3607  ctx->player->SetCaptionsEnabled(true, true);
3608  else if (action == ACTION_DISABLESUBS)
3609  ctx->player->SetCaptionsEnabled(false, true);
3610  else if (action == ACTION_TOGGLESUBS && !browsehelper->IsBrowsing())
3611  {
3612  if (ccInputMode)
3613  {
3614  bool valid = false;
3615  int page = GetQueuedInputAsInt(&valid, 16);
3616  if (vbimode == VBIMode::PAL_TT && valid)
3617  ctx->player->SetTeletextPage(page);
3618  else if (vbimode == VBIMode::NTSC_CC)
3620  max(min(page - 1, 1), 0));
3621 
3622  ClearInputQueues(ctx, true);
3623 
3624  QMutexLocker locker(&timerIdLock);
3625  ccInputMode = false;
3626  if (ccInputTimerId)
3627  {
3629  ccInputTimerId = 0;
3630  }
3631  }
3633  {
3634  ClearInputQueues(ctx, false);
3635  AddKeyToInputQueue(ctx, 0);
3636 
3637  QMutexLocker locker(&timerIdLock);
3638  ccInputMode = true;
3639  asInputMode = false;
3641  if (asInputTimerId)
3642  {
3644  asInputTimerId = 0;
3645  }
3646  }
3647  else
3648  {
3649  ctx->player->ToggleCaptions();
3650  }
3651  }
3652  else if (action.left(6) == "TOGGLE")
3653  {
3654  int type = to_track_type(action.mid(6));
3655  if (type == kTrackTypeTeletextMenu)
3656  ctx->player->EnableTeletext();
3657  else if (type >= kTrackTypeSubtitle)
3658  ctx->player->ToggleCaptions(type);
3659  else
3660  handled = false;
3661  }
3662  else if (action.left(6) == "SELECT")
3663  {
3664  int type = to_track_type(action.mid(6));
3665  int num = action.section("_", -1).toInt();
3666  if (type >= kTrackTypeAudio)
3667  ctx->player->SetTrack(type, num);
3668  else
3669  handled = false;
3670  }
3671  else if (action.left(4) == "NEXT" || action.left(4) == "PREV")
3672  {
3673  int dir = (action.left(4) == "NEXT") ? +1 : -1;
3674  int type = to_track_type(action.mid(4));
3675  if (type >= kTrackTypeAudio)
3676  ctx->player->ChangeTrack(type, dir);
3677  else if (action.right(2) == "CC")
3678  ctx->player->ChangeCaptionTrack(dir);
3679  else
3680  handled = false;
3681  }
3682  else
3683  handled = false;
3684 
3685  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
3686 
3687  return handled;
3688 }
3689 
3690 static bool has_action(QString action, const QStringList &actions)
3691 {
3692  QStringList::const_iterator it;
3693  for (it = actions.begin(); it != actions.end(); ++it)
3694  {
3695  if (action == *it)
3696  return true;
3697  }
3698  return false;
3699 }
3700 
3701 bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e)
3702 {
3703  bool ignoreKeys = actx->IsPlayerChangingBuffers();
3704 #if DEBUG_ACTIONS
3705  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("ProcessKeypress() ignoreKeys: %1")
3706  .arg(ignoreKeys));
3707 #endif // DEBUG_ACTIONS
3708 
3709  if (idleTimerId)
3710  {
3712  idleTimerId = StartTimer(db_idle_timeout, __LINE__);
3713  }
3714 
3715  QStringList actions;
3716  bool handled = false;
3717 
3718  if (ignoreKeys)
3719  {
3720  handled = GetMythMainWindow()->TranslateKeyPress(
3721  "TV Playback", e, actions);
3722 
3723  if (handled || actions.isEmpty())
3724  return true;
3725 
3726  bool esc = has_action("ESCAPE", actions) ||
3727  has_action("BACK", actions);
3728  bool pause = has_action(ACTION_PAUSE, actions);
3729  bool play = has_action(ACTION_PLAY, actions);
3730 
3731  if ((!esc || browsehelper->IsBrowsing()) && !pause && !play)
3732  return false;
3733  }
3734 
3735  OSD *osd = GetOSDLock(actx);
3736  if (osd && osd->DialogVisible())
3737  {
3738  osd->DialogHandleKeypress(e);
3739  handled = true;
3740  }
3741  ReturnOSDLock(actx, osd);
3742 
3743  if (editmode && !handled)
3744  {
3745  handled |= GetMythMainWindow()->TranslateKeyPress(
3746  "TV Editing", e, actions);
3747 
3748  if (!handled && actx->player)
3749  {
3750  if (has_action("MENU", actions))
3751  {
3752  ShowOSDCutpoint(actx, "EDIT_CUT_POINTS");
3753  handled = true;
3754  }
3755  if (has_action("ESCAPE", actions))
3756  {
3757  if (!actx->player->IsCutListSaved())
3758  ShowOSDCutpoint(actx, "EXIT_EDIT_MODE");
3759  else
3760  {
3761  actx->LockDeletePlayer(__FILE__, __LINE__);
3762  actx->player->DisableEdit(0);
3763  actx->UnlockDeletePlayer(__FILE__, __LINE__);
3764  }
3765  handled = true;
3766  }
3767  else
3768  {
3769  actx->LockDeletePlayer(__FILE__, __LINE__);
3770  int64_t current_frame = actx->player->GetFramesPlayed();
3771  actx->UnlockDeletePlayer(__FILE__, __LINE__);
3772  if ((has_action(ACTION_SELECT, actions)) &&
3773  (actx->player->IsInDelete(current_frame)) &&
3774  (!(actx->player->HasTemporaryMark())))
3775  {
3776  ShowOSDCutpoint(actx, "EDIT_CUT_REGION");
3777  handled = true;
3778  }
3779  else
3780  handled |= actx->player->HandleProgramEditorActions(
3781  actions, current_frame);
3782  }
3783  }
3784  if (handled)
3785  editmode = (actx->player && actx->player->GetEditMode());
3786  }
3787 
3788  if (handled)
3789  return true;
3790 
3791  // If text is already queued up, be more lax on what is ok.
3792  // This allows hex teletext entry and minor channel entry.
3793  const QString txt = e->text();
3794  if (HasQueuedInput() && (1 == txt.length()))
3795  {
3796  bool ok = false;
3797  txt.toInt(&ok, 16);
3798  if (ok || txt=="_" || txt=="-" || txt=="#" || txt==".")
3799  {
3800  AddKeyToInputQueue(actx, txt.at(0).toLatin1());
3801  return true;
3802  }
3803  }
3804 
3805  // Teletext menu
3806  actx->LockDeletePlayer(__FILE__, __LINE__);
3807  if (actx->player && (actx->player->GetCaptionMode() == kDisplayTeletextMenu))
3808  {
3809  QStringList tt_actions;
3810  handled = GetMythMainWindow()->TranslateKeyPress(
3811  "Teletext Menu", e, tt_actions);
3812 
3813  if (!handled && !tt_actions.isEmpty())
3814  {
3815  for (int i = 0; i < tt_actions.size(); i++)
3816  {
3817  if (actx->player->HandleTeletextAction(tt_actions[i]))
3818  {
3819  actx->UnlockDeletePlayer(__FILE__, __LINE__);
3820  return true;
3821  }
3822  }
3823  }
3824  }
3825 
3826  // Interactive television
3827  if (actx->player && actx->player->GetInteractiveTV())
3828  {
3829  QStringList itv_actions;
3830  handled = GetMythMainWindow()->TranslateKeyPress(
3831  "TV Playback", e, itv_actions);
3832 
3833  if (!handled && !itv_actions.isEmpty())
3834  {
3835  for (int i = 0; i < itv_actions.size(); i++)
3836  {
3837  if (actx->player->ITVHandleAction(itv_actions[i]))
3838  {
3839  actx->UnlockDeletePlayer(__FILE__, __LINE__);
3840  return true;
3841  }
3842  }
3843  }
3844  }
3845  actx->UnlockDeletePlayer(__FILE__, __LINE__);
3846 
3847  handled = GetMythMainWindow()->TranslateKeyPress(
3848  "TV Playback", e, actions);
3849 
3850  if (handled || actions.isEmpty())
3851  return true;
3852 
3853  handled = false;
3854 
3855  bool isDVD = actx->buffer && actx->buffer->IsDVD();
3856  bool isMenuOrStill = actx->buffer && actx->buffer->IsInDiscMenuOrStillFrame();
3857 
3858  handled = handled || BrowseHandleAction(actx, actions);
3859  handled = handled || ManualZoomHandleAction(actx, actions);
3860  handled = handled || PictureAttributeHandleAction(actx, actions);
3861  handled = handled || TimeStretchHandleAction(actx, actions);
3862  handled = handled || AudioSyncHandleAction(actx, actions);
3863  handled = handled || SubtitleZoomHandleAction(actx, actions);
3864  handled = handled || SubtitleDelayHandleAction(actx, actions);
3865  handled = handled || DiscMenuHandleAction(actx, actions);
3866  handled = handled || ActiveHandleAction(
3867  actx, actions, isDVD, isMenuOrStill);
3868  handled = handled || ToggleHandleAction(actx, actions, isDVD);
3869  handled = handled || PxPHandleAction(actx, actions);
3870  handled = handled || FFRewHandleAction(actx, actions);
3871  handled = handled || ActivePostQHandleAction(actx, actions);
3872 
3873 #if DEBUG_ACTIONS
3874  for (uint i = 0; i < actions.size(); ++i)
3875  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("handled(%1) actions[%2](%3)")
3876  .arg(handled).arg(i).arg(actions[i]));
3877 #endif // DEBUG_ACTIONS
3878 
3879  if (handled)
3880  return true;
3881 
3882  if (!handled)
3883  {
3884  for (int i = 0; i < actions.size() && !handled; i++)
3885  {
3886  QString action = actions[i];
3887  bool ok = false;
3888  int val = action.toInt(&ok);
3889 
3890  if (ok)
3891  {
3892  AddKeyToInputQueue(actx, '0' + val);
3893  handled = true;
3894  }
3895  }
3896  }
3897 
3898  return true;
3899 }
3900 
3901 bool TV::BrowseHandleAction(PlayerContext *ctx, const QStringList &actions)
3902 {
3903  if (!browsehelper->IsBrowsing())
3904  return false;
3905 
3906  bool handled = true;
3907 
3908  if (has_action(ACTION_UP, actions) || has_action(ACTION_CHANNELUP, actions))
3910  else if (has_action(ACTION_DOWN, actions) || has_action(ACTION_CHANNELDOWN, actions))
3912  else if (has_action(ACTION_LEFT, actions))
3914  else if (has_action(ACTION_RIGHT, actions))
3916  else if (has_action("NEXTFAV", actions))
3918  else if (has_action(ACTION_SELECT, actions))
3919  {
3920  browsehelper->BrowseEnd(ctx, true);
3921  }
3922  else if (has_action(ACTION_CLEAROSD, actions) ||
3923  has_action("ESCAPE", actions) ||
3924  has_action("BACK", actions) ||
3925  has_action("TOGGLEBROWSE", actions))
3926  {
3927  browsehelper->BrowseEnd(ctx, false);
3928  }
3929  else if (has_action(ACTION_TOGGLERECORD, actions))
3930  QuickRecord(ctx);
3931  else
3932  {
3933  handled = false;
3934  QStringList::const_iterator it = actions.begin();
3935  for (; it != actions.end(); ++it)
3936  {
3937  if ((*it).length() == 1 && (*it)[0].isDigit())
3938  {
3939  AddKeyToInputQueue(ctx, (*it)[0].toLatin1());
3940  handled = true;
3941  }
3942  }
3943  }
3944 
3945  // only pass-through actions listed below
3946  return handled ||
3947  !(has_action(ACTION_VOLUMEDOWN, actions) ||
3948  has_action(ACTION_VOLUMEUP, actions) ||
3949  has_action("STRETCHINC", actions) ||
3950  has_action("STRETCHDEC", actions) ||
3951  has_action(ACTION_MUTEAUDIO, actions) ||
3952  has_action("CYCLEAUDIOCHAN", actions) ||
3953  has_action("TOGGLEASPECT", actions) ||
3954  has_action("TOGGLEPIPMODE", actions) ||
3955  has_action("TOGGLEPIPSTATE", actions) ||
3956  has_action("NEXTPIPWINDOW", actions) ||
3957  has_action("CREATEPIPVIEW", actions) ||
3958  has_action("CREATEPBPVIEW", actions) ||
3959  has_action("SWAPPIP", actions));
3960 }
3961 
3962 bool TV::ManualZoomHandleAction(PlayerContext *actx, const QStringList &actions)
3963 {
3964  if (!zoomMode)
3965  return false;
3966 
3967  actx->LockDeletePlayer(__FILE__, __LINE__);
3968  if (!actx->player)
3969  {
3970  actx->UnlockDeletePlayer(__FILE__, __LINE__);
3971  return false;
3972  }
3973 
3974  bool end_manual_zoom = false;
3975  bool handled = true;
3976  bool updateOSD = true;
3977  ZoomDirection zoom = kZoom_END;
3978  if (has_action(ACTION_ZOOMUP, actions))
3979  zoom = kZoomUp;
3980  else if (has_action(ACTION_ZOOMDOWN, actions))
3981  zoom = kZoomDown;
3982  else if (has_action(ACTION_ZOOMLEFT, actions))
3983  zoom = kZoomLeft;
3984  else if (has_action(ACTION_ZOOMRIGHT, actions))
3985  zoom = kZoomRight;
3986  else if (has_action(ACTION_ZOOMASPECTUP, actions))
3987  zoom = kZoomAspectUp;
3988  else if (has_action(ACTION_ZOOMASPECTDOWN, actions))
3989  zoom = kZoomAspectDown;
3990  else if (has_action(ACTION_ZOOMIN, actions))
3991  zoom = kZoomIn;
3992  else if (has_action(ACTION_ZOOMOUT, actions))
3993  zoom = kZoomOut;
3994  else if (has_action(ACTION_ZOOMQUIT, actions))
3995  {
3996  zoom = kZoomHome;
3997  end_manual_zoom = true;
3998  }
3999  else if (has_action(ACTION_ZOOMCOMMIT, actions))
4000  {
4001  end_manual_zoom = true;
4002  SetManualZoom(actx, false, tr("Zoom Committed"));
4003  }
4004  else if (has_action(ACTION_UP, actions) ||
4005  has_action(ACTION_CHANNELUP, actions))
4006  {
4007  zoom = kZoomUp;
4008  }
4009  else if (has_action(ACTION_DOWN, actions) ||
4010  has_action(ACTION_CHANNELDOWN, actions))
4011  {
4012  zoom = kZoomDown;
4013  }
4014  else if (has_action(ACTION_LEFT, actions))
4015  zoom = kZoomLeft;
4016  else if (has_action(ACTION_RIGHT, actions))
4017  zoom = kZoomRight;
4018  else if (has_action(ACTION_VOLUMEUP, actions))
4019  zoom = kZoomAspectUp;
4020  else if (has_action(ACTION_VOLUMEDOWN, actions))
4021  zoom = kZoomAspectDown;
4022  else if (has_action("ESCAPE", actions) ||
4023  has_action("BACK", actions))
4024  {
4025  zoom = kZoomHome;
4026  end_manual_zoom = true;
4027  }
4028  else if (has_action(ACTION_SELECT, actions))
4029  {
4030  end_manual_zoom = true;
4031  SetManualZoom(actx, false, tr("Zoom Committed"));
4032  }
4033  else if (has_action(ACTION_JUMPFFWD, actions))
4034  zoom = kZoomIn;
4035  else if (has_action(ACTION_JUMPRWND, actions))
4036  zoom = kZoomOut;
4037  else
4038  {
4039  updateOSD = false;
4040  // only pass-through actions listed below
4041  handled = !(has_action("STRETCHINC", actions) ||
4042  has_action("STRETCHDEC", actions) ||
4043  has_action(ACTION_MUTEAUDIO, actions) ||
4044  has_action("CYCLEAUDIOCHAN", actions) ||
4045  has_action(ACTION_PAUSE, actions) ||
4046  has_action(ACTION_CLEAROSD, actions));
4047  }
4048  QString msg = tr("Zoom Committed");
4049  if (zoom != kZoom_END)
4050  {
4051  actx->player->Zoom(zoom);
4052  if (end_manual_zoom)
4053  msg = tr("Zoom Ignored");
4054  else
4055  msg = actx->player->GetVideoOutput()->GetZoomString();
4056  }
4057  else if (end_manual_zoom)
4058  msg = tr("%1 Committed")
4059  .arg(actx->player->GetVideoOutput()->GetZoomString());
4060  actx->UnlockDeletePlayer(__FILE__, __LINE__);
4061 
4062  if (updateOSD)
4063  SetManualZoom(actx, !end_manual_zoom, msg);
4064 
4065  return handled;
4066 }
4067 
4069  const QStringList &actions)
4070 {
4071  if (!adjustingPicture)
4072  return false;
4073 
4074  bool handled = true;
4075  if (has_action(ACTION_LEFT, actions))
4076  {
4078  adjustingPictureAttribute, false);
4079  }
4080  else if (has_action(ACTION_RIGHT, actions))
4081  {
4084  }
4085  else
4086  handled = false;
4087 
4088  return handled;
4089 }
4090 
4092  const QStringList &actions)
4093 {
4094  if (!stretchAdjustment)
4095  return false;
4096 
4097  bool handled = true;
4098 
4099  if (has_action(ACTION_LEFT, actions))
4100  ChangeTimeStretch(ctx, -1);
4101  else if (has_action(ACTION_RIGHT, actions))
4102  ChangeTimeStretch(ctx, 1);
4103  else if (has_action(ACTION_DOWN, actions))
4104  ChangeTimeStretch(ctx, -5);
4105  else if (has_action(ACTION_UP, actions))
4106  ChangeTimeStretch(ctx, 5);
4107  else if (has_action("ADJUSTSTRETCH", actions))
4108  ToggleTimeStretch(ctx);
4109  else if (has_action(ACTION_SELECT, actions))
4110  ClearOSD(ctx);
4111  else
4112  handled = false;
4113 
4114  return handled;
4115 }
4116 
4118  const QStringList &actions)
4119 {
4120  if (!audiosyncAdjustment)
4121  return false;
4122 
4123  bool handled = true;
4124 
4125  if (has_action(ACTION_LEFT, actions))
4126  ChangeAudioSync(ctx, -1);
4127  else if (has_action(ACTION_RIGHT, actions))
4128  ChangeAudioSync(ctx, 1);
4129  else if (has_action(ACTION_UP, actions))
4130  ChangeAudioSync(ctx, -10);
4131  else if (has_action(ACTION_DOWN, actions))
4132  ChangeAudioSync(ctx, 10);
4133  else if (has_action(ACTION_TOGGELAUDIOSYNC, actions))
4134  ClearOSD(ctx);
4135  else
4136  handled = false;
4137 
4138  return handled;
4139 }
4140 
4142  const QStringList &actions)
4143 {
4145  return false;
4146 
4147  bool handled = true;
4148 
4149  if (has_action(ACTION_LEFT, actions))
4150  ChangeSubtitleZoom(ctx, -1);
4151  else if (has_action(ACTION_RIGHT, actions))
4152  ChangeSubtitleZoom(ctx, 1);
4153  else if (has_action(ACTION_UP, actions))
4154  ChangeSubtitleZoom(ctx, -10);
4155  else if (has_action(ACTION_DOWN, actions))
4156  ChangeSubtitleZoom(ctx, 10);
4157  else if (has_action(ACTION_TOGGLESUBTITLEZOOM, actions))
4158  ClearOSD(ctx);
4159  else
4160  handled = false;
4161 
4162  return handled;
4163 }
4164 
4166  const QStringList &actions)
4167 {
4169  return false;
4170 
4171  bool handled = true;
4172 
4173  if (has_action(ACTION_LEFT, actions))
4174  ChangeSubtitleDelay(ctx, -5);
4175  else if (has_action(ACTION_RIGHT, actions))
4176  ChangeSubtitleDelay(ctx, 5);
4177  else if (has_action(ACTION_UP, actions))
4178  ChangeSubtitleDelay(ctx, -25);
4179  else if (has_action(ACTION_DOWN, actions))
4180  ChangeSubtitleDelay(ctx, 25);
4181  else if (has_action(ACTION_TOGGLESUBTITLEDELAY, actions))
4182  ClearOSD(ctx);
4183  else
4184  handled = false;
4185 
4186  return handled;
4187 }
4188 
4189 bool TV::DiscMenuHandleAction(PlayerContext *ctx, const QStringList &actions)
4190 {
4191  int64_t pts = 0;
4192  VideoOutput *output = ctx->player->GetVideoOutput();
4193  if (output)
4194  {
4195  VideoFrame *frame = output->GetLastShownFrame();
4196  if (frame)
4197  {
4198  // convert timecode (msec) to pts (90kHz)
4199  pts = (int64_t)(frame->timecode * 90);
4200  }
4201  }
4202  return ctx->buffer->HandleAction(actions, pts);
4203 }
4204 
4205 bool TV::Handle3D(PlayerContext *ctx, const QString &action)
4206 {
4207  ctx->LockDeletePlayer(__FILE__, __LINE__);
4208  if (ctx->player && ctx->player->GetVideoOutput() &&
4210  {
4212  if (ACTION_3DSIDEBYSIDE == action)
4214  else if (ACTION_3DSIDEBYSIDEDISCARD == action)
4216  else if (ACTION_3DTOPANDBOTTOM == action)
4218  else if (ACTION_3DTOPANDBOTTOMDISCARD == action)
4220  ctx->player->GetVideoOutput()->SetStereoscopicMode(mode);
4221  SetOSDMessage(ctx, StereoscopictoString(mode));
4222  }
4223  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
4224  return true;
4225 }
4226 
4228  const QStringList &actions,
4229  bool isDVD, bool isDVDStill)
4230 {
4231  bool handled = true;
4232 
4233  if (has_action("SKIPCOMMERCIAL", actions) && !isDVD)
4234  DoSkipCommercials(ctx, 1);
4235  else if (has_action("SKIPCOMMBACK", actions) && !isDVD)
4236  DoSkipCommercials(ctx, -1);
4237  else if (has_action("QUEUETRANSCODE", actions) && !isDVD)
4238  DoQueueTranscode(ctx, "Default");
4239  else if (has_action("QUEUETRANSCODE_AUTO", actions) && !isDVD)
4240  DoQueueTranscode(ctx, "Autodetect");
4241  else if (has_action("QUEUETRANSCODE_HIGH", actions) && !isDVD)
4242  DoQueueTranscode(ctx, "High Quality");
4243  else if (has_action("QUEUETRANSCODE_MEDIUM", actions) && !isDVD)
4244  DoQueueTranscode(ctx, "Medium Quality");
4245  else if (has_action("QUEUETRANSCODE_LOW", actions) && !isDVD)
4246  DoQueueTranscode(ctx, "Low Quality");
4247  else if (has_action(ACTION_PLAY, actions))
4248  DoPlay(ctx);
4249  else if (has_action(ACTION_PAUSE, actions))
4250  DoTogglePause(ctx, true);
4251  else if (has_action("SPEEDINC", actions) && !isDVDStill)
4252  ChangeSpeed(ctx, 1);
4253  else if (has_action("SPEEDDEC", actions) && !isDVDStill)
4254  ChangeSpeed(ctx, -1);
4255  else if (has_action("ADJUSTSTRETCH", actions))
4256  ChangeTimeStretch(ctx, 0); // just display
4257  else if (has_action("CYCLECOMMSKIPMODE",actions) && !isDVD)
4259  else if (has_action("NEXTSCAN", actions))
4260  {
4261  QString msg = QString::null;
4262  ctx->LockDeletePlayer(__FILE__, __LINE__);
4263  if (ctx->player)
4264  {
4265  ctx->player->NextScanType();
4266  msg = toString(ctx->player->GetScanType());
4267  }
4268  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
4269 
4270  if (!msg.isEmpty())
4271  SetOSDMessage(ctx, msg);
4272  }
4273  else if (has_action(ACTION_SEEKARB, actions) && !isDVD)
4274  {
4275  if (asInputMode)
4276  {
4277  ClearInputQueues(ctx, true);
4278  SetOSDText(ctx, "osd_input", "osd_number_entry", tr("Seek:"),
4279  kOSDTimeout_Med);
4280 
4281  QMutexLocker locker(&timerIdLock);
4282  asInputMode = false;
4283  if (asInputTimerId)
4284  {
4286  asInputTimerId = 0;
4287  }
4288  }
4289  else
4290  {
4291  ClearInputQueues(ctx, false);
4292  AddKeyToInputQueue(ctx, 0);
4293 
4294  QMutexLocker locker(&timerIdLock);
4295  asInputMode = true;
4296  ccInputMode = false;
4298  if (ccInputTimerId)
4299  {
4301  ccInputTimerId = 0;
4302  }
4303  }
4304  }
4305  else if (has_action(ACTION_JUMPRWND, actions))
4306  DoJumpRWND(ctx);
4307  else if (has_action(ACTION_JUMPFFWD, actions))
4308  DoJumpFFWD(ctx);
4309  else if (has_action(ACTION_JUMPBKMRK, actions))
4310  {
4311  ctx->LockDeletePlayer(__FILE__, __LINE__);
4312  uint64_t bookmark = ctx->player->GetBookmark();
4313  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
4314 
4315  if (bookmark)
4316  {
4317  DoPlayerSeekToFrame(ctx, bookmark);
4318  ctx->LockDeletePlayer(__FILE__, __LINE__);
4319  UpdateOSDSeekMessage(ctx, tr("Jump to Bookmark"), kOSDTimeout_Med);
4320  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
4321  }
4322  }
4323  else if (has_action(ACTION_JUMPSTART,actions))
4324  {
4325  DoSeek(ctx, 0, tr("Jump to Beginning"),
4326  /*timeIsOffset*/false,
4327  /*honorCutlist*/true);
4328  }
4329  else if (has_action(ACTION_CLEAROSD, actions))
4330  {
4331  ClearOSD(ctx);
4332  }
4333  else if (has_action(ACTION_VIEWSCHEDULED, actions))
4335  else if (HandleJumpToProgramAction(ctx, actions))
4336  {
4337  }
4338  else if (has_action(ACTION_SIGNALMON, actions))
4339  {
4340  if ((GetState(ctx) == kState_WatchingLiveTV) && ctx->recorder)
4341  {
4342  QString input = ctx->recorder->GetInput();
4343  uint timeout = ctx->recorder->GetSignalLockTimeout(input);
4344 
4345  if (timeout == 0xffffffff)
4346  {
4347  SetOSDMessage(ctx, "No Signal Monitor");
4348  return false;
4349  }
4350 
4351  int rate = sigMonMode ? 0 : 100;
4352  int notify = sigMonMode ? 0 : 1;
4353 
4354  PauseLiveTV(ctx);
4355  ctx->recorder->SetSignalMonitoringRate(rate, notify);
4356  UnpauseLiveTV(ctx);
4357 
4358  lockTimerOn = false;
4360  }
4361  }
4362  else if (has_action(ACTION_SCREENSHOT, actions))
4363  {
4364  ctx->LockDeletePlayer(__FILE__, __LINE__);
4365  if (ctx->player && ctx->player->GetScreenShot())
4366  {
4367  // VideoOutput has saved screenshot
4368  }
4369  else
4370  {
4372  }
4373  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
4374  }
4375  else if (has_action(ACTION_STOP, actions))
4376  {
4377  PrepareToExitPlayer(ctx, __LINE__);
4378  SetExitPlayer(true, true);
4379  }
4380  else if (has_action(ACTION_EXITSHOWNOPROMPTS, actions))
4381  {
4382  requestDelete = false;
4383  PrepareToExitPlayer(ctx, __LINE__);
4384  SetExitPlayer(true, true);
4385  }
4386  else if (has_action("ESCAPE", actions) ||
4387  has_action("BACK", actions))
4388  {
4389  if (StateIsLiveTV(ctx->GetState()) &&
4390  (ctx->lastSignalMsgTime.elapsed() <
4392  {
4393  ClearOSD(ctx);
4394  }
4395  else
4396  {
4397  OSD *osd = GetOSDLock(ctx);
4398  if (osd && osd->IsVisible())
4399  {
4400  ClearOSD(ctx);
4401  ReturnOSDLock(ctx, osd);
4402  return handled;
4403  }
4404  ReturnOSDLock(ctx, osd);
4405  }
4406 
4407  NormalSpeed(ctx);
4408 
4409  StopFFRew(ctx);
4410 
4411  bool do_exit = false;
4412 
4413  if (StateIsLiveTV(GetState(ctx)))
4414  {
4415  if (ctx->HasPlayer() && (12 & db_playback_exit_prompt))
4416  {
4418  return handled;
4419  }
4420  else
4421  {
4422  do_exit = true;
4423  }
4424  }
4425  else
4426  {
4427  if (ctx->HasPlayer() && (5 & db_playback_exit_prompt) &&
4428  !underNetworkControl && !isDVDStill)
4429  {
4431  return handled;
4432  }
4433  PrepareToExitPlayer(ctx, __LINE__);
4434  requestDelete = false;
4435  do_exit = true;
4436  }
4437 
4438  if (do_exit)
4439  {
4440  PlayerContext *mctx = GetPlayer(ctx, 0);
4441  if (mctx != ctx)
4442  { // A PIP is active, just tear it down..
4443  PxPTeardownView(ctx);
4444  return handled;
4445  }
4446  else
4447  {
4448  // If it's a DVD, and we're not trying to execute a
4449  // jumppoint, and it's not in a menu, then first try
4450  // jumping to the title or root menu.
4451  if (isDVD &&
4452  !GetMythMainWindow()->IsExitingToMain() &&
4453  has_action("BACK", actions) &&
4454  !ctx->buffer->DVD()->IsInMenu() &&
4455  (ctx->player->GoToMenu("title") ||
4456  ctx->player->GoToMenu("root")))
4457  {
4458  return handled;
4459  }
4460  SetExitPlayer(true, true);
4461  }
4462  }
4463 
4464  SetActive(ctx, 0, false);
4465  }
4466  else if (has_action(ACTION_ENABLEUPMIX, actions))
4467  EnableUpmix(ctx, true);
4468  else if (has_action(ACTION_DISABLEUPMIX, actions))
4469  EnableUpmix(ctx, false);
4470  else if (has_action(ACTION_VOLUMEDOWN, actions))
4471  ChangeVolume(ctx, false);
4472  else if (has_action(ACTION_VOLUMEUP, actions))
4473  ChangeVolume(ctx, true);
4474  else if (has_action("CYCLEAUDIOCHAN", actions))
4475  ToggleMute(ctx, true);
4476  else if (has_action(ACTION_MUTEAUDIO, actions))
4477  ToggleMute(ctx);
4478  else if (has_action("STRETCHINC", actions))
4479  ChangeTimeStretch(ctx, 1);
4480  else if (has_action("STRETCHDEC", actions))
4481  ChangeTimeStretch(ctx, -1);
4482  else if (has_action("MENU", actions))
4483  ShowOSDMenu(ctx);
4484  else if (has_action("INFO", actions) ||
4485  has_action("INFOWITHCUTLIST", actions))
4486  {
4487  if (HasQueuedInput())
4488  {
4489  DoArbSeek(ctx, ARBSEEK_SET,
4490  has_action("INFOWITHCUTLIST", actions));
4491  }
4492  else
4493  ToggleOSD(ctx, true);
4494  }
4495  else if (has_action(ACTION_TOGGLEOSDDEBUG, actions))
4496  ToggleOSDDebug(ctx);
4497  else if (!isDVDStill && SeekHandleAction(ctx, actions, isDVD))
4498  {
4499  }
4500  else
4501  {
4502  handled = false;
4503  QStringList::const_iterator it = actions.begin();
4504  for (; it != actions.end() && !handled; ++it)
4505  handled = HandleTrackAction(ctx, *it);
4506  }
4507 
4508  return handled;
4509 }
4510 
4511 bool TV::FFRewHandleAction(PlayerContext *ctx, const QStringList &actions)
4512 {
4513  bool handled = false;
4514 
4515  if (ctx->ff_rew_state)
4516  {
4517  for (int i = 0; i < actions.size() && !handled; i++)
4518  {
4519  QString action = actions[i];
4520  bool ok = false;
4521  int val = action.toInt(&ok);
4522 
4523  if (ok && val < (int)ff_rew_speeds.size())
4524  {
4525  SetFFRew(ctx, val);
4526  handled = true;
4527  }
4528  }
4529 
4530  if (!handled)
4531  {
4532  DoPlayerSeek(ctx, StopFFRew(ctx));
4534  handled = true;
4535  }
4536  }
4537 
4538  if (ctx->ff_rew_speed)
4539  {
4540  NormalSpeed(ctx);
4542  handled = true;
4543  }
4544 
4545  return handled;
4546 }
4547 
4549  const QStringList &actions, bool isDVD)
4550 {
4551  bool handled = true;
4552  bool islivetv = StateIsLiveTV(GetState(ctx));
4553 
4554  if (has_action("TOGGLEASPECT", actions))
4555  ToggleAspectOverride(ctx);
4556  else if (has_action("TOGGLEFILL", actions))
4557  ToggleAdjustFill(ctx);
4558  else if (has_action(ACTION_TOGGELAUDIOSYNC, actions))
4559  ChangeAudioSync(ctx, 0); // just display
4560  else if (has_action(ACTION_TOGGLESUBTITLEZOOM, actions))
4561  ChangeSubtitleZoom(ctx, 0); // just display
4562  else if (has_action(ACTION_TOGGLESUBTITLEDELAY, actions))
4563  ChangeSubtitleDelay(ctx, 0); // just display
4564  else if (has_action(ACTION_TOGGLEVISUALISATION, actions))
4565  EnableVisualisation(ctx, false, true /*toggle*/);
4566  else if (has_action(ACTION_ENABLEVISUALISATION, actions))
4567  EnableVisualisation(ctx, true);
4568  else if (has_action(ACTION_DISABLEVISUALISATION, actions))
4569  EnableVisualisation(ctx, false);
4570  else if (has_action("TOGGLEPICCONTROLS", actions))
4572  else if (has_action(ACTION_TOGGLESTUDIOLEVELS, actions))
4573  DoToggleStudioLevels(ctx);
4574  else if (has_action(ACTION_TOGGLENIGHTMODE, actions))
4575  DoToggleNightMode(ctx);
4576  else if (has_action("TOGGLESTRETCH", actions))
4577  ToggleTimeStretch(ctx);
4578  else if (has_action(ACTION_TOGGLEUPMIX, actions))
4579  EnableUpmix(ctx, false, true);
4580  else if (has_action(ACTION_TOGGLESLEEP, actions))
4581  ToggleSleepTimer(ctx);
4582  else if (has_action(ACTION_TOGGLERECORD, actions) && islivetv)
4583  QuickRecord(ctx);
4584  else if (has_action(ACTION_TOGGLEFAV, actions) && islivetv)
4585  ToggleChannelFavorite(ctx);
4586  else if (has_action(ACTION_TOGGLECHANCONTROLS, actions) && islivetv)
4588  else if (has_action(ACTION_TOGGLERECCONTROLS, actions) && islivetv)
4590  else if (has_action(ACTION_TOGGLEINPUTS, actions) &&
4591  islivetv && !ctx->pseudoLiveTVState)
4592  {
4593  ToggleInputs(ctx);
4594  }
4595  else if (has_action("TOGGLEBROWSE", actions))
4596  {
4597  if (islivetv)
4598  browsehelper->BrowseStart(ctx);
4599  else if (!isDVD)
4600  ShowOSDMenu(ctx);
4601  else
4602  handled = false;
4603  }
4604  else if (has_action("EDIT", actions))
4605  {
4606  if (islivetv)
4607  StartChannelEditMode(ctx);
4608  else if (!isDVD)
4609  StartProgramEditMode(ctx);
4610  }
4611  else
4612  handled = false;
4613 
4614  return handled;
4615 }
4616 
4617 void TV::EnableVisualisation(const PlayerContext *ctx, bool enable,
4618  bool toggle, const QString &action)
4619 {
4620  QString visualiser = QString("");
4621  if (action.startsWith("VISUALISER"))
4622  visualiser = action.mid(11);
4623 
4624  ctx->LockDeletePlayer(__FILE__, __LINE__);
4625  if (ctx->player && ctx->player->CanVisualise())
4626  {
4627  bool want = enable || !visualiser.isEmpty();
4628  if (toggle && visualiser.isEmpty())
4629  want = !ctx->player->IsVisualising();
4630  bool on = ctx->player->EnableVisualisation(want, visualiser);
4631  SetOSDMessage(ctx, on ? ctx->player->GetVisualiserName() :
4632  tr("Visualisation Off"));
4633  }
4634  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
4635 }
4636 
4637 bool TV::PxPHandleAction(PlayerContext *ctx, const QStringList &actions)
4638 {
4639  if (!IsPIPSupported(ctx) && !IsPBPSupported(ctx))
4640  return false;
4641 
4642  bool handled = true;
4643  {
4644  QMutexLocker locker(&timerIdLock);
4645 
4646  if (has_action("TOGGLEPIPMODE", actions))
4647  changePxP.enqueue("TOGGLEPIPMODE");
4648  else if (has_action("TOGGLEPBPMODE", actions))
4649  changePxP.enqueue("TOGGLEPBPMODE");
4650  else if (has_action("CREATEPIPVIEW", actions))
4651  changePxP.enqueue("CREATEPIPVIEW");
4652  else if (has_action("CREATEPBPVIEW", actions))
4653  changePxP.enqueue("CREATEPBPVIEW");
4654  else if (has_action("SWAPPIP", actions))
4655  changePxP.enqueue("SWAPPIP");
4656  else if (has_action("TOGGLEPIPSTATE", actions))
4657  changePxP.enqueue("TOGGLEPIPSTATE");
4658  else
4659  handled = false;
4660 
4661  if (!changePxP.empty() && !pipChangeTimerId)
4662  pipChangeTimerId = StartTimer(1, __LINE__);
4663  }
4664 
4665  if (has_action("NEXTPIPWINDOW", actions))
4666  {
4667  SetActive(ctx, -1, true);
4668  handled = true;
4669  }
4670 
4671  return handled;
4672 }
4673 
4675 {
4676  ctx->LockDeletePlayer(__FILE__, __LINE__);
4677  if (ctx->player)
4678  {
4679  if (clear)
4680  {
4681  ctx->player->SetBookmark(true);
4682  SetOSDMessage(ctx, QObject::tr("Bookmark Cleared"));
4683  }
4684  else if (IsBookmarkAllowed(ctx))
4685  {
4686  ctx->player->SetBookmark();
4687  osdInfo info;
4688  ctx->CalcPlayerSliderPosition(info);
4689  info.text["title"] = QObject::tr("Position");
4691  kOSDTimeout_Med);
4692  SetOSDMessage(ctx, QObject::tr("Bookmark Saved"));
4693  }
4694  }
4695  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
4696 }
4697 
4698 bool TV::ActivePostQHandleAction(PlayerContext *ctx, const QStringList &actions)
4699 {
4700  bool handled = true;
4701  TVState state = GetState(ctx);
4702  bool islivetv = StateIsLiveTV(state);
4703  bool isdvd = state == kState_WatchingDVD;
4704  bool isdisc = isdvd || state == kState_WatchingBD;
4705 
4706  if (has_action(ACTION_SELECT, actions))
4707  {
4708  if (!islivetv || !CommitQueuedInput(ctx))
4709  {
4710  ctx->LockDeletePlayer(__FILE__, __LINE__);
4712  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
4713  }
4714  }
4715  else if (has_action("NEXTFAV", actions) && islivetv)
4717  else if (has_action("NEXTSOURCE", actions) && islivetv)
4718  SwitchSource(ctx, kNextSource);
4719  else if (has_action("PREVSOURCE", actions) && islivetv)
4721  else if (has_action("NEXTINPUT", actions) && islivetv)
4722  ToggleInputs(ctx);
4723  else if (has_action("NEXTCARD", actions) && islivetv)
4724  SwitchCards(ctx);
4725  else if (has_action(ACTION_GUIDE, actions))
4727  else if (has_action("PREVCHAN", actions) && islivetv)
4728  PopPreviousChannel(ctx, false);
4729  else if (has_action(ACTION_CHANNELUP, actions))
4730  {
4731  if (islivetv)
4732  {
4733  if (db_browse_always)
4735  else
4737  }
4738  else
4739  DoJumpRWND(ctx);
4740  }
4741  else if (has_action(ACTION_CHANNELDOWN, actions))
4742  {
4743  if (islivetv)
4744  {
4745  if (db_browse_always)
4747  else
4749  }
4750  else
4751  DoJumpFFWD(ctx);
4752  }
4753  else if (has_action("DELETE", actions) && !islivetv)
4754  {
4755  NormalSpeed(ctx);
4756  StopFFRew(ctx);
4757  SetBookmark(ctx);
4758  ShowOSDPromptDeleteRecording(ctx, tr("Are you sure you want to delete:"));
4759  }
4760  else if (has_action(ACTION_JUMPTODVDROOTMENU, actions) && isdisc)
4761  {
4762  ctx->LockDeletePlayer(__FILE__, __LINE__);
4763  if (ctx->player)
4764  ctx->player->GoToMenu("root");
4765  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
4766  }
4767  else if (has_action(ACTION_JUMPTOPOPUPMENU, actions) && isdisc)
4768  {
4769  ctx->LockDeletePlayer(__FILE__, __LINE__);
4770  if (ctx->player)
4771  ctx->player->GoToMenu("popup");
4772  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
4773  }
4774  else if (has_action(ACTION_FINDER, actions))
4776  else
4777  handled = false;
4778 
4779  return handled;
4780 }
4781 
4782 
4784  const QString &command)
4785 {
4786  bool ignoreKeys = ctx->IsPlayerChangingBuffers();
4787 #ifdef DEBUG_ACTIONS
4788  LOG(VB_GENERAL, LOG_DEBUG, LOC + "ProcessNetworkControlCommand(" +
4789  QString("%1) ignoreKeys: %2").arg(command).arg(ignoreKeys));
4790 #endif
4791 
4792  if (ignoreKeys)
4793  {
4794  LOG(VB_GENERAL, LOG_WARNING, LOC +
4795  "Ignoring network control command"
4796  "\n\t\t\tbecause ignoreKeys is set");
4797  return;
4798  }
4799 
4800  QStringList tokens = command.split(" ", QString::SkipEmptyParts);
4801  if (tokens.size() < 2)
4802  {
4803  LOG(VB_GENERAL, LOG_ERR, LOC + "Not enough tokens"
4804  "in network control command" + "\n\t\t\t" +
4805  QString("'%1'").arg(command));
4806  return;
4807  }
4808 
4809  OSD *osd = GetOSDLock(ctx);
4810  bool dlg = false;
4811  if (osd)
4812  dlg = osd->DialogVisible();
4813  ReturnOSDLock(ctx, osd);
4814 
4815  if (dlg)
4816  {
4817  LOG(VB_GENERAL, LOG_WARNING, LOC +
4818  "Ignoring network control command\n\t\t\t" +
4819  QString("because dialog is waiting for a response"));
4820  return;
4821  }
4822 
4823  if (tokens[1] != "QUERY")
4824  ClearOSD(ctx);
4825 
4826  if (tokens.size() == 3 && tokens[1] == "CHANID")
4827  {
4828  queuedChanID = tokens[2].toUInt();
4829  queuedChanNum = QString::null;
4830  CommitQueuedInput(ctx);
4831  }
4832  else if (tokens.size() == 3 && tokens[1] == "CHANNEL")
4833  {
4834  if (StateIsLiveTV(GetState(ctx)))
4835  {
4836  if (tokens[2] == "UP")
4838  else if (tokens[2] == "DOWN")
4840  else if (tokens[2].contains(QRegExp("^[-\\.\\d_#]+$")))
4841  ChangeChannel(ctx, 0, tokens[2]);
4842  }
4843  }
4844  else if (tokens.size() == 3 && tokens[1] == "SPEED")
4845  {
4846  bool paused = ContextIsPaused(ctx, __FILE__, __LINE__);
4847 
4848  if (tokens[2] == "0x")
4849  {
4850  NormalSpeed(ctx);
4851  StopFFRew(ctx);
4852  if (!paused)
4853  DoTogglePause(ctx, true);
4854  }
4855  else
4856  {
4857  float tmpSpeed = 1.0f;
4858  bool ok = false;
4859 
4860  if (tokens[2].contains(QRegExp("^\\-*\\d+x$")))
4861  {
4862  QString speed = tokens[2].left(tokens[2].length()-1);
4863  tmpSpeed = speed.toFloat(&ok);
4864  }
4865  else if (tokens[2].contains(QRegExp("^\\-*\\d*\\.\\d+x$")))
4866  {
4867  QString speed = tokens[2].left(tokens[2].length() - 1);
4868  tmpSpeed = speed.toFloat(&ok);
4869  }
4870  else
4871  {
4872  QRegExp re = QRegExp("^(\\-*\\d+)\\/(\\d+)x$");
4873  if (tokens[2].contains(re))
4874  {
4875  QStringList matches = re.capturedTexts();
4876 
4877  int numerator, denominator;
4878  numerator = matches[1].toInt(&ok);
4879  denominator = matches[2].toInt(&ok);
4880 
4881  if (ok && denominator != 0)
4882  tmpSpeed = static_cast<float>(numerator) /
4883  static_cast<float>(denominator);
4884  else
4885  ok = false;
4886  }
4887  }
4888 
4889  if (ok)
4890  {
4891  float searchSpeed = fabs(tmpSpeed);
4892  unsigned int index;
4893 
4894  if (paused)
4895  DoTogglePause(ctx, true);
4896 
4897  if (tmpSpeed == 0.0f)
4898  {
4899  NormalSpeed(ctx);
4900  StopFFRew(ctx);
4901 
4902  if (!paused)
4903  DoTogglePause(ctx, true);
4904  }
4905  else if (tmpSpeed == 1.0f)
4906  {
4907  StopFFRew(ctx);
4908  ctx->ts_normal = 1.0f;
4909  ChangeTimeStretch(ctx, 0, false);
4910 
4911  ReturnPlayerLock(ctx);
4912  return;
4913  }
4914 
4915  NormalSpeed(ctx);
4916 
4917  for (index = 0; index < ff_rew_speeds.size(); index++)
4918  if (float(ff_rew_speeds[index]) == searchSpeed)
4919  break;
4920 
4921  if ((index < ff_rew_speeds.size()) &&
4922  (float(ff_rew_speeds[index]) == searchSpeed))
4923  {
4924  if (tmpSpeed < 0)
4925  ctx->ff_rew_state = -1;
4926  else if (tmpSpeed > 1)
4927  ctx->ff_rew_state = 1;
4928  else
4929  StopFFRew(ctx);
4930 
4931  if (ctx->ff_rew_state)
4932  SetFFRew(ctx, index);
4933  }
4934  else if (0.48 <= tmpSpeed && tmpSpeed <= 2.0) {
4935  StopFFRew(ctx);
4936 
4937  ctx->ts_normal = tmpSpeed; // alter speed before display
4938  ChangeTimeStretch(ctx, 0, false);
4939  }
4940  else
4941  {
4942  LOG(VB_GENERAL, LOG_WARNING,
4943  QString("Couldn't find %1 speed. Setting Speed to 1x")
4944  .arg(searchSpeed));
4945 
4946  ctx->ff_rew_state = 0;
4947  SetFFRew(ctx, kInitFFRWSpeed);
4948  }
4949  }
4950  else
4951  {
4952  LOG(VB_GENERAL, LOG_ERR,
4953  QString("Found an unknown speed of %1").arg(tokens[2]));
4954  }
4955  }
4956  }
4957  else if (tokens.size() == 2 && tokens[1] == "STOP")
4958  {
4959  SetBookmark(ctx);
4960  ctx->LockDeletePlayer(__FILE__, __LINE__);
4961  if (ctx->player && db_auto_set_watched)
4962  ctx->player->SetWatched();
4963  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
4964  SetExitPlayer(true, true);
4965  }
4966  else if (tokens.size() >= 3 && tokens[1] == "SEEK" && ctx->HasPlayer())
4967  {
4968  if (ctx->buffer && ctx->buffer->IsInDiscMenuOrStillFrame())
4969  return;
4970 
4971  if (tokens[2] == "BEGINNING")
4972  DoSeek(ctx, 0, tr("Jump to Beginning"),
4973  /*timeIsOffset*/false,
4974  /*honorCutlist*/true);
4975  else if (tokens[2] == "FORWARD")
4976  DoSeek(ctx, ctx->fftime, tr("Skip Ahead"),
4977  /*timeIsOffset*/true,
4978  /*honorCutlist*/true);
4979  else if (tokens[2] == "BACKWARD")
4980  DoSeek(ctx, -ctx->rewtime, tr("Skip Back"),
4981  /*timeIsOffset*/true,
4982  /*honorCutlist*/true);
4983  else if ((tokens[2] == "POSITION" ||
4984  tokens[2] == "POSITIONWITHCUTLIST") &&
4985  (tokens.size() == 4) &&
4986  (tokens[3].contains(QRegExp("^\\d+$"))))
4987  {
4988  DoSeekAbsolute(ctx, tokens[3].toInt(),
4989  tokens[2] == "POSITIONWITHCUTLIST");
4990  }
4991  }
4992  else if (tokens.size() >= 3 && tokens[1] == "SUBTITLES")
4993  {
4994  bool ok = false;
4995  uint track = tokens[2].toUInt(&ok);
4996 
4997  if (!ok)
4998  return;
4999 
5000  if (track == 0)
5001  {
5002  ctx->player->SetCaptionsEnabled(false, true);
5003  }
5004  else
5005  {
5006  uint start = 1;
5007  QStringList subs = ctx->player->GetTracks(kTrackTypeSubtitle);
5008  uint finish = start + subs.size();
5009  if (track >= start && track < finish)
5010  {
5011  ctx->player->SetTrack(kTrackTypeSubtitle, track - start);
5013  return;
5014  }
5015 
5016  start = finish + 1;
5017  subs = ctx->player->GetTracks(kTrackTypeCC708);
5018  finish = start + subs.size();
5019  if (track >= start && track < finish)
5020  {
5021  ctx->player->SetTrack(kTrackTypeCC708, track - start);
5023  return;
5024  }
5025 
5026  start = finish + 1;
5027  subs = ctx->player->GetTracks(kTrackTypeCC608);
5028  finish = start + subs.size();
5029  if (track >= start && track < finish)
5030  {
5031  ctx->player->SetTrack(kTrackTypeCC608, track - start);
5033  return;
5034  }
5035 
5036  start = finish + 1;
5038  finish = start + subs.size();
5039  if (track >= start && track < finish)
5040  {
5041  ctx->player->SetTrack(kTrackTypeTeletextCaptions, track-start);
5043  return;
5044  }
5045 
5046  start = finish + 1;
5047  subs = ctx->player->GetTracks(kTrackTypeTeletextMenu);
5048  finish = start + subs.size();
5049  if (track >= start && track < finish)
5050  {
5051  ctx->player->SetTrack(kTrackTypeTeletextMenu, track - start);
5053  return;
5054  }
5055 
5056  start = finish + 1;
5057  subs = ctx->player->GetTracks(kTrackTypeRawText);
5058  finish = start + subs.size();
5059  if (track >= start && track < finish)
5060  {
5061  ctx->player->SetTrack(kTrackTypeRawText, track - start);
5063  return;
5064  }
5065  }
5066  }
5067  else if (tokens.size() >= 3 && tokens[1] == "VOLUME")
5068  {
5069  QRegExp re = QRegExp("(\\d+)%");
5070  if (tokens[2].contains(re))
5071  {
5072  QStringList matches = re.capturedTexts();
5073 
5074  LOG(VB_GENERAL, LOG_INFO, QString("Set Volume to %1%")
5075  .arg(matches[1]));
5076 
5077  bool ok = false;
5078 
5079  int vol = matches[1].toInt(&ok);
5080 
5081  if (!ok)
5082  return;
5083 
5084  if (0 <= vol && vol <= 100)
5085  {
5086  ctx->LockDeletePlayer(__FILE__, __LINE__);
5087  if (!ctx->player)
5088  {
5089  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
5090  return;
5091  }
5092 
5093  vol -= ctx->player->GetVolume();
5094  vol = ctx->player->AdjustVolume(vol);
5095  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
5096 
5097  if (!browsehelper->IsBrowsing() && !editmode)
5098  {
5100  ctx, tr("Adjust Volume"), tr("Volume"),
5101  QString::number(vol),
5102  kOSDFunctionalType_PictureAdjust, "%", vol * 10,
5103  kOSDTimeout_Med);
5104  SetUpdateOSDPosition(false);
5105  }
5106  }
5107  }
5108  }
5109  else if (tokens.size() >= 3 && tokens[1] == "QUERY")
5110  {
5111  if (tokens[2] == "POSITION")
5112  {
5113  QString speedStr;
5114  if (ContextIsPaused(ctx, __FILE__, __LINE__))
5115  {
5116  speedStr = "pause";
5117  }
5118  else if (ctx->ff_rew_state)
5119  {
5120  speedStr = QString("%1x").arg(ctx->ff_rew_speed);
5121  }
5122  else
5123  {
5124  QRegExp re = QRegExp("Play (.*)x");
5125  if (QString(ctx->GetPlayMessage()).contains(re))
5126  {
5127  QStringList matches = re.capturedTexts();
5128  speedStr = QString("%1x").arg(matches[1]);
5129  }
5130  else
5131  {
5132  speedStr = "1x";
5133  }
5134  }
5135 
5136  osdInfo info;
5137  ctx->CalcPlayerSliderPosition(info, true);
5138 
5139  QDateTime respDate = MythDate::current(true);
5140  QString infoStr = "";
5141 
5142  ctx->LockDeletePlayer(__FILE__, __LINE__);
5143  long long fplay = 0;
5144  float rate = 30.0f;
5145  if (ctx->player)
5146  {
5147  fplay = ctx->player->GetFramesPlayed();
5148  rate = ctx->player->GetFrameRate(); // for display only
5149  }
5150  ctx->UnlockDeletePlayer(__FILE__, __LINE__);
5151 
5152  ctx->LockPlayingInfo(__FILE__, __LINE__);
5153  if (ctx->GetState() == kState_WatchingLiveTV)
5154  {
5155  infoStr = "LiveTV";
5156  if (ctx->playingInfo)
5157  respDate = ctx->playingInfo->GetScheduledStartTime();
5158  }
5159  else
5160  {
5161  if (ctx->buffer->IsDVD())
5162  infoStr = "DVD";
5163  else if (ctx->playingInfo->IsRecording())
5164  infoStr = "Recorded";
5165  else
5166  infoStr = "Video";
5167 
5168  if (ctx->playingInfo)
5169  respDate = ctx->playingInfo->GetRecordingStartTime();
5170  }
5171 
5172  if ((infoStr == "Recorded") || (infoStr == "LiveTV"))
5173  {
5174  infoStr += QString(" %1 %2 %3 %4 %5 %6 %7")
5175  .arg(info.text["description"])
5176  .arg(speedStr)
5177  .arg(ctx->playingInfo != NULL ?
5178  ctx->playingInfo->GetChanID() : 0)
5179  .arg(respDate.toString(Qt::ISODate))
5180  .arg(fplay)
5181  .arg(ctx->buffer->GetFilename())
5182  .arg(rate);
5183  }
5184  else
5185  {
5186  QString position = info.text["description"].section(" ",0,0);
5187  infoStr += QString(" %1 %2 %3 %4 %5")
5188  .arg(position)
5189  .arg(speedStr)
5190  .arg(ctx->buffer->GetFilename())
5191  .arg(fplay)
5192  .arg(rate);
5193  }
5194 
5195  infoStr += QString(" Subtitles:");
5196 
5197  uint subtype = ctx->player->GetCaptionMode();
5198 
5199  if (subtype == kDisplayNone)
5200  infoStr += QString(" *0:[None]*");
5201  else
5202  infoStr += QString(" 0:[None]");
5203 
5204  uint n = 1;
5205 
5206  QStringList subs = ctx->player->GetTracks(kTrackTypeSubtitle);
5207  for (uint i = 0; i < (uint)subs.size(); i++)
5208  {
5209  if ((subtype & kDisplayAVSubtitle) &&
5210  (ctx->player->GetTrack(kTrackTypeSubtitle) == (int)i))
5211  {
5212  infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
5213  }
5214  else
5215  {
5216  infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
5217  }
5218  n++;
5219  }
5220 
5221  subs = ctx->player->GetTracks(kTrackTypeCC708);
5222  for (uint i = 0; i < (uint)subs.size(); i++)
5223  {
5224  if ((subtype & kDisplayCC708) &&
5225  (ctx->player->GetTrack(kTrackTypeCC708) == (int)i))
5226  {
5227  infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
5228  }
5229  else
5230  {
5231  infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
5232  }
5233  n++;
5234  }
5235 
5236  subs = ctx->player->GetTracks(kTrackTypeCC608);
5237  for (uint i = 0; i < (uint)subs.size(); i++)
5238  {
5239  if ((subtype & kDisplayCC608) &&
5240  (ctx->player->GetTrack(kTrackTypeCC608) == (int)i))
5241  {
5242  infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
5243  }
5244  else
5245  {
5246  infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
5247  }
5248  n++;
5249  }
5250 
5252  for (uint i = 0; i < (uint)subs.size(); i++)
5253  {
5254  if ((subtype & kDisplayTeletextCaptions) &&
5256  {
5257  infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
5258  }
5259  else
5260  {
5261  infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
5262  }
5263  n++;
5264  }
5265 
5266  subs = ctx->player->GetTracks(kTrackTypeTeletextMenu);
5267  for (uint i = 0; i < (uint)subs.size(); i++)
5268  {
5269  if ((subtype & kDisplayTeletextMenu) &&
5271  {
5272  infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
5273  }
5274  else
5275  {
5276  infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
5277  }
5278  n++;
5279  }
5280 
5281  subs = ctx->player->GetTracks(kTrackTypeRawText);
5282  for (uint i = 0; i < (uint)subs.size(); i++)
5283  {
5284  if ((subtype & kDisplayRawTextSubtitle) &&
5285  ctx->player->GetTrack(kTrackTypeRawText) == (int)i)
5286  {
5287  infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
5288  }
5289  else
5290  {
5291  infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
5292  }
5293  n++;
5294  }
5295 
5296  ctx->UnlockPlayingInfo(__FILE__, __LINE__);
5297 
5298  QString message = QString("NETWORK_CONTROL ANSWER %1")
5299  .arg(infoStr);
5300  MythEvent me(message);
5301  gCoreContext->dispatch(me);
5302  }
5303  else if (tokens[2] == "VOLUME")
5304  {
5305  QString infoStr = QString("%1%").arg(ctx->player->GetVolume());
5306 
5307  QString message = QString("NETWORK_CONTROL ANSWER %1")
5308  .arg(infoStr);
5309  MythEvent me(message);
5310  gCoreContext->dispatch(me);
5311  }
5312  }
5313 }
5314 
5319 bool TV::CreatePBP(PlayerContext *ctx, const ProgramInfo *info)
5320 {
5321  LOG(VB_PLAYBACK, LOG_INFO, LOC + "CreatePBP() -- begin");
5322 
5323  if (player.size() > 1)
5324  {
5325  LOG(VB_GENERAL, LOG_ERR, LOC + "CreatePBP() -- end : "
5326  "only allowed when player.size() == 1");
5327  return false;
5328  }
5329 
5330  PlayerContext *mctx = GetPlayer(ctx, 0);
5331  if (!IsPBPSupported(mctx))
5332  {
5333  LOG(VB_GENERAL, LOG_ERR, LOC + "CreatePBP() -- end : "
5334  "PBP not supported by video method.");
5335  return false;
5336  }
5337 
5338  if (!mctx->player)
5339  return false;
5340  mctx->LockDeletePlayer(__FILE__, __LINE__);
5341  long long mctx_frame = mctx->player->GetFramesPlayed();
5342  mctx->UnlockDeletePlayer(__FILE__, __LINE__);
5343 
5344  // This is safe because we are already holding lock for a ctx
5345  player.push_back(new PlayerContext(kPBPPlayerInUseID));
5346  PlayerContext *pbpctx = player.back();
5347  if (noHardwareDecoders)
5348  pbpctx->SetNoHardwareDecoders();
5349  pbpctx->SetPIPState(kPBPRight);
5350 
5351  if (info)
5352  {
5353  pbpctx->SetPlayingInfo(info);
5354  pbpctx->SetInitialTVState(false);
5355  ScheduleStateChange(pbpctx);
5356  }
5357  else if (RequestNextRecorder(pbpctx, false))
5358  {
5359  pbpctx->SetInitialTVState(true);
5360  ScheduleStateChange(pbpctx);
5361  }
5362  else
5363  {
5364  delete player.back();
5365  player.pop_back();
5366  return false;
5367  }
5368 
5369  mctx->PIPTeardown();
5370  mctx->SetPIPState(kPBPLeft);
5371  mctx->buffer->Seek(0, SEEK_SET);
5372 
5373  if (StateIsLiveTV(mctx->GetState()))
5374  mctx->buffer->Unpause();
5375 
5376  bool ok = mctx->CreatePlayer(
5377  this, GetMythMainWindow(), mctx->GetState(), false);
5378 
5379  if (ok)
5380  {
5381  ScheduleStateChange(mctx);
5382  mctx->LockDeletePlayer(__FILE__, __LINE__);
5383  if (mctx->player)
5384  mctx->player->JumpToFrame(mctx_frame);
5385  mctx->UnlockDeletePlayer(__FILE__, __LINE__);
5386  SetSpeedChangeTimer(25, __LINE__);
5387  }
5388  else
5389  {
5390  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to restart new main context");
5391  // Make putative PBP context the main context
5392  swap(player[0],player[1]);
5393  player[0]->SetPIPState(kPIPOff);
5394  // End the old main context..
5395  ForceNextStateNone(mctx);
5396  }
5397 
5398  LOG(VB_PLAYBACK, LOG_INFO, LOC +
5399  QString("CreatePBP() -- end : %1").arg(ok));
5400  return ok;
5401 }
5402 
5407 bool TV::CreatePIP(PlayerContext *ctx, const ProgramInfo *info)
5408 {
5409  PlayerContext *mctx = GetPlayer(ctx, 0);
5410  if (!mctx)
5411  return false;
5412 
5413  LOG(VB_PLAYBACK, LOG_INFO, LOC + "CreatePIP -- begin");
5414 
5415  if (mctx->IsPBP())
5416  {
5417  LOG(VB_GENERAL, LOG_ERR, LOC +
5418  "CreatePIP called, but we're in PBP mode already, ignoring.");
5419  return false;
5420  }
5421 
5422  if (!IsPIPSupported(mctx))
5423  {
5424  LOG(VB_GENERAL, LOG_ERR, LOC + "PiP not supported by video method.");
5425  return false;
5426  }
5427 
5429  if (noHardwareDecoders)
5430  pipctx->SetNoHardwareDecoders();
5431  pipctx->SetNullVideo(true);
5432  pipctx->SetPIPState(kPIPonTV);
5433  if (info)
5434  {
5435  pipctx->SetPlayingInfo(info);
5436  pipctx->SetInitialTVState(false);
5437  ScheduleStateChange(pipctx);
5438  }
5439  else if (RequestNextRecorder(pipctx, false))
5440  {
5441  pipctx->SetInitialTVState(true);
5442  ScheduleStateChange(pipctx);
5443  }
5444  else
5445  {
5446  delete pipctx;
5447  return false;
5448  }
5449 
5450  // this is safe because we are already holding lock for ctx
5451  player.push_back(pipctx);
5452 
5453  return true;
5454 }
5455 
5457 {
5458  for (uint i = 0; i < player.size(); i++)
5459  if (GetPlayer(ctx, i) == ctx)
5460  return i;
5461  return -1;
5462 }
5463 
5465  TVState desiredState)
5466 {
5467  bool wantPiP = ctx->IsPIP();
5468 
5469  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("StartPlayer(%1, %2, %3) -- begin")
5470  .arg(find_player_index(ctx)).arg(StateToString(desiredState))
5471  .arg((wantPiP) ? "PiP" : "main"));
5472 
5473  LOG(VB_PLAYBACK, LOG_INFO, LOC +
5474  QString("Elapsed time since TV constructor was called: %1 ms")
5475  .arg(ctorTime.elapsed()));
5476 
5477  if (wantPiP)
5478  {
5479  if (mctx->HasPlayer() && ctx->StartPIPPlayer(this, desiredState) &&
5480  ctx->HasPlayer() && PIPAddPlayer(mctx, ctx))
5481  {
5482  ScheduleStateChange(ctx);
5483  LOG(VB_GENERAL, LOG_INFO, "StartPlayer PiP -- end : ok");
5484  return true;
5485  }
5486 
5487  ForceNextStateNone(ctx);
5488  LOG(VB_GENERAL, LOG_INFO,