MythTV master
tv_play.cpp
Go to the documentation of this file.
1// Std
2#include <algorithm>
3#include <chrono>
4#include <cmath>
5#include <cstdarg>
6#include <cstdint>
7#include <cstdlib>
8#include <thread>
9
10// Qt
11#include <QApplication>
12#include <QDomDocument>
13#include <QDomElement>
14#include <QDomNode>
15#include <QEvent>
16#include <QFile>
17#include <QKeyEvent>
18#include <QRegularExpression>
19#include <QRunnable>
20#include <QTimerEvent>
21#include <utility>
22
23#include "libmythbase/mythconfig.h"
24
25// libmythbase
26#include "libmythbase/compat.h"
32#include "libmythbase/mythdb.h"
42
43// libmythui
51
52// libmythtv
53#include "Bluray/mythbdbuffer.h"
54#include "Bluray/mythbdplayer.h"
55#include "DVD/mythdvdbuffer.h"
56#include "DVD/mythdvdplayer.h"
57#include "cardutil.h"
58#include "channelutil.h"
60#include "io/mythmediabuffer.h"
61#include "jobqueue.h"
62#include "livetvchain.h"
63#include "mythplayerui.h"
64#include "mythsystemevent.h"
65#include "mythtvactionutils.h"
66#include "playercontext.h"
67#include "playgroup.h"
68#include "recordinginfo.h"
69#include "recordingrule.h"
70#include "remoteencoder.h"
71#include "signalmonitorvalue.h"
72#include "sourceutil.h"
73#include "tv_play.h"
74#include "tv_play_win.h"
75#include "tvremoteutil.h"
76#include "videometadatautil.h"
77
78#define DEBUG_CHANNEL_PREFIX 0
79#define DEBUG_ACTIONS 0
81#define LOC QString("TV::%1(): ").arg(__func__)
82
83static int comp_originalAirDate_rev(const ProgramInfo *a, const ProgramInfo *b)
84{
85 QDate dt1 = (a->GetOriginalAirDate().isValid()) ?
87 QDate dt2 = (b->GetOriginalAirDate().isValid()) ?
89
90 if (dt1 == dt2)
91 return (a->GetRecordingStartTime() >
92 b->GetRecordingStartTime() ? 1 : -1);
93 return (dt1 > dt2 ? 1 : -1);
94}
95
96static int comp_season_rev(const ProgramInfo *a, const ProgramInfo *b)
97{
98 if (a->GetSeason() == 0 || b->GetSeason() == 0)
99 return comp_originalAirDate_rev(a, b);
100 if (a->GetSeason() != b->GetSeason())
101 return (a->GetSeason() > b->GetSeason() ? 1 : -1);
102 if (a->GetEpisode() == 0 && b->GetEpisode() == 0)
103 return comp_originalAirDate_rev(a, b);
104 return (a->GetEpisode() > b->GetEpisode() ? 1 : -1);
105}
106
107static bool comp_title(const ProgramInfo *a, const ProgramInfo *b)
108{
110 if (cmp != 0)
111 return cmp < 0;
112 return comp_season_rev(a, b) < 0;
113}
114
119{
120 int count = 0;
121
123 query.prepare("SELECT COUNT(cardid) FROM capturecard;");
124 if (query.exec() && query.isActive() && query.size() && query.next())
125 count = query.value(0).toInt();
126
127 LOG(VB_RECORD, LOG_INFO,
128 "ConfiguredTunerCards() = " + QString::number(count));
129
130 return count;
131}
132
140TV* TV::AcquireRelease(int& RefCount, bool Acquire, bool Create /*=false*/)
141{
142 static QMutex s_lock;
143 static TV* s_tv = nullptr;
144 QMutexLocker locker(&s_lock);
145
146 if (Acquire)
147 {
148 if (!s_tv && Create)
149 s_tv = new TV(GetMythMainWindow());
150 else if (s_tv)
151 s_tv->IncrRef();
152 }
153 else
154 {
155 if (!s_tv)
156 LOG(VB_GENERAL, LOG_ERR, LOC + "Ref count error");
157 else
158 if (s_tv->DecrRef() == 0)
159 s_tv = nullptr;
160 }
161
162 if (s_tv)
163 RefCount = s_tv->m_referenceCount;
164 else
165 RefCount = 0;
166 return s_tv;
167}
168
174{
175 bool result = false;
176 int dummy = 0;
177 TV* tv = AcquireRelease(dummy, true);
178 if (tv)
179 {
180 result = true;
181 AcquireRelease(dummy, false);
182 }
183 return result;
184}
185
194{
195 return &m_playerContext;
196}
197
199{
201 {
202 LOG(VB_GENERAL, LOG_ERR, LOC + "Already have a player");
203 return false;
204 }
205
206 uint playerflags = kDecodeAllowGPU;
207 playerflags |= Muted ? kAudioMuted : kNoFlags;
208 auto flags = static_cast<PlayerFlags>(playerflags);
209
210 MythPlayerUI *player = nullptr;
212 player = new MythBDPlayer(m_mainWindow, this, &m_playerContext, flags);
213 else if (kState_WatchingDVD == State)
214 player = new MythDVDPlayer(m_mainWindow, this, &m_playerContext, flags);
215 else
216 player = new MythPlayerUI(m_mainWindow, this, &m_playerContext, flags);
217
219
220 bool isWatchingRecording = (State == kState_WatchingRecording);
221 player->SetWatchingRecording(isWatchingRecording);
222
225 m_player = player;
226 return StartPlaying(-1ms);
227}
228
234bool TV::StartPlaying(std::chrono::milliseconds MaxWait)
235{
236 if (!m_player)
237 return false;
238
239 if (!m_player->StartPlaying())
240 {
241 LOG(VB_GENERAL, LOG_ERR, LOC + "StartPlaying() Failed to start player");
242 // no need to call StopPlaying here as the player context will be deleted
243 // later following the error
244 return false;
245 }
246 MaxWait = (MaxWait <= 0ms) ? 20s : MaxWait;
247#if CONFIG_VALGRIND
248 MaxWait = std::chrono::milliseconds::max();
249#endif // CONFIG_VALGRIND
250 MythTimer t;
251 t.start();
252
253 while (!m_player->IsPlaying(50ms, true) && (t.elapsed() < MaxWait))
255
256 if (m_player->IsPlaying())
257 {
258 LOG(VB_PLAYBACK, LOG_INFO, LOC +
259 QString("StartPlaying(): took %1 ms to start player.")
260 .arg(t.elapsed().count()));
261 return true;
262 }
263 LOG(VB_GENERAL, LOG_ERR, LOC + "StartPlaying() Failed to start player");
265 return false;
266}
267
269{
271 PrepareToExitPlayer(__LINE__);
272 SetExitPlayer(true, true);
275}
276
287bool TV::StartTV(ProgramInfo* TVRec, uint Flags, const ChannelInfoList& Selection)
288{
289 int refs = 0;
290 TV* tv = AcquireRelease(refs, true, true);
291 // handle existing TV object atomically
292 if (refs > 1)
293 {
294 AcquireRelease(refs, false);
295 LOG(VB_GENERAL, LOG_WARNING, LOC + "Already have a TV object.");
297 return false;
298 }
299
300 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "-- begin");
301 bool inPlaylist = (Flags & kStartTVInPlayList) != 0U;
302 bool initByNetworkCommand = (Flags & kStartTVByNetworkCommand) != 0U;
303 bool quitAll = false;
304 bool showDialogs = true;
305 bool playCompleted = false;
306 ProgramInfo *curProgram = nullptr;
307 bool startSysEventSent = false;
308 bool startLivetvEventSent = false;
309
310 if (TVRec)
311 {
312 curProgram = new ProgramInfo(*TVRec);
313 curProgram->SetIgnoreBookmark((Flags & kStartTVIgnoreBookmark) != 0U);
314 curProgram->SetIgnoreProgStart((Flags & kStartTVIgnoreProgStart) != 0U);
315 curProgram->SetIgnoreLastPlayPos((Flags & kStartTVIgnoreLastPlayPos) != 0U);
316 }
317
318 // Initialize TV
319 if (!tv->Init())
320 {
321 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed initializing TV");
322 AcquireRelease(refs, false);
323 delete curProgram;
325 return false;
326 }
327
328 if (!lastProgramStringList.empty())
329 {
331 if (pginfo.HasPathname() || pginfo.GetChanID())
332 tv->SetLastProgram(&pginfo);
333 }
334
335 // Notify others that we are about to play
337
338 QString playerError;
339 while (!quitAll)
340 {
341 if (curProgram)
342 {
343 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "tv->Playback() -- begin");
344 if (!tv->Playback(*curProgram))
345 {
346 quitAll = true;
347 }
348 else if (!startSysEventSent)
349 {
350 startSysEventSent = true;
351 SendMythSystemPlayEvent("PLAY_STARTED", curProgram);
352 }
353
354 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "tv->Playback() -- end");
355 }
357 {
358 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "tv->LiveTV() -- begin");
359 if (!tv->LiveTV(showDialogs, Selection))
360 {
361 tv->SetExitPlayer(true, true);
362 quitAll = true;
363 }
364 else if (!startSysEventSent)
365 {
366 startSysEventSent = true;
367 startLivetvEventSent = true;
368 gCoreContext->SendSystemEvent("LIVETV_STARTED");
369 }
370
371 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "tv->LiveTV() -- end");
372 }
373 else
374 {
376 LOG(VB_GENERAL, LOG_ERR, LOC + "No tuners configured");
377 else
378 LOG(VB_GENERAL, LOG_ERR, LOC + "No tuners free for live tv");
379 quitAll = true;
380 continue;
381 }
382
383 tv->SetInPlayList(inPlaylist);
384 tv->setUnderNetworkControl(initByNetworkCommand);
385
387
388 // Process Events
389 LOG(VB_GENERAL, LOG_INFO, LOC + "Entering main playback loop.");
390 tv->PlaybackLoop();
391 LOG(VB_GENERAL, LOG_INFO, LOC + "Exiting main playback loop.");
392
393 if (tv->GetJumpToProgram())
394 {
395 ProgramInfo *nextProgram = tv->GetLastProgram();
396
397 tv->SetLastProgram(curProgram);
398 delete curProgram;
399 curProgram = nextProgram;
400
401 SendMythSystemPlayEvent("PLAY_CHANGED", curProgram);
402 continue;
403 }
404
405 tv->GetPlayerReadLock();
406 PlayerContext* context = tv->GetPlayerContext();
407 quitAll = tv->m_wantsToQuit || (context->m_errored);
408 context->LockDeletePlayer(__FILE__, __LINE__);
409 if (context->m_player && context->m_player->IsErrored())
410 playerError = context->m_player->GetError();
411 context->UnlockDeletePlayer(__FILE__, __LINE__);
412 tv->ReturnPlayerLock();
413 quitAll |= !playerError.isEmpty();
414 }
415
416 QCoreApplication::processEvents();
417
418 // check if the show has reached the end.
419 if (TVRec && tv->GetEndOfRecording())
420 playCompleted = true;
421
422 bool allowrerecord = tv->GetAllowRerecord();
423 bool deleterecording = tv->m_requestDelete;
424 AcquireRelease(refs, false);
427
428 if (curProgram)
429 {
430 if (startSysEventSent)
431 SendMythSystemPlayEvent("PLAY_STOPPED", curProgram);
432
433 if (deleterecording)
434 {
435 QStringList list;
436 list.push_back(QString::number(curProgram->GetRecordingID()));
437 list.push_back("0"); // do not force delete
438 list.push_back(allowrerecord ? "1" : "0");
439 MythEvent me("LOCAL_PBB_DELETE_RECORDINGS", list);
441 }
442 else if (curProgram->IsRecording())
443 {
444 lastProgramStringList.clear();
446 }
447
448 delete curProgram;
449 }
450 else if (startSysEventSent)
451 {
452 gCoreContext->SendSystemEvent("PLAY_STOPPED");
453 }
454
455 if (!playerError.isEmpty())
456 {
457 MythScreenStack *ss = GetMythMainWindow()->GetStack("popup stack");
458 auto *dlg = new MythConfirmationDialog(ss, playerError, false);
459 if (!dlg->Create())
460 delete dlg;
461 else
462 ss->AddScreen(dlg);
463 }
464
465 if (startLivetvEventSent)
466 gCoreContext->SendSystemEvent("LIVETV_ENDED");
467
468 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "-- end");
469
470 return playCompleted;
471}
472
477void TV::SetFuncPtr(const char* Name, void* Pointer)
478{
479 QString name(Name);
480 if (name == "playbackbox")
481 RunPlaybackBoxPtr = reinterpret_cast<EMBEDRETURNVOID>(Pointer);
482 else if (name == "viewscheduled")
483 RunViewScheduledPtr = reinterpret_cast<EMBEDRETURNVOID>(Pointer);
484 else if (name == "programguide")
485 RunProgramGuidePtr = reinterpret_cast<EMBEDRETURNVOIDEPG>(Pointer);
486 else if (name == "programfinder")
487 RunProgramFinderPtr = reinterpret_cast<EMBEDRETURNVOIDFINDER>(Pointer);
488 else if (name == "scheduleeditor")
489 RunScheduleEditorPtr = reinterpret_cast<EMBEDRETURNVOIDSCHEDIT>(Pointer);
490 else if (name == "programlist")
491 RunProgramListPtr = reinterpret_cast<EMBEDRETURNVOIDPROGLIST>(Pointer);
492}
493
495{
496 REG_KEY("TV Frontend", ACTION_PLAYBACK, QT_TRANSLATE_NOOP("MythControls",
497 "Play Program"), "P,Media Play");
498 REG_KEY("TV Frontend", ACTION_STOP, QT_TRANSLATE_NOOP("MythControls",
499 "Stop Program"), "");
500 REG_KEY("TV Frontend", ACTION_TOGGLERECORD, QT_TRANSLATE_NOOP("MythControls",
501 "Toggle recording status of current program"), "R");
502 REG_KEY("TV Frontend", ACTION_DAYLEFT, QT_TRANSLATE_NOOP("MythControls",
503 "Page the program guide back one day"), "Home,Media Previous");
504 REG_KEY("TV Frontend", ACTION_DAYRIGHT, QT_TRANSLATE_NOOP("MythControls",
505 "Page the program guide forward one day"), "End,Media Next");
506 REG_KEY("TV Frontend", ACTION_PAGELEFT, QT_TRANSLATE_NOOP("MythControls",
507 "Page the program guide left"), ",,<,Ctrl+B,Media Rewind");
508 REG_KEY("TV Frontend", ACTION_PAGERIGHT, QT_TRANSLATE_NOOP("MythControls",
509 "Page the program guide right"), ">,.,Ctrl+F,Media Fast Forward");
510 REG_KEY("TV Frontend", ACTION_TOGGLEFAV, QT_TRANSLATE_NOOP("MythControls",
511 "Toggle the current channel as a favorite"), "?");
512 REG_KEY("TV Frontend", ACTION_TOGGLEPGORDER, QT_TRANSLATE_NOOP("MythControls",
513 "Reverse the channel order in the program guide"), "");
514 REG_KEY("TV Frontend", ACTION_GUIDE, QT_TRANSLATE_NOOP("MythControls",
515 "Show the Program Guide"), "S");
516 REG_KEY("TV Frontend", ACTION_FINDER, QT_TRANSLATE_NOOP("MythControls",
517 "Show the Program Finder"), "#");
518 REG_KEY("TV Frontend", ACTION_CHANNELSEARCH, QT_TRANSLATE_NOOP("MythControls",
519 "Show the Channel Search"), "");
520 REG_KEY("TV Frontend", "NEXTFAV", QT_TRANSLATE_NOOP("MythControls",
521 "Cycle through channel groups and all channels in the "
522 "program guide."), "/");
523 REG_KEY("TV Frontend", "CHANUPDATE", QT_TRANSLATE_NOOP("MythControls",
524 "Switch channels without exiting guide in Live TV mode."), "X");
525 REG_KEY("TV Frontend", ACTION_VOLUMEDOWN, QT_TRANSLATE_NOOP("MythControls",
526 "Volume down"), "[,{,F10,Volume Down");
527 REG_KEY("TV Frontend", ACTION_VOLUMEUP, QT_TRANSLATE_NOOP("MythControls",
528 "Volume up"), "],},F11,Volume Up");
529 REG_KEY("TV Frontend", ACTION_MUTEAUDIO, QT_TRANSLATE_NOOP("MythControls",
530 "Mute"), "|,\\,F9,Volume Mute");
531 REG_KEY("TV Frontend", "CYCLEAUDIOCHAN", QT_TRANSLATE_NOOP("MythControls",
532 "Cycle audio channels"), "");
533 REG_KEY("TV Frontend", "RANKINC", QT_TRANSLATE_NOOP("MythControls",
534 "Increase program or channel rank"), "Right");
535 REG_KEY("TV Frontend", "RANKDEC", QT_TRANSLATE_NOOP("MythControls",
536 "Decrease program or channel rank"), "Left");
537 REG_KEY("TV Frontend", "UPCOMING", QT_TRANSLATE_NOOP("MythControls",
538 "List upcoming episodes"), "O");
539 REG_KEY("TV Frontend", ACTION_VIEWSCHEDULED, QT_TRANSLATE_NOOP("MythControls",
540 "List scheduled upcoming episodes"), "");
541 REG_KEY("TV Frontend", ACTION_PREVRECORDED, QT_TRANSLATE_NOOP("MythControls",
542 "List previously recorded episodes"), "");
543 REG_KEY("TV Frontend", "DETAILS", QT_TRANSLATE_NOOP("MythControls",
544 "Show details"), "U");
545 REG_KEY("TV Frontend", "VIEWINPUT", QT_TRANSLATE_NOOP("MythControls",
546 "Switch Recording Input view"), "C");
547 REG_KEY("TV Frontend", "CUSTOMEDIT", QT_TRANSLATE_NOOP("MythControls",
548 "Edit Custom Record Rule"), "");
549 REG_KEY("TV Frontend", "CHANGERECGROUP", QT_TRANSLATE_NOOP("MythControls",
550 "Change Recording Group"), "");
551 REG_KEY("TV Frontend", "CHANGEGROUPVIEW", QT_TRANSLATE_NOOP("MythControls",
552 "Change Group View"), "");
553 REG_KEY("TV Frontend", ACTION_LISTRECORDEDEPISODES, QT_TRANSLATE_NOOP("MythControls",
554 "List recorded episodes"), "");
555 /*
556 * TODO DB update needs to perform the necessary conversion and delete
557 * the following upgrade code and replace bkmKeys and togBkmKeys with "" in the
558 * REG_KEY for ACTION_SETBOOKMARK and ACTION_TOGGLEBOOKMARK.
559 */
560 // Bookmarks - Instead of SELECT to add or toggle,
561 // Use separate bookmark actions. This code is to convert users
562 // who may already be using SELECT. If they are not already using
563 // this frontend then nothing will be assigned to bookmark actions.
564 QString bkmKeys;
565 QString togBkmKeys;
566 // Check if this is a new frontend - if PAUSE returns
567 // "?" then frontend is new, never used before, so we will not assign
568 // any default bookmark keys
569 QString testKey = MythMainWindow::GetKey("TV Playback", ACTION_PAUSE);
570 if (testKey != "?")
571 {
572 int alternate = gCoreContext->GetNumSetting("AltClearSavedPosition",0);
573 QString selectKeys = MythMainWindow::GetKey("Global", ACTION_SELECT);
574 if (selectKeys != "?")
575 {
576 if (alternate)
577 togBkmKeys = selectKeys;
578 else
579 bkmKeys = selectKeys;
580 }
581 }
582 REG_KEY("TV Playback", ACTION_SETBOOKMARK, QT_TRANSLATE_NOOP("MythControls",
583 "Add Bookmark"), bkmKeys);
584 REG_KEY("TV Playback", ACTION_TOGGLEBOOKMARK, QT_TRANSLATE_NOOP("MythControls",
585 "Toggle Bookmark"), togBkmKeys);
586 REG_KEY("TV Playback", "BACK", QT_TRANSLATE_NOOP("MythControls",
587 "Exit or return to DVD menu"), "Esc,Back");
588 REG_KEY("TV Playback", ACTION_MENUCOMPACT, QT_TRANSLATE_NOOP("MythControls",
589 "Playback Compact Menu"), "Alt+M");
590 REG_KEY("TV Playback", ACTION_CLEAROSD, QT_TRANSLATE_NOOP("MythControls",
591 "Clear OSD"), "Backspace");
592 REG_KEY("TV Playback", ACTION_PAUSE, QT_TRANSLATE_NOOP("MythControls",
593 "Pause"), "P,Space,Media Play");
594 REG_KEY("TV Playback", ACTION_SEEKFFWD, QT_TRANSLATE_NOOP("MythControls",
595 "Fast Forward"), "Right");
596 REG_KEY("TV Playback", ACTION_SEEKRWND, QT_TRANSLATE_NOOP("MythControls",
597 "Rewind"), "Left");
598 REG_KEY("TV Playback", ACTION_SEEKARB, QT_TRANSLATE_NOOP("MythControls",
599 "Arbitrary Seek"), "*");
600 REG_KEY("TV Playback", ACTION_SEEKABSOLUTE, QT_TRANSLATE_NOOP("MythControls",
601 "Seek to a position in seconds"), "");
602 REG_KEY("TV Playback", ACTION_CHANNELUP, QT_TRANSLATE_NOOP("MythControls",
603 "Channel up"), "Up");
604 REG_KEY("TV Playback", ACTION_CHANNELDOWN, QT_TRANSLATE_NOOP("MythControls",
605 "Channel down"), "Down");
606 REG_KEY("TV Playback", "NEXTFAV", QT_TRANSLATE_NOOP("MythControls",
607 "Switch to the next favorite channel"), "/");
608 REG_KEY("TV Playback", "PREVCHAN", QT_TRANSLATE_NOOP("MythControls",
609 "Switch to the previous channel"), "H");
610 REG_KEY("TV Playback", ACTION_JUMPFFWD, QT_TRANSLATE_NOOP("MythControls",
611 "Jump ahead"), "PgDown");
612 REG_KEY("TV Playback", ACTION_JUMPRWND, QT_TRANSLATE_NOOP("MythControls",
613 "Jump back"), "PgUp");
614 REG_KEY("TV Playback", "INFOWITHCUTLIST", QT_TRANSLATE_NOOP("MythControls",
615 "Info utilizing cutlist"), "");
616 REG_KEY("TV Playback", ACTION_JUMPBKMRK, QT_TRANSLATE_NOOP("MythControls",
617 "Jump to bookmark"), "K");
618 REG_KEY("TV Playback", "FFWDSTICKY", QT_TRANSLATE_NOOP("MythControls",
619 "Fast Forward (Sticky) or Forward one second while paused"), ">,.,Ctrl+F,Media Fast Forward");
620 REG_KEY("TV Playback", "RWNDSTICKY", QT_TRANSLATE_NOOP("MythControls",
621 "Rewind (Sticky) or Rewind one second while paused"), ",,<,Ctrl+B,Media Rewind");
622 REG_KEY("TV Playback", "NEXTSOURCE", QT_TRANSLATE_NOOP("MythControls",
623 "Next Video Source"), "Y");
624 REG_KEY("TV Playback", "PREVSOURCE", QT_TRANSLATE_NOOP("MythControls",
625 "Previous Video Source"), "");
626 REG_KEY("TV Playback", "NEXTINPUT", QT_TRANSLATE_NOOP("MythControls",
627 "Next Input"), "C");
628 REG_KEY("TV Playback", "NEXTCARD", QT_TRANSLATE_NOOP("MythControls",
629 "Next Card"), "");
630 REG_KEY("TV Playback", "SKIPCOMMERCIAL", QT_TRANSLATE_NOOP("MythControls",
631 "Skip Commercial"), "Z,End,Media Next");
632 REG_KEY("TV Playback", "SKIPCOMMBACK", QT_TRANSLATE_NOOP("MythControls",
633 "Skip Commercial (Reverse)"), "Q,Home,Media Previous");
634 REG_KEY("TV Playback", ACTION_JUMPSTART, QT_TRANSLATE_NOOP("MythControls",
635 "Jump to the start of the recording."), "Ctrl+A");
636 REG_KEY("TV Playback", "TOGGLEBROWSE", QT_TRANSLATE_NOOP("MythControls",
637 "Toggle channel browse mode"), "O");
638 REG_KEY("TV Playback", ACTION_TOGGLERECORD, QT_TRANSLATE_NOOP("MythControls",
639 "Toggle recording status of current program"), "R");
640 REG_KEY("TV Playback", ACTION_TOGGLEFAV, QT_TRANSLATE_NOOP("MythControls",
641 "Toggle the current channel as a favorite"), "?");
642 REG_KEY("TV Playback", ACTION_VOLUMEDOWN, QT_TRANSLATE_NOOP("MythControls",
643 "Volume down"), "[,{,F10,Volume Down");
644 REG_KEY("TV Playback", ACTION_VOLUMEUP, QT_TRANSLATE_NOOP("MythControls",
645 "Volume up"), "],},F11,Volume Up");
646 REG_KEY("TV Playback", ACTION_MUTEAUDIO, QT_TRANSLATE_NOOP("MythControls",
647 "Mute"), "|,\\,F9,Volume Mute");
648 REG_KEY("TV Playback", ACTION_SETVOLUME, QT_TRANSLATE_NOOP("MythControls",
649 "Set the volume"), "");
650 REG_KEY("TV Playback", "CYCLEAUDIOCHAN", QT_TRANSLATE_NOOP("MythControls",
651 "Cycle audio channels"), "");
652 REG_KEY("TV Playback", ACTION_TOGGLEUPMIX, QT_TRANSLATE_NOOP("MythControls",
653 "Toggle audio upmixer"), "Ctrl+U");
654 REG_KEY("TV Playback", ACTION_BOTTOMLINEMOVE,
655 QT_TRANSLATE_NOOP("MythControls", "Move BottomLine off screen"), "L");
656 REG_KEY("TV Playback", ACTION_BOTTOMLINESAVE,
657 QT_TRANSLATE_NOOP("MythControls", "Save manual zoom for BottomLine"), "");
658 REG_KEY("TV Playback", "TOGGLEASPECT", QT_TRANSLATE_NOOP("MythControls",
659 "Toggle the video aspect ratio"), "Ctrl+W");
660 REG_KEY("TV Playback", "TOGGLEFILL", QT_TRANSLATE_NOOP("MythControls",
661 "Next Preconfigured Zoom mode"), "W");
662 REG_KEY("TV Playback", ACTION_TOGGLESUBS, QT_TRANSLATE_NOOP("MythControls",
663 "Toggle any captions"), "T");
664 REG_KEY("TV Playback", ACTION_ENABLESUBS, QT_TRANSLATE_NOOP("MythControls",
665 "Enable any captions"), "");
666 REG_KEY("TV Playback", ACTION_DISABLESUBS, QT_TRANSLATE_NOOP("MythControls",
667 "Disable any captions"), "");
668 REG_KEY("TV Playback", "TOGGLETTC", QT_TRANSLATE_NOOP("MythControls",
669 "Toggle Teletext Captions"),"");
670 REG_KEY("TV Playback", "TOGGLESUBTITLE", QT_TRANSLATE_NOOP("MythControls",
671 "Toggle Subtitles"), "");
672 REG_KEY("TV Playback", "TOGGLECC608", QT_TRANSLATE_NOOP("MythControls",
673 "Toggle VBI CC"), "");
674 REG_KEY("TV Playback", "TOGGLECC708", QT_TRANSLATE_NOOP("MythControls",
675 "Toggle ATSC CC"), "");
676 REG_KEY("TV Playback", "TOGGLETTM", QT_TRANSLATE_NOOP("MythControls",
677 "Toggle Teletext Menu"), "");
678 REG_KEY("TV Playback", ACTION_TOGGLEEXTTEXT, QT_TRANSLATE_NOOP("MythControls",
679 "Toggle External Subtitles"), "");
680 REG_KEY("TV Playback", ACTION_ENABLEEXTTEXT, QT_TRANSLATE_NOOP("MythControls",
681 "Enable External Subtitles"), "");
682 REG_KEY("TV Playback", ACTION_DISABLEEXTTEXT, QT_TRANSLATE_NOOP("MythControls",
683 "Disable External Subtitles"), "");
684 REG_KEY("TV Playback", "TOGGLERAWTEXT", QT_TRANSLATE_NOOP("MythControls",
685 "Toggle Text Subtitles"), "");
686
687 REG_KEY("TV Playback", "SELECTAUDIO_0", QT_TRANSLATE_NOOP("MythControls",
688 "Play audio track 1"), "");
689 REG_KEY("TV Playback", "SELECTAUDIO_1", QT_TRANSLATE_NOOP("MythControls",
690 "Play audio track 2"), "");
691 REG_KEY("TV Playback", "SELECTSUBTITLE_0",QT_TRANSLATE_NOOP("MythControls",
692 "Display subtitle 1"), "");
693 REG_KEY("TV Playback", "SELECTSUBTITLE_1",QT_TRANSLATE_NOOP("MythControls",
694 "Display subtitle 2"), "");
695 REG_KEY("TV Playback", "SELECTRAWTEXT_0",QT_TRANSLATE_NOOP("MythControls",
696 "Display Text Subtitle 1"), "");
697 REG_KEY("TV Playback", "SELECTCC608_0", QT_TRANSLATE_NOOP("MythControls",
698 "Display VBI CC1"), "");
699 REG_KEY("TV Playback", "SELECTCC608_1", QT_TRANSLATE_NOOP("MythControls",
700 "Display VBI CC2"), "");
701 REG_KEY("TV Playback", "SELECTCC608_2", QT_TRANSLATE_NOOP("MythControls",
702 "Display VBI CC3"), "");
703 REG_KEY("TV Playback", "SELECTCC608_3", QT_TRANSLATE_NOOP("MythControls",
704 "Display VBI CC4"), "");
705 REG_KEY("TV Playback", "SELECTCC708_0", QT_TRANSLATE_NOOP("MythControls",
706 "Display ATSC CC1"), "");
707 REG_KEY("TV Playback", "SELECTCC708_1", QT_TRANSLATE_NOOP("MythControls",
708 "Display ATSC CC2"), "");
709 REG_KEY("TV Playback", "SELECTCC708_2", QT_TRANSLATE_NOOP("MythControls",
710 "Display ATSC CC3"), "");
711 REG_KEY("TV Playback", "SELECTCC708_3", QT_TRANSLATE_NOOP("MythControls",
712 "Display ATSC CC4"), "");
713 REG_KEY("TV Playback", ACTION_ENABLEFORCEDSUBS, QT_TRANSLATE_NOOP("MythControls",
714 "Enable Forced Subtitles"), "");
715 REG_KEY("TV Playback", ACTION_DISABLEFORCEDSUBS, QT_TRANSLATE_NOOP("MythControls",
716 "Disable Forced Subtitles"), "");
717
718 REG_KEY("TV Playback", "NEXTAUDIO", QT_TRANSLATE_NOOP("MythControls",
719 "Next audio track"), "+");
720 REG_KEY("TV Playback", "PREVAUDIO", QT_TRANSLATE_NOOP("MythControls",
721 "Previous audio track"), "-");
722 REG_KEY("TV Playback", "NEXTSUBTITLE", QT_TRANSLATE_NOOP("MythControls",
723 "Next subtitle track"), "");
724 REG_KEY("TV Playback", "PREVSUBTITLE", QT_TRANSLATE_NOOP("MythControls",
725 "Previous subtitle track"), "");
726 REG_KEY("TV Playback", "NEXTRAWTEXT", QT_TRANSLATE_NOOP("MythControls",
727 "Next Text track"), "");
728 REG_KEY("TV Playback", "PREVRAWTEXT", QT_TRANSLATE_NOOP("MythControls",
729 "Previous Text track"), "");
730 REG_KEY("TV Playback", "NEXTCC608", QT_TRANSLATE_NOOP("MythControls",
731 "Next VBI CC track"), "");
732 REG_KEY("TV Playback", "PREVCC608", QT_TRANSLATE_NOOP("MythControls",
733 "Previous VBI CC track"), "");
734 REG_KEY("TV Playback", "NEXTCC708", QT_TRANSLATE_NOOP("MythControls",
735 "Next ATSC CC track"), "");
736 REG_KEY("TV Playback", "PREVCC708", QT_TRANSLATE_NOOP("MythControls",
737 "Previous ATSC CC track"), "");
738 REG_KEY("TV Playback", "NEXTCC", QT_TRANSLATE_NOOP("MythControls",
739 "Next of any captions"), "");
740
741 REG_KEY("TV Playback", "NEXTSCAN", QT_TRANSLATE_NOOP("MythControls",
742 "Next video scan overidemode"), "");
743 REG_KEY("TV Playback", "QUEUETRANSCODE", QT_TRANSLATE_NOOP("MythControls",
744 "Queue the current recording for transcoding"), "X");
745 REG_KEY("TV Playback", "SPEEDINC", QT_TRANSLATE_NOOP("MythControls",
746 "Increase the playback speed"), "U");
747 REG_KEY("TV Playback", "SPEEDDEC", QT_TRANSLATE_NOOP("MythControls",
748 "Decrease the playback speed"), "J");
749 REG_KEY("TV Playback", "ADJUSTSTRETCH", QT_TRANSLATE_NOOP("MythControls",
750 "Turn on time stretch control"), "A");
751 REG_KEY("TV Playback", "STRETCHINC", QT_TRANSLATE_NOOP("MythControls",
752 "Increase time stretch speed"), "");
753 REG_KEY("TV Playback", "STRETCHDEC", QT_TRANSLATE_NOOP("MythControls",
754 "Decrease time stretch speed"), "");
755 REG_KEY("TV Playback", "TOGGLESTRETCH", QT_TRANSLATE_NOOP("MythControls",
756 "Toggle time stretch speed"), "");
757 REG_KEY("TV Playback", ACTION_TOGGELAUDIOSYNC,
758 QT_TRANSLATE_NOOP("MythControls",
759 "Turn on audio sync adjustment controls"), "");
760 REG_KEY("TV Playback", ACTION_SETAUDIOSYNC,
761 QT_TRANSLATE_NOOP("MythControls",
762 "Set the audio sync adjustment"), "");
763 REG_KEY("TV Playback", "TOGGLEPICCONTROLS",
764 QT_TRANSLATE_NOOP("MythControls", "Playback picture adjustments"),
765 "F");
766 REG_KEY("TV Playback", ACTION_SETBRIGHTNESS,
767 QT_TRANSLATE_NOOP("MythControls", "Set the picture brightness"), "");
768 REG_KEY("TV Playback", ACTION_SETCONTRAST,
769 QT_TRANSLATE_NOOP("MythControls", "Set the picture contrast"), "");
770 REG_KEY("TV Playback", ACTION_SETCOLOUR,
771 QT_TRANSLATE_NOOP("MythControls", "Set the picture color"), "");
772 REG_KEY("TV Playback", ACTION_SETHUE,
773 QT_TRANSLATE_NOOP("MythControls", "Set the picture hue"), "");
774 REG_KEY("TV Playback", ACTION_TOGGLECHANCONTROLS,
775 QT_TRANSLATE_NOOP("MythControls", "Recording picture adjustments "
776 "for this channel"), "Ctrl+G");
777 REG_KEY("TV Playback", ACTION_TOGGLERECCONTROLS,
778 QT_TRANSLATE_NOOP("MythControls", "Recording picture adjustments "
779 "for this recorder"), "G");
780 REG_KEY("TV Playback", "CYCLECOMMSKIPMODE",
781 QT_TRANSLATE_NOOP("MythControls", "Cycle Commercial Skip mode"),
782 "");
783 REG_KEY("TV Playback", ACTION_GUIDE, QT_TRANSLATE_NOOP("MythControls",
784 "Show the Program Guide"), "S");
785 REG_KEY("TV Playback", ACTION_FINDER, QT_TRANSLATE_NOOP("MythControls",
786 "Show the Program Finder"), "#");
787 REG_KEY("TV Playback", ACTION_TOGGLESLEEP, QT_TRANSLATE_NOOP("MythControls",
788 "Toggle the Sleep Timer"), "F8");
789 REG_KEY("TV Playback", ACTION_PLAY, QT_TRANSLATE_NOOP("MythControls", "Play"),
790 "Ctrl+P");
791 REG_KEY("TV Playback", ACTION_JUMPPREV, QT_TRANSLATE_NOOP("MythControls",
792 "Jump to previously played recording"), "");
793 REG_KEY("TV Playback", ACTION_JUMPREC, QT_TRANSLATE_NOOP("MythControls",
794 "Display menu of recorded programs to jump to"), "");
795 REG_KEY("TV Playback", ACTION_VIEWSCHEDULED, QT_TRANSLATE_NOOP("MythControls",
796 "Display scheduled recording list"), "");
797 REG_KEY("TV Playback", ACTION_PREVRECORDED, QT_TRANSLATE_NOOP("MythControls",
798 "Display previously recorded episodes"), "");
799 REG_KEY("TV Playback", ACTION_SIGNALMON, QT_TRANSLATE_NOOP("MythControls",
800 "Monitor Signal Quality"), "Alt+F7");
801 REG_KEY("TV Playback", ACTION_JUMPTODVDROOTMENU,
802 QT_TRANSLATE_NOOP("MythControls", "Jump to the DVD Root Menu"), "");
803 REG_KEY("TV Playback", ACTION_JUMPTOPOPUPMENU,
804 QT_TRANSLATE_NOOP("MythControls", "Jump to the Popup Menu"), "");
806 QT_TRANSLATE_NOOP("MythControls", "Jump to the DVD Chapter Menu"), "");
807 REG_KEY("TV Playback", ACTION_JUMPTODVDTITLEMENU,
808 QT_TRANSLATE_NOOP("MythControls", "Jump to the DVD Title Menu"), "");
809 REG_KEY("TV Playback", ACTION_EXITSHOWNOPROMPTS,
810 QT_TRANSLATE_NOOP("MythControls", "Exit Show without any prompts"),
811 "");
812 REG_KEY("TV Playback", ACTION_JUMPCHAPTER, QT_TRANSLATE_NOOP("MythControls",
813 "Jump to a chapter"), "");
814 REG_KEY("TV Playback", ACTION_SWITCHTITLE, QT_TRANSLATE_NOOP("MythControls",
815 "Switch title"), "");
816 REG_KEY("TV Playback", ACTION_SWITCHANGLE, QT_TRANSLATE_NOOP("MythControls",
817 "Switch angle"), "");
818 REG_KEY("TV Playback", ACTION_OSDNAVIGATION, QT_TRANSLATE_NOOP("MythControls",
819 "OSD Navigation"), "");
820 REG_KEY("TV Playback", ACTION_ZOOMUP, QT_TRANSLATE_NOOP("MythControls",
821 "Zoom mode - shift up"), "");
822 REG_KEY("TV Playback", ACTION_ZOOMDOWN, QT_TRANSLATE_NOOP("MythControls",
823 "Zoom mode - shift down"), "");
824 REG_KEY("TV Playback", ACTION_ZOOMLEFT, QT_TRANSLATE_NOOP("MythControls",
825 "Zoom mode - shift left"), "");
826 REG_KEY("TV Playback", ACTION_ZOOMRIGHT, QT_TRANSLATE_NOOP("MythControls",
827 "Zoom mode - shift right"), "");
828 REG_KEY("TV Playback", ACTION_ZOOMASPECTUP,
829 QT_TRANSLATE_NOOP("MythControls",
830 "Zoom mode - increase aspect ratio"), "3");
831 REG_KEY("TV Playback", ACTION_ZOOMASPECTDOWN,
832 QT_TRANSLATE_NOOP("MythControls",
833 "Zoom mode - decrease aspect ratio"), "7");
834 REG_KEY("TV Playback", ACTION_ZOOMIN, QT_TRANSLATE_NOOP("MythControls",
835 "Zoom mode - zoom in"), "9");
836 REG_KEY("TV Playback", ACTION_ZOOMOUT, QT_TRANSLATE_NOOP("MythControls",
837 "Zoom mode - zoom out"), "1");
838 REG_KEY("TV Playback", ACTION_ZOOMVERTICALIN,
839 QT_TRANSLATE_NOOP("MythControls",
840 "Zoom mode - vertical zoom in"), "8");
841 REG_KEY("TV Playback", ACTION_ZOOMVERTICALOUT,
842 QT_TRANSLATE_NOOP("MythControls",
843 "Zoom mode - vertical zoom out"), "2");
844 REG_KEY("TV Playback", ACTION_ZOOMHORIZONTALIN,
845 QT_TRANSLATE_NOOP("MythControls",
846 "Zoom mode - horizontal zoom in"), "6");
847 REG_KEY("TV Playback", ACTION_ZOOMHORIZONTALOUT,
848 QT_TRANSLATE_NOOP("MythControls",
849 "Zoom mode - horizontal zoom out"), "4");
850 REG_KEY("TV Playback", ACTION_ZOOMQUIT, QT_TRANSLATE_NOOP("MythControls",
851 "Zoom mode - quit and abandon changes"), "");
852 REG_KEY("TV Playback", ACTION_ZOOMCOMMIT, QT_TRANSLATE_NOOP("MythControls",
853 "Zoom mode - commit changes"), "");
854
855 REG_KEY("TV Playback", ACTION_CAST, QT_TRANSLATE_NOOP("MythControls",
856 "Display list of cast members"), "");
857
858 /* Interactive Television keys */
859 REG_KEY("TV Playback", ACTION_MENURED, QT_TRANSLATE_NOOP("MythControls",
860 "Menu Red"), "F2");
861 REG_KEY("TV Playback", ACTION_MENUGREEN, QT_TRANSLATE_NOOP("MythControls",
862 "Menu Green"), "F3");
863 REG_KEY("TV Playback", ACTION_MENUYELLOW, QT_TRANSLATE_NOOP("MythControls",
864 "Menu Yellow"), "F4");
865 REG_KEY("TV Playback", ACTION_MENUBLUE, QT_TRANSLATE_NOOP("MythControls",
866 "Menu Blue"), "F5");
867 REG_KEY("TV Playback", ACTION_TEXTEXIT, QT_TRANSLATE_NOOP("MythControls",
868 "Menu Exit"), "F6");
869 REG_KEY("TV Playback", ACTION_MENUTEXT, QT_TRANSLATE_NOOP("MythControls",
870 "Menu Text"), "F7");
871 REG_KEY("TV Playback", ACTION_MENUEPG, QT_TRANSLATE_NOOP("MythControls",
872 "Menu EPG"), "F12");
873
874 /* Editing keys */
875 REG_KEY("TV Editing", ACTION_CLEARMAP, QT_TRANSLATE_NOOP("MythControls",
876 "Clear editing cut points"), "C,Q,Home");
877 REG_KEY("TV Editing", ACTION_INVERTMAP, QT_TRANSLATE_NOOP("MythControls",
878 "Invert Begin/End cut points"),"I,Home Page");
879 REG_KEY("TV Editing", ACTION_SAVEMAP, QT_TRANSLATE_NOOP("MythControls",
880 "Save cuts"),"");
881 REG_KEY("TV Editing", ACTION_LOADCOMMSKIP,QT_TRANSLATE_NOOP("MythControls",
882 "Load cuts from detected commercials"), "Z,End");
883 REG_KEY("TV Editing", ACTION_NEXTCUT, QT_TRANSLATE_NOOP("MythControls",
884 "Jump to the next cut point"), "PgDown,Media Next");
885 REG_KEY("TV Editing", ACTION_PREVCUT, QT_TRANSLATE_NOOP("MythControls",
886 "Jump to the previous cut point"), "PgUp,Media Previous");
887 REG_KEY("TV Editing", ACTION_BIGJUMPREW, QT_TRANSLATE_NOOP("MythControls",
888 "Jump back 10x the normal amount"), ",,<,Ctrl+B,Media Rewind");
889 REG_KEY("TV Editing", ACTION_BIGJUMPFWD, QT_TRANSLATE_NOOP("MythControls",
890 "Jump forward 10x the normal amount"), ">,.,Ctrl+F,Media Fast Forward");
891 REG_KEY("TV Editing", ACTION_MENUCOMPACT, QT_TRANSLATE_NOOP("MythControls",
892 "Cut point editor compact menu"), "Alt+M");
893
894 /* Teletext keys */
895 REG_KEY("Teletext Menu", ACTION_NEXTPAGE, QT_TRANSLATE_NOOP("MythControls",
896 "Next Page"), "Down");
897 REG_KEY("Teletext Menu", ACTION_PREVPAGE, QT_TRANSLATE_NOOP("MythControls",
898 "Previous Page"), "Up");
899 REG_KEY("Teletext Menu", ACTION_NEXTSUBPAGE, QT_TRANSLATE_NOOP("MythControls",
900 "Next Subpage"), "Right");
901 REG_KEY("Teletext Menu", ACTION_PREVSUBPAGE, QT_TRANSLATE_NOOP("MythControls",
902 "Previous Subpage"), "Left");
903 REG_KEY("Teletext Menu", ACTION_TOGGLETT, QT_TRANSLATE_NOOP("MythControls",
904 "Toggle Teletext"), "T");
905 REG_KEY("Teletext Menu", ACTION_MENURED, QT_TRANSLATE_NOOP("MythControls",
906 "Menu Red"), "F2");
907 REG_KEY("Teletext Menu", ACTION_MENUGREEN, QT_TRANSLATE_NOOP("MythControls",
908 "Menu Green"), "F3");
909 REG_KEY("Teletext Menu", ACTION_MENUYELLOW, QT_TRANSLATE_NOOP("MythControls",
910 "Menu Yellow"), "F4");
911 REG_KEY("Teletext Menu", ACTION_MENUBLUE, QT_TRANSLATE_NOOP("MythControls",
912 "Menu Blue"), "F5");
913 REG_KEY("Teletext Menu", ACTION_MENUWHITE, QT_TRANSLATE_NOOP("MythControls",
914 "Menu White"), "F6");
915 REG_KEY("Teletext Menu", ACTION_TOGGLEBACKGROUND,
916 QT_TRANSLATE_NOOP("MythControls", "Toggle Background"), "F7");
917 REG_KEY("Teletext Menu", ACTION_REVEAL, QT_TRANSLATE_NOOP("MythControls",
918 "Reveal hidden Text"), "F8");
919
920 /* Visualisations */
921 REG_KEY("TV Playback", ACTION_TOGGLEVISUALISATION,
922 QT_TRANSLATE_NOOP("MythControls", "Toggle audio visualisation"), "");
923
924 /* OSD playback information screen */
925 REG_KEY("TV Playback", ACTION_TOGGLEOSDDEBUG,
926 QT_TRANSLATE_NOOP("MythControls", "Toggle OSD playback information"), "");
927
928 /* 3D/Frame compatible/Stereoscopic TV */
929 REG_KEY("TV Playback", ACTION_3DNONE,
930 QT_TRANSLATE_NOOP("MythControls", "Auto 3D"), "");
931 REG_KEY("TV Playback", ACTION_3DIGNORE,
932 QT_TRANSLATE_NOOP("MythControls", "Ignore 3D"), "");
933 REG_KEY("TV Playback", ACTION_3DSIDEBYSIDEDISCARD,
934 QT_TRANSLATE_NOOP("MythControls", "Discard 3D Side by Side"), "");
936 QT_TRANSLATE_NOOP("MythControls", "Discard 3D Top and Bottom"), "");
937
938/*
939 keys already used:
940
941 Global: I M 0123456789
942 Playback: ABCDEFGH JK NOPQRSTUVWXYZ
943 Frontend: CD OP R U XY 01 3 7 9
944 Editing: C E I Q Z
945 Teletext: T
946
947 Playback: <>,.?/|[]{}\+-*#^
948 Frontend: <>,.?/
949 Editing: <>,.
950
951 Global: PgDown, PgUp, Right, Left, Home, End, Up, Down,
952 Playback: PgDown, PgUp, Right, Left, Home, End, Up, Down, Backspace,
953 Frontend: Right, Left, Home, End
954 Editing: PgDown, PgUp, Home, End
955 Teletext: Right, Left, Up, Down,
956
957 Global: Return, Enter, Space, Esc
958
959 Global: F1,
960 Playback: F7,F8,F9,F10,F11
961 Teletext F2,F3,F4,F5,F6,F7,F8
962 ITV F2,F3,F4,F5,F6,F7,F12
963
964 Playback: Ctrl-B,Ctrl-G,Ctrl-Y,Ctrl-U,L
965*/
966}
967
969{
970 m_mainWindow->ClearKeyContext("TV Frontend");
971 m_mainWindow->ClearKeyContext("TV Playback");
972 m_mainWindow->ClearKeyContext("TV Editing");
973 m_mainWindow->ClearKeyContext("Teletext Menu");
974 InitKeys();
975}
976
977
979{
980 public:
981 SleepTimerInfo(QString String, std::chrono::milliseconds MilliSeconds)
982 : dispString(std::move(String)),
983 milliseconds(MilliSeconds) {}
984 QString dispString;
985 std::chrono::milliseconds milliseconds;
986};
987
988const std::vector<TV::SleepTimerInfo> TV::s_sleepTimes =
989{
990 { tr("Off", "Sleep timer"), 0min },
991 { tr("30m", "Sleep timer"), 30min },
992 { tr("1h", "Sleep timer"), 60min },
993 { tr("1h30m", "Sleep timer"), 90min },
994 { tr("2h", "Sleep timer"), 120min }
995};
996
1009 : ReferenceCounter("TV"),
1010 TVBrowseHelper(this),
1011 m_mainWindow(MainWindow),
1012 m_posThreadPool(new MThreadPool("PosSaverPool"))
1013
1014{
1015 LOG(VB_GENERAL, LOG_INFO, LOC + "Creating TV object");
1016
1017 QObject::setObjectName("TV");
1019 connect(this, &TV::RequestEmbedding, this, &TV::Embed);
1020 InitFromDB();
1021
1022#ifdef Q_OS_ANDROID
1023 connect(qApp, &QApplication::applicationStateChanged, this, &TV::onApplicationStateChange);
1024#endif
1025
1026 if (m_mainWindow)
1028
1029 // Setup various state signals
1030 connect(this, &TV::ChangeAudioOffset, [&]() { m_audiosyncAdjustment = true; });
1031 connect(this, &TV::AdjustSubtitleZoom, [&]() { m_subtitleZoomAdjustment = true; });
1032 connect(this, &TV::AdjustSubtitleDelay, [&]() { m_subtitleDelayAdjustment = true; });
1033
1034 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Finished creating TV object");
1035}
1036
1038{
1039 QMap<QString,QString> kv;
1040 kv["LiveTVIdleTimeout"] = "0";
1041 kv["BrowseMaxForward"] = "240";
1042 kv["PlaybackExitPrompt"] = "0";
1043 kv["AutomaticSetWatched"] = "0";
1044 kv["EndOfRecordingExitPrompt"] = "0";
1045 kv["JumpToProgramOSD"] = "1";
1046 kv["GuiSizeForTV"] = "0";
1047 kv["UseVideoModes"] = "0";
1048 kv["JobsRunOnRecordHost"] = "0";
1049 kv["ContinueEmbeddedTVPlay"] = "0";
1050 kv["UseFixedWindowSize"] = "1";
1051 kv["RunFrontendInWindow"] = "0";
1052 kv["PersistentBrowseMode"] = "0";
1053 kv["BrowseAllTuners"] = "0";
1054 kv["ChannelOrdering"] = "channum";
1055
1056 kv["CustomFilters"] = "";
1057 kv["ChannelFormat"] = "<num> <sign>";
1058
1059 kv["TryUnflaggedSkip"] = "0";
1060
1061 kv["ChannelGroupDefault"] = "-1";
1062 kv["BrowseChannelGroup"] = "0";
1063 kv["SmartForward"] = "0";
1064 kv["FFRewReposTime"] = "100";
1065 kv["FFRewReverse"] = "1";
1066
1067 kv["BrowseChannelGroup"] = "0";
1068 kv["ChannelGroupDefault"] = "-1";
1069 kv["ChannelGroupRememberLast"] = "0";
1070
1071 kv["VbiFormat"] = "";
1072 kv["DecodeVBIFormat"] = "";
1073
1074 // these need exactly 12 items, comma cant be used as it is the delimiter
1075 kv["PlaybackScreenPressKeyMap"] = "P,Up,Z,],Left,Return,Return,Right,A,Down,Q,[";
1076 kv["LiveTVScreenPressKeyMap"] = "P,Up,Z,S,Left,Return,Return,Right,A,Down,Q,F";
1077
1078 constexpr std::array<const int,8> ff_rew_def { 3, 5, 10, 20, 30, 60, 120, 180 };
1079 for (size_t i = 0; i < ff_rew_def.size(); i++)
1080 kv[QString("FFRewSpeed%1").arg(i)] = QString::number(ff_rew_def[i]);
1081
1082 MythDB::getMythDB()->GetSettings(kv);
1083
1084 m_screenPressKeyMapPlayback = ConvertScreenPressKeyMap(kv["PlaybackScreenPressKeyMap"]);
1085 m_screenPressKeyMapLiveTV = ConvertScreenPressKeyMap(kv["LiveTVScreenPressKeyMap"]);
1086
1087 QString db_channel_ordering;
1088
1089 m_dbIdleTimeout = std::chrono::minutes(kv["LiveTVIdleTimeout"].toUInt());
1090 auto db_browse_max_forward = std::chrono::minutes(kv["BrowseMaxForward"].toUInt());
1091 m_dbPlaybackExitPrompt = kv["PlaybackExitPrompt"].toInt();
1092 m_dbAutoSetWatched = (kv["AutomaticSetWatched"].toInt() != 0);
1093 m_dbEndOfRecExitPrompt = (kv["EndOfRecordingExitPrompt"].toInt() != 0);
1094 m_dbJumpPreferOsd = (kv["JumpToProgramOSD"].toInt() != 0);
1095 m_dbUseGuiSizeForTv = (kv["GuiSizeForTV"].toInt() != 0);
1096 m_dbUseVideoModes = (kv["UseVideoModes"].toInt() != 0);
1097 m_dbRunJobsOnRemote = (kv["JobsRunOnRecordHost"].toInt() != 0);
1098 m_dbContinueEmbedded = (kv["ContinueEmbeddedTVPlay"].toInt() != 0);
1099 m_dbBrowseAlways = (kv["PersistentBrowseMode"].toInt() != 0);
1100 m_dbBrowseAllTuners = (kv["BrowseAllTuners"].toInt() != 0);
1101 db_channel_ordering = kv["ChannelOrdering"];
1102 m_dbChannelFormat = kv["ChannelFormat"];
1103 m_smartForward = (kv["SmartForward"].toInt() != 0);
1104 m_ffRewRepos = kv["FFRewReposTime"].toFloat() * 0.01F;
1105 m_ffRewReverse = (kv["FFRewReverse"].toInt() != 0);
1106
1107 m_dbUseChannelGroups = (kv["BrowseChannelGroup"].toInt() != 0);
1108 m_dbRememberLastChannelGroup = (kv["ChannelGroupRememberLast"].toInt() != 0);
1109 m_channelGroupId = kv["ChannelGroupDefault"].toInt();
1110
1111 QString beVBI = kv["VbiFormat"];
1112 QString feVBI = kv["DecodeVBIFormat"];
1113
1114 RecordingRule record;
1115 record.LoadTemplate("Default");
1116 m_dbAutoexpireDefault = static_cast<uint>(record.m_autoExpire);
1117
1119 {
1121 if (m_channelGroupId > -1)
1122 {
1123 m_channelGroupChannelList = ChannelUtil::GetChannels(0, true, "channum, callsign",
1124 static_cast<uint>(m_channelGroupId));
1126 }
1127 }
1128
1129 for (size_t i = 0; i < sizeof(ff_rew_def)/sizeof(ff_rew_def[0]); i++)
1130 m_ffRewSpeeds.push_back(kv[QString("FFRewSpeed%1").arg(i)].toInt());
1131
1132 // process it..
1133 BrowseInit(db_browse_max_forward, m_dbBrowseAllTuners, m_dbUseChannelGroups, db_channel_ordering);
1134
1135 m_vbimode = VBIMode::Parse(!feVBI.isEmpty() ? feVBI : beVBI);
1136
1139}
1140
1146{
1147 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "-- begin");
1148
1149 if (!m_mainWindow)
1150 {
1151 LOG(VB_GENERAL, LOG_ERR, LOC + "No MythMainWindow");
1152 return false;
1153 }
1154
1155 bool fullscreen = !m_dbUseGuiSizeForTv;
1156 m_savedGuiBounds = QRect(m_mainWindow->geometry().topLeft(), m_mainWindow->size());
1157
1158 // adjust for window manager wierdness.
1159 QRect screen = m_mainWindow->GetScreenRect();
1160 if ((abs(m_savedGuiBounds.x() - screen.left()) < 3) &&
1161 (abs(m_savedGuiBounds.y() - screen.top()) < 3))
1162 {
1163 m_savedGuiBounds = QRect(screen.topLeft(), m_mainWindow->size());
1164 }
1165
1166 // if width && height are zero users expect fullscreen playback
1167 if (!fullscreen)
1168 {
1169 int gui_width = 0;
1170 int gui_height = 0;
1171 gCoreContext->GetResolutionSetting("Gui", gui_width, gui_height);
1172 fullscreen |= (0 == gui_width && 0 == gui_height);
1173 }
1174
1176 if (fullscreen)
1178
1179 // player window sizing
1181
1182 m_myWindow = new TvPlayWindow(mainStack, "Playback");
1183
1184 if (m_myWindow->Create())
1185 {
1186 mainStack->AddScreen(m_myWindow, false);
1187 LOG(VB_GENERAL, LOG_INFO, LOC + "Created TvPlayWindow.");
1188 }
1189 else
1190 {
1191 delete m_myWindow;
1192 m_myWindow = nullptr;
1193 }
1194
1196 m_mainWindow->GetPaintWindow()->update();
1197 m_mainWindow->installEventFilter(this);
1198 QCoreApplication::processEvents();
1199
1206
1207 m_sleepIndex = 0;
1208
1209 emit ChangeOSDPositionUpdates(false);
1210
1212 ClearInputQueues(false);
1214
1215 m_switchToRec = nullptr;
1216 SetExitPlayer(false, false);
1217
1219 m_lcdTimerId = StartTimer(1ms, __LINE__);
1222
1223 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "-- end");
1224 return true;
1225}
1226
1228{
1229 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "-- begin");
1230
1231 BrowseStop();
1232 BrowseWait();
1233
1236
1237 if (m_mainWindow)
1238 {
1239 m_mainWindow->removeEventFilter(this);
1240 if (m_weDisabledGUI)
1242 }
1243
1244 if (m_myWindow)
1245 {
1246 m_myWindow->Close();
1247 m_myWindow = nullptr;
1248 }
1249
1250 LOG(VB_PLAYBACK, LOG_INFO, LOC + "-- lock");
1251
1252 // restore window to gui size and position
1253 if (m_mainWindow)
1254 {
1255 MythDisplay* display = m_mainWindow->GetDisplay();
1256 if (display->UsingVideoModes())
1257 {
1258 bool hide = display->NextModeIsLarger(display->GetGUIResolution());
1259 if (hide)
1260 m_mainWindow->hide();
1261 display->SwitchToGUI(true);
1262 if (hide)
1263 m_mainWindow->Show();
1264 }
1266 #ifdef Q_OS_ANDROID
1267 m_mainWindow->Show();
1268 #else
1269 m_mainWindow->show();
1270 #endif
1272 }
1273
1274 qDeleteAll(m_screenPressKeyMapPlayback);
1276 qDeleteAll(m_screenPressKeyMapLiveTV);
1278
1279 delete m_lastProgram;
1280
1281 if (LCD *lcd = LCD::Get())
1282 {
1283 lcd->setFunctionLEDs(FUNC_TV, false);
1284 lcd->setFunctionLEDs(FUNC_MOVIE, false);
1285 lcd->switchToTime();
1286 }
1287
1288 if (m_posThreadPool)
1289 {
1290 // Wait for "PositionSaver" to complete before proceeding
1292 delete m_posThreadPool;
1293 m_posThreadPool = nullptr;
1294 }
1295
1296 m_playerLock.lockForWrite();
1298 m_player = nullptr;
1299 m_playerLock.unlock();
1300
1301 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "-- end");
1302}
1303
1308{
1309 while (true)
1310 {
1311 QCoreApplication::processEvents();
1313 {
1314 m_wantsToQuit = true;
1315 return;
1316 }
1317
1318 TVState state = GetState();
1319 if ((kState_Error == state) || (kState_None == state))
1320 return;
1321
1322 if (kState_ChangingState == state)
1323 continue;
1324
1326 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
1327 if (m_player && !m_player->IsErrored())
1328 {
1331 }
1332 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
1334
1336 return;
1337 }
1338}
1339
1343void TV::UpdateChannelList(int GroupID)
1344{
1346 return;
1347
1348 QMutexLocker locker(&m_channelGroupLock);
1349 if (GroupID == m_channelGroupId)
1350 return;
1351
1352 ChannelInfoList list;
1353 if (GroupID >= 0)
1354 {
1355 list = ChannelUtil::GetChannels(0, true, "channum, callsign", static_cast<uint>(GroupID));
1356 ChannelUtil::SortChannels(list, "channum", true);
1357 }
1358
1359 m_channelGroupId = GroupID;
1361
1363 gCoreContext->SaveSetting("ChannelGroupDefault", m_channelGroupId);
1364}
1365
1367{
1370 ret = m_playerContext.GetState();
1371 return ret;
1372}
1373
1374// XXX what about subtitlezoom?
1376{
1377 QVariantMap status;
1378
1380 status.insert("state", StateToString(GetState()));
1381 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
1383 {
1384 status.insert("title", m_playerContext.m_playingInfo->GetTitle());
1385 status.insert("subtitle", m_playerContext.m_playingInfo->GetSubtitle());
1386 status.insert("starttime", m_playerContext.m_playingInfo->GetRecordingStartTime()
1387 .toUTC().toString("yyyy-MM-ddThh:mm:ssZ"));
1388 status.insert("chanid", QString::number(m_playerContext.m_playingInfo->GetChanID()));
1389 status.insert("programid", m_playerContext.m_playingInfo->GetProgramID());
1390 status.insert("pathname", m_playerContext.m_playingInfo->GetPathname());
1391 }
1392 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
1393 osdInfo info;
1395 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
1396 if (m_player)
1397 {
1398 if (!info.text["totalchapters"].isEmpty())
1399 {
1400 QList<std::chrono::seconds> chapters;
1401 m_player->GetChapterTimes(chapters);
1402 QVariantList var;
1403 for (std::chrono::seconds chapter : std::as_const(chapters))
1404 var << QVariant((long long)chapter.count());
1405 status.insert("chaptertimes", var);
1406 }
1407
1409 QVariantMap tracks;
1410
1411 QStringList list = m_player->GetTracks(kTrackTypeSubtitle);
1412 int currenttrack = -1;
1413 if (!list.isEmpty() && (kDisplayAVSubtitle == capmode))
1414 currenttrack = m_player->GetTrack(kTrackTypeSubtitle);
1415 for (int i = 0; i < list.size(); i++)
1416 {
1417 if (i == currenttrack)
1418 status.insert("currentsubtitletrack", list[i]);
1419 tracks.insert("SELECTSUBTITLE_" + QString::number(i), list[i]);
1420 }
1421
1423 currenttrack = -1;
1424 if (!list.isEmpty() && (kDisplayTeletextCaptions == capmode))
1426 for (int i = 0; i < list.size(); i++)
1427 {
1428 if (i == currenttrack)
1429 status.insert("currentsubtitletrack", list[i]);
1430 tracks.insert("SELECTTTC_" + QString::number(i), list[i]);
1431 }
1432
1434 currenttrack = -1;
1435 if (!list.isEmpty() && (kDisplayCC708 == capmode))
1436 currenttrack = m_player->GetTrack(kTrackTypeCC708);
1437 for (int i = 0; i < list.size(); i++)
1438 {
1439 if (i == currenttrack)
1440 status.insert("currentsubtitletrack", list[i]);
1441 tracks.insert("SELECTCC708_" + QString::number(i), list[i]);
1442 }
1443
1445 currenttrack = -1;
1446 if (!list.isEmpty() && (kDisplayCC608 == capmode))
1447 currenttrack = m_player->GetTrack(kTrackTypeCC608);
1448 for (int i = 0; i < list.size(); i++)
1449 {
1450 if (i == currenttrack)
1451 status.insert("currentsubtitletrack", list[i]);
1452 tracks.insert("SELECTCC608_" + QString::number(i), list[i]);
1453 }
1454
1456 currenttrack = -1;
1457 if (!list.isEmpty() && (kDisplayRawTextSubtitle == capmode))
1458 currenttrack = m_player->GetTrack(kTrackTypeRawText);
1459 for (int i = 0; i < list.size(); i++)
1460 {
1461 if (i == currenttrack)
1462 status.insert("currentsubtitletrack", list[i]);
1463 tracks.insert("SELECTRAWTEXT_" + QString::number(i), list[i]);
1464 }
1465
1467 {
1468 if (kDisplayTextSubtitle == capmode)
1469 status.insert("currentsubtitletrack", tr("External Subtitles"));
1470 tracks.insert(ACTION_ENABLEEXTTEXT, tr("External Subtitles"));
1471 }
1472
1473 status.insert("totalsubtitletracks", tracks.size());
1474 if (!tracks.isEmpty())
1475 status.insert("subtitletracks", tracks);
1476
1477 tracks.clear();
1479 currenttrack = m_player->GetTrack(kTrackTypeAudio);
1480 for (int i = 0; i < list.size(); i++)
1481 {
1482 if (i == currenttrack)
1483 status.insert("currentaudiotrack", list[i]);
1484 tracks.insert("SELECTAUDIO_" + QString::number(i), list[i]);
1485 }
1486
1487 status.insert("totalaudiotracks", tracks.size());
1488 if (!tracks.isEmpty())
1489 status.insert("audiotracks", tracks);
1490
1491 status.insert("playspeed", m_player->GetPlaySpeed());
1492 status.insert("audiosyncoffset", static_cast<long long>(m_audioState.m_audioOffset.count()));
1493
1495 {
1496 status.insert("volume", m_audioState.m_volume);
1497 status.insert("mute", m_audioState.m_muteState);
1498 }
1499
1502 status.insert("brightness", m_videoColourState.GetValue(kPictureAttribute_Brightness));
1504 status.insert("contrast", m_videoColourState.GetValue(kPictureAttribute_Contrast));
1506 status.insert("colour", m_videoColourState.GetValue(kPictureAttribute_Colour));
1508 status.insert("hue", m_videoColourState.GetValue(kPictureAttribute_Hue));
1509 }
1510 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
1512
1513 for (auto tit =info.text.cbegin(); tit != info.text.cend(); ++tit)
1514 status.insert(tit.key(), tit.value());
1515
1516 QHashIterator<QString,int> vit(info.values);
1517 while (vit.hasNext())
1518 {
1519 vit.next();
1520 status.insert(vit.key(), vit.value());
1521 }
1522
1524}
1525
1531bool TV::LiveTV(bool ShowDialogs, const ChannelInfoList &Selection)
1532{
1533 m_requestDelete = false;
1534 m_allowRerecord = false;
1535 m_jumpToProgram = false;
1536
1538 if (m_playerContext.GetState() == kState_None && RequestNextRecorder(ShowDialogs, Selection))
1539 {
1542 m_switchToRec = nullptr;
1543
1544 // Start Idle Timer
1545 if (m_dbIdleTimeout > 0ms)
1546 {
1548 LOG(VB_GENERAL, LOG_INFO, QString("Using Idle Timer. %1 minutes")
1549 .arg(duration_cast<std::chrono::minutes>(m_dbIdleTimeout).count()));
1550 }
1551
1553 return true;
1554 }
1556 return false;
1557}
1558
1559bool TV::RequestNextRecorder(bool ShowDialogs, const ChannelInfoList &Selection)
1560{
1562
1563 RemoteEncoder *testrec = nullptr;
1564 if (m_switchToRec)
1565 {
1566 // If this is set we, already got a new recorder in SwitchCards()
1567 testrec = m_switchToRec;
1568 m_switchToRec = nullptr;
1569 }
1570 else if (!Selection.empty())
1571 {
1572 for (const auto & ci : Selection)
1573 {
1574 uint chanid = ci.m_chanId;
1575 QString channum = ci.m_chanNum;
1576 if (!chanid || channum.isEmpty())
1577 continue;
1578 QVector<uint> cards = IsTunableOn(&m_playerContext, chanid);
1579
1580 if (chanid && !channum.isEmpty() && !cards.isEmpty())
1581 {
1582 testrec = RemoteGetExistingRecorder(static_cast<int>(*(cards.begin())));
1583 m_initialChanID = chanid;
1584 break;
1585 }
1586 }
1587 }
1588 else
1589 {
1590 // When starting LiveTV we just get the next free recorder
1591 testrec = RemoteRequestNextFreeRecorder(-1);
1592 }
1593
1594 if (!testrec)
1595 return false;
1596
1597 if (!testrec->IsValidRecorder())
1598 {
1599 if (ShowDialogs)
1601
1602 delete testrec;
1603
1604 return false;
1605 }
1606
1608
1609 return true;
1610}
1611
1612void TV::AskAllowRecording(const QStringList &Msg, int Timeuntil, bool HasRec, bool HasLater)
1613{
1614 if (!StateIsLiveTV(GetState()))
1615 return;
1616
1617 auto *info = new ProgramInfo(Msg);
1618 if (!info->GetChanID())
1619 {
1620 delete info;
1621 return;
1622 }
1623
1624 QMutexLocker locker(&m_askAllowLock);
1625 QString key = info->MakeUniqueKey();
1626 if (Timeuntil > 0)
1627 {
1628 // add program to list
1629#if 0
1630 LOG(VB_GENERAL, LOG_DEBUG, LOC + "AskAllowRecording -- " +
1631 QString("adding '%1'").arg(info->m_title));
1632#endif
1633 QDateTime expiry = MythDate::current().addSecs(Timeuntil);
1634 m_askAllowPrograms[key] = AskProgramInfo(expiry, HasRec, HasLater, info);
1635 }
1636 else
1637 {
1638 // remove program from list
1639 LOG(VB_GENERAL, LOG_INFO, LOC + "-- " +
1640 QString("removing '%1'").arg(info->GetTitle()));
1641 QMap<QString,AskProgramInfo>::iterator it = m_askAllowPrograms.find(key);
1642 if (it != m_askAllowPrograms.end())
1643 {
1644 delete (*it).m_info;
1645 m_askAllowPrograms.erase(it);
1646 }
1647 delete info;
1648 }
1649
1651}
1652
1654{
1655 QMutexLocker locker(&m_askAllowLock);
1657 return;
1658
1659 uint cardid = m_playerContext.GetCardID();
1660
1661 QString single_rec = tr("MythTV wants to record \"%1\" on %2 in %d seconds. Do you want to:");
1662
1663 QString record_watch = tr("Record and watch while it records");
1664 QString let_record1 = tr("Let it record and go back to the Main Menu");
1665 QString let_recordm = tr("Let them record and go back to the Main Menu");
1666 QString record_later1 = tr("Record it later, I want to watch TV");
1667 QString record_laterm = tr("Record them later, I want to watch TV");
1668 QString do_not_record1= tr("Don't let it record, I want to watch TV");
1669 QString do_not_recordm= tr("Don't let them record, I want to watch TV");
1670
1671 // eliminate timed out programs
1672 QDateTime timeNow = MythDate::current();
1673 QMap<QString,AskProgramInfo>::iterator it = m_askAllowPrograms.begin();
1674 while (it != m_askAllowPrograms.end())
1675 {
1676 if ((*it).m_expiry <= timeNow)
1677 {
1678#if 0
1679 LOG(VB_GENERAL, LOG_DEBUG, LOC + "-- " +
1680 QString("removing '%1'").arg((*it).m_info->m_title));
1681#endif
1682 delete (*it).m_info;
1683 it = m_askAllowPrograms.erase(it);
1684 }
1685 else
1686 {
1687 it++;
1688 }
1689 }
1690 std::chrono::milliseconds timeuntil = 0ms;
1691 QString message;
1692 uint conflict_count = static_cast<uint>(m_askAllowPrograms.size());
1693
1694 it = m_askAllowPrograms.begin();
1695 if ((1 == m_askAllowPrograms.size()) && ((*it).m_info->GetInputID() == cardid))
1696 {
1697 (*it).m_isInSameInputGroup = (*it).m_isConflicting = true;
1698 }
1699 else if (!m_askAllowPrograms.empty())
1700 {
1701 // get the currently used input on our card
1702 bool busy_input_grps_loaded = false;
1703 std::vector<uint> busy_input_grps;
1704 InputInfo busy_input;
1705 RemoteIsBusy(cardid, busy_input);
1706
1707 // check if current input can conflict
1708 for (it = m_askAllowPrograms.begin(); it != m_askAllowPrograms.end(); ++it)
1709 {
1710 (*it).m_isInSameInputGroup =
1711 (cardid == (*it).m_info->GetInputID());
1712
1713 if ((*it).m_isInSameInputGroup)
1714 continue;
1715
1716 // is busy_input in same input group as recording
1717 if (!busy_input_grps_loaded)
1718 {
1719 busy_input_grps = CardUtil::GetInputGroups(busy_input.m_inputId);
1720 busy_input_grps_loaded = true;
1721 }
1722
1723 std::vector<uint> input_grps =
1724 CardUtil::GetInputGroups((*it).m_info->GetInputID());
1725
1726 for (uint grp : input_grps)
1727 {
1728 if (find(busy_input_grps.begin(), busy_input_grps.end(),
1729 grp) != busy_input_grps.end())
1730 {
1731 (*it).m_isInSameInputGroup = true;
1732 break;
1733 }
1734 }
1735 }
1736
1737 // check if inputs that can conflict are ok
1738 conflict_count = 0;
1739 for (it = m_askAllowPrograms.begin(); it != m_askAllowPrograms.end(); ++it)
1740 {
1741 if (!(*it).m_isInSameInputGroup)
1742 (*it).m_isConflicting = false; // NOLINT(bugprone-branch-clone)
1743 else if (cardid == (*it).m_info->GetInputID())
1744 (*it).m_isConflicting = true; // NOLINT(bugprone-branch-clone)
1745 else if (!CardUtil::IsTunerShared(cardid, (*it).m_info->GetInputID()))
1746 (*it).m_isConflicting = true;
1747 else if ((busy_input.m_mplexId &&
1748 (busy_input.m_mplexId == (*it).m_info->QueryMplexID())) ||
1749 (!busy_input.m_mplexId &&
1750 (busy_input.m_chanId == (*it).m_info->GetChanID())))
1751 (*it).m_isConflicting = false;
1752 else
1753 (*it).m_isConflicting = true;
1754
1755 conflict_count += (*it).m_isConflicting ? 1 : 0;
1756 }
1757 }
1758
1759 it = m_askAllowPrograms.begin();
1760 for (; it != m_askAllowPrograms.end() && !(*it).m_isConflicting; ++it);
1761
1762 if (conflict_count == 0)
1763 {
1764 LOG(VB_GENERAL, LOG_INFO, LOC + "The scheduler wants to make "
1765 "a non-conflicting recording.");
1766 // TODO take down mplexid and inform user of problem
1767 // on channel changes.
1768 }
1769 else if (conflict_count == 1 && ((*it).m_info->GetInputID() == cardid))
1770 {
1771#if 0
1772 LOG(VB_GENERAL, LOG_DEBUG, LOC + "UpdateOSDAskAllowDialog -- " +
1773 "kAskAllowOneRec");
1774#endif
1775
1776 it = m_askAllowPrograms.begin();
1777
1778 QString channel = m_dbChannelFormat;
1779 channel
1780 .replace("<num>", (*it).m_info->GetChanNum())
1781 .replace("<sign>", (*it).m_info->GetChannelSchedulingID())
1782 .replace("<name>", (*it).m_info->GetChannelName());
1783
1784 message = single_rec.arg((*it).m_info->GetTitle(), channel);
1785
1786 BrowseEnd(false);
1787 timeuntil = MythDate::secsInFuture((*it).m_expiry);
1788 MythOSDDialogData dialog { OSD_DLG_ASKALLOW, message, timeuntil };
1789 dialog.m_buttons.push_back({ record_watch, "DIALOG_ASKALLOW_WATCH_0", false, !((*it).m_hasRec)} );
1790 dialog.m_buttons.push_back({ let_record1, "DIALOG_ASKALLOW_EXIT_0" });
1791 dialog.m_buttons.push_back({ ((*it).m_hasLater) ? record_later1 : do_not_record1,
1792 "DIALOG_ASKALLOW_CANCELRECORDING_0", false, ((*it).m_hasRec) });
1793 emit ChangeOSDDialog(dialog);
1794 }
1795 else
1796 {
1797 if (conflict_count > 1)
1798 {
1799 message = tr(
1800 "MythTV wants to record these programs in %d seconds:");
1801 message += "\n";
1802 }
1803
1804 bool has_rec = false;
1805 for (it = m_askAllowPrograms.begin(); it != m_askAllowPrograms.end(); ++it)
1806 {
1807 if (!(*it).m_isConflicting)
1808 continue;
1809
1810 QString title = (*it).m_info->GetTitle();
1811 if ((title.length() < 10) && !(*it).m_info->GetSubtitle().isEmpty())
1812 title += ": " + (*it).m_info->GetSubtitle();
1813 if (title.length() > 20)
1814 title = title.left(17) + "...";
1815
1816 QString channel = m_dbChannelFormat;
1817 channel
1818 .replace("<num>", (*it).m_info->GetChanNum())
1819 .replace("<sign>", (*it).m_info->GetChannelSchedulingID())
1820 .replace("<name>", (*it).m_info->GetChannelName());
1821
1822 if (conflict_count > 1)
1823 {
1824 message += tr("\"%1\" on %2").arg(title, channel);
1825 message += "\n";
1826 }
1827 else
1828 {
1829 message = single_rec.arg((*it).m_info->GetTitle(), channel);
1830 has_rec = (*it).m_hasRec;
1831 }
1832 }
1833
1834 if (conflict_count > 1)
1835 {
1836 message += "\n";
1837 message += tr("Do you want to:");
1838 }
1839
1840 bool all_have_later = true;
1841 timeuntil = 9999999ms;
1842 for (it = m_askAllowPrograms.begin(); it != m_askAllowPrograms.end(); ++it)
1843 {
1844 if ((*it).m_isConflicting)
1845 {
1846 all_have_later &= (*it).m_hasLater;
1847 auto tmp = std::chrono::milliseconds(MythDate::secsInFuture((*it).m_expiry));
1848 timeuntil = std::clamp(tmp, 0ms, timeuntil);
1849 }
1850 }
1851 timeuntil = (9999999ms == timeuntil) ? 0ms : timeuntil;
1852
1853 if (conflict_count > 1)
1854 {
1855 BrowseEnd(false);
1856 emit ChangeOSDDialog( { OSD_DLG_ASKALLOW, message, timeuntil, {
1857 { let_recordm, "DIALOG_ASKALLOW_EXIT_0", false, true },
1858 { all_have_later ? record_laterm : do_not_recordm, "DIALOG_ASKALLOW_CANCELCONFLICTING_0" }
1859 }});
1860 }
1861 else
1862 {
1863 BrowseEnd(false);
1864 emit ChangeOSDDialog( {OSD_DLG_ASKALLOW, message, timeuntil, {
1865 { let_record1, "DIALOG_ASKALLOW_EXIT_0", false, !has_rec},
1866 { all_have_later ? record_later1 : do_not_record1, "DIALOG_ASKALLOW_CANCELRECORDING_0", false, has_rec}
1867 }});
1868 }
1869 }
1870}
1871
1872void TV::HandleOSDAskAllow(const QString& Action)
1873{
1875 return;
1876
1877 if (!m_askAllowLock.tryLock())
1878 {
1879 LOG(VB_GENERAL, LOG_ERR, "allowrecordingbox : askAllowLock is locked");
1880 return;
1881 }
1882
1883 if (Action == "CANCELRECORDING")
1884 {
1887 }
1888 else if (Action == "CANCELCONFLICTING")
1889 {
1890 for (const auto& pgm : std::as_const(m_askAllowPrograms))
1891 {
1892 if (pgm.m_isConflicting)
1893 RemoteCancelNextRecording(pgm.m_info->GetInputID(), true);
1894 }
1895 }
1896 else if (Action == "WATCH")
1897 {
1900 }
1901 else // if (action == "EXIT")
1902 {
1903 PrepareToExitPlayer(__LINE__);
1904 SetExitPlayer(true, true);
1905 }
1906
1907 m_askAllowLock.unlock();
1908}
1909
1911{
1912 m_wantsToQuit = false;
1913 m_jumpToProgram = false;
1914 m_allowRerecord = false;
1915 m_requestDelete = false;
1917
1920 {
1922 return 0;
1923 }
1924
1926
1930
1932
1933 if (LCD *lcd = LCD::Get())
1934 {
1935 lcd->switchToChannel(ProgInfo.GetChannelSchedulingID(), ProgInfo.GetTitle(), ProgInfo.GetSubtitle());
1936 lcd->setFunctionLEDs((ProgInfo.IsRecording())?FUNC_TV:FUNC_MOVIE, true);
1937 }
1938
1939 return 1;
1940}
1941
1943{
1945}
1946
1948{
1949 return (State == kState_WatchingPreRecorded ||
1954}
1955
1957{
1958 return (State == kState_WatchingLiveTV);
1959}
1960
1961// NOLINTBEGIN(cppcoreguidelines-macro-usage)
1962#define TRANSITION(ASTATE,BSTATE) ((ctxState == (ASTATE)) && (desiredNextState == (BSTATE)))
1963
1964#define SET_NEXT() do { nextState = desiredNextState; changed = true; } while(false)
1965#define SET_LAST() do { nextState = ctxState; changed = true; } while(false)
1966// NOLINTEND(cppcoreguidelines-macro-usage)
1967
1968static QString tv_i18n(const QString &msg)
1969{
1970 QByteArray msg_arr = msg.toLatin1();
1971 QString msg_i18n = TV::tr(msg_arr.constData());
1972 QByteArray msg_i18n_arr = msg_i18n.toLatin1();
1973 return (msg_arr == msg_i18n_arr) ? msg_i18n : msg;
1974}
1975
1985{
1987 {
1988 LOG(VB_GENERAL, LOG_ERR, LOC + "Called after fatal error detected.");
1989 return;
1990 }
1991
1992 bool changed = false;
1993
1995 TVState nextState = m_playerContext.GetState();
1996 if (m_playerContext.m_nextState.empty())
1997 {
1998 LOG(VB_GENERAL, LOG_WARNING, LOC + "Warning, called with no state to change to.");
2000 return;
2001 }
2002
2003 TVState ctxState = m_playerContext.GetState();
2004 TVState desiredNextState = m_playerContext.DequeueNextState();
2005
2006 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Attempting to change from %1 to %2")
2007 .arg(StateToString(nextState), StateToString(desiredNextState)));
2008
2009 if (desiredNextState == kState_Error)
2010 {
2011 LOG(VB_GENERAL, LOG_ERR, LOC + "Attempting to set to an error state!");
2012 SetErrored();
2014 return;
2015 }
2016
2017 bool ok = false;
2019 {
2021
2023
2024 QDateTime timerOffTime = MythDate::current();
2025 m_lockTimerOn = false;
2026
2027 SET_NEXT();
2028
2029 uint chanid = m_initialChanID;
2030 if (!chanid)
2031 chanid = static_cast<uint>(gCoreContext->GetNumSetting("DefaultChanid", 0));
2032
2033 if (chanid && !IsTunablePriv(chanid))
2034 chanid = 0;
2035
2036 QString channum = "";
2037
2038 if (chanid)
2039 {
2040 QStringList reclist;
2041
2043 query.prepare("SELECT channum FROM channel "
2044 "WHERE chanid = :CHANID");
2045 query.bindValue(":CHANID", chanid);
2046 if (query.exec() && query.isActive() && query.size() > 0 && query.next())
2047 channum = query.value(0).toString();
2048 else
2049 channum = QString::number(chanid);
2050
2052 QString::number(chanid));
2053
2054 if (getit)
2055 reclist = ChannelUtil::GetValidRecorderList(chanid, channum);
2056
2057 if (!reclist.empty())
2058 {
2059 RemoteEncoder *testrec = RemoteRequestFreeRecorderFromList(reclist, 0);
2060 if (testrec && testrec->IsValidRecorder())
2061 {
2064 }
2065 else
2066 {
2067 delete testrec; // If testrec isn't a valid recorder ...
2068 }
2069 }
2070 else if (getit)
2071 {
2072 chanid = 0;
2073 }
2074 }
2075
2076 LOG(VB_GENERAL, LOG_DEBUG, LOC + "Spawning LiveTV Recorder -- begin");
2077
2078 if (chanid && !channum.isEmpty())
2080 else
2082
2083 LOG(VB_GENERAL, LOG_DEBUG, LOC + "Spawning LiveTV Recorder -- end");
2084
2086 {
2087 LOG(VB_GENERAL, LOG_ERR, LOC + "LiveTV not successfully started");
2090 SetErrored();
2091 SET_LAST();
2092 }
2093 else
2094 {
2095 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
2096 QString playbackURL = m_playerContext.m_playingInfo->GetPlaybackURL(true);
2097 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
2098
2099 bool opennow = (m_playerContext.m_tvchain->GetInputType(-1) != "DUMMY");
2100
2101 LOG(VB_GENERAL, LOG_INFO, LOC +
2102 QString("playbackURL(%1) inputtype(%2)")
2103 .arg(playbackURL, m_playerContext.m_tvchain->GetInputType(-1)));
2104
2107 playbackURL, false, true,
2108 opennow ? MythMediaBuffer::kLiveTVOpenTimeout : -1ms));
2109
2112 }
2113
2114
2116 {
2117 ok = StartPlayer(desiredNextState);
2118 }
2119 if (!ok)
2120 {
2121 LOG(VB_GENERAL, LOG_ERR, LOC + "LiveTV not successfully started");
2124 SetErrored();
2125 SET_LAST();
2126 }
2127 else
2128 {
2129 if (!m_lastLockSeenTime.isValid() ||
2130 (m_lastLockSeenTime < timerOffTime))
2131 {
2132 m_lockTimer.start();
2133 m_lockTimerOn = true;
2134 }
2135 }
2136 }
2138 {
2139 SET_NEXT();
2141 StopStuff(true, true, true);
2142 }
2148 {
2149 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
2150 QString playbackURL = m_playerContext.m_playingInfo->GetPlaybackURL(true);
2151 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
2152
2153 MythMediaBuffer *buffer = MythMediaBuffer::Create(playbackURL, false);
2154 if (buffer && !buffer->GetLastError().isEmpty())
2155 {
2156 ShowNotificationError(tr("Can't start playback"),
2157 TV::tr( "TV Player" ), buffer->GetLastError());
2158 delete buffer;
2159 buffer = nullptr;
2160 }
2162
2164 {
2165 if (desiredNextState == kState_WatchingRecording)
2166 {
2167 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
2169 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
2170
2172
2175 {
2176 LOG(VB_GENERAL, LOG_ERR, LOC +
2177 "Couldn't find recorder for in-progress recording");
2178 desiredNextState = kState_WatchingPreRecorded;
2180 }
2181 else
2182 {
2184 }
2185 }
2186
2187 ok = StartPlayer(desiredNextState);
2188
2189 if (ok)
2190 {
2191 SET_NEXT();
2192
2193 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
2195 {
2196 QString message = "COMMFLAG_REQUEST ";
2198 gCoreContext->SendMessage(message);
2199 }
2200 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
2201 }
2202 }
2203
2204 if (!ok)
2205 {
2206 SET_LAST();
2207 SetErrored();
2209 {
2211 TV::tr( "TV Player" ),
2212 playbackURL);
2213 // We're going to display this error as notification
2214 // no need to display it later as popup
2216 }
2217 }
2218 }
2224 {
2225 SET_NEXT();
2227 StopStuff(true, true, false);
2228 }
2231 {
2232 SET_NEXT();
2233 }
2234
2235 // Print state changed message...
2236 if (!changed)
2237 {
2238 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unknown state transition: %1 to %2")
2239 .arg(StateToString(m_playerContext.GetState()), StateToString(desiredNextState)));
2240 }
2241 else if (m_playerContext.GetState() != nextState)
2242 {
2243 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Changing from %1 to %2")
2245 }
2246
2247 // update internal state variable
2248 TVState lastState = m_playerContext.GetState();
2249 m_playerContext.m_playingState = nextState;
2251
2253 {
2254 LOG(VB_GENERAL, LOG_INFO, LOC + "State is LiveTV");
2256 LOG(VB_GENERAL, LOG_INFO, LOC + "UpdateOSDInput done");
2257 UpdateLCD();
2258 LOG(VB_GENERAL, LOG_INFO, LOC + "UpdateLCD done");
2259 ITVRestart(true);
2260 LOG(VB_GENERAL, LOG_INFO, LOC + "ITVRestart done");
2261 }
2262 else if (StateIsPlaying(m_playerContext.GetState()) && lastState == kState_None)
2263 {
2264 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
2265 int count = PlayGroup::GetCount();
2266 QString msg = tr("%1 Settings")
2268 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
2269 if (count > 0)
2270 emit ChangeOSDMessage(msg);
2271 ITVRestart(false);
2272 }
2273
2275 {
2276 UpdateLCD();
2277 }
2278
2281
2287
2291
2293 {
2295 }
2296
2303 {
2305 // m_playerBounds is not applicable when switching modes so
2306 // skip this logic in that case.
2307 if (!m_dbUseVideoModes)
2309
2310 if (!m_weDisabledGUI)
2311 {
2312 m_weDisabledGUI = true;
2314 }
2315 // we no longer need the contents of myWindow
2316 if (m_myWindow)
2318
2319 LOG(VB_GENERAL, LOG_INFO, LOC + "Main UI disabled.");
2320 }
2321
2322 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + " -- end");
2323}
2324
2325#undef TRANSITION
2326#undef SET_NEXT
2327#undef SET_LAST
2328
2334bool TV::StartRecorder(std::chrono::milliseconds MaxWait)
2335{
2337 MaxWait = (MaxWait <= 0ms) ? 40s : MaxWait;
2338 MythTimer t;
2339 t.start();
2340 bool recording = false;
2341 bool ok = true;
2342 if (!rec)
2343 {
2344 LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid Remote Encoder");
2345 SetErrored();
2346 return false;
2347 }
2348 while (!(recording = rec->IsRecording(&ok)) && !m_exitPlayerTimerId && t.elapsed() < MaxWait)
2349 {
2350 if (!ok)
2351 {
2352 LOG(VB_GENERAL, LOG_ERR, LOC + "Lost contact with backend");
2353 SetErrored();
2354 return false;
2355 }
2356 std::this_thread::sleep_for(5us);
2357 }
2358
2359 if (!recording || m_exitPlayerTimerId)
2360 {
2362 LOG(VB_GENERAL, LOG_ERR, LOC + "Timed out waiting for recorder to start");
2363 return false;
2364 }
2365
2366 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Took %1 ms to start recorder.")
2367 .arg(t.elapsed().count()));
2368 return true;
2369}
2370
2384void TV::StopStuff(bool StopRingBuffer, bool StopPlayer, bool StopRecorder)
2385{
2386 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "-- begin");
2387
2388 emit PlaybackExiting(this);
2389
2392
2393 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
2394 if (StopPlayer)
2396 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
2397
2398 if (StopRingBuffer)
2399 {
2400 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Stopping ring buffer");
2402 {
2406 }
2407 }
2408
2409 if (StopRecorder)
2410 {
2411 LOG(VB_PLAYBACK, LOG_INFO, LOC + "stopping recorder");
2414 }
2415
2416 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "-- end");
2417}
2418
2419void TV::timerEvent(QTimerEvent *Event)
2420{
2421 const int timer_id = Event->timerId();
2422
2424 bool errored = m_playerContext.IsErrored();
2426 if (errored)
2427 return;
2428
2429 bool handled = true;
2430 if (timer_id == m_lcdTimerId)
2432 else if (timer_id == m_lcdVolumeTimerId)
2434 else if (timer_id == m_sleepTimerId)
2435 ShowOSDSleep();
2436 else if (timer_id == m_sleepDialogTimerId)
2438 else if (timer_id == m_idleTimerId)
2439 ShowOSDIdle();
2440 else if (timer_id == m_idleDialogTimerId)
2442 else if (timer_id == m_endOfPlaybackTimerId)
2444 else if (timer_id == m_endOfRecPromptTimerId)
2446 else if (timer_id == m_videoExitDialogTimerId)
2448 else if (timer_id == m_pseudoChangeChanTimerId)
2450 else if (timer_id == m_speedChangeTimerId)
2452 else if (timer_id == m_saveLastPlayPosTimerId)
2454 else
2455 handled = false;
2456
2457 if (handled)
2458 return;
2459
2460 // Check if it matches a signalMonitorTimerId
2461 if (timer_id == m_signalMonitorTimerId)
2462 {
2466 if (!m_playerContext.m_lastSignalMsg.empty())
2467 {
2468 // set last signal msg, so we get some feedback...
2471 }
2474 return;
2475 }
2476
2477 // Check if it matches networkControlTimerId
2478 QString netCmd;
2479 if (timer_id == m_networkControlTimerId)
2480 {
2481 if (!m_networkControlCommands.empty())
2483 if (m_networkControlCommands.empty())
2484 {
2487 }
2488 }
2489
2490 if (!netCmd.isEmpty())
2491 {
2495 handled = true;
2496 }
2497
2498 if (handled)
2499 return;
2500
2501 // Check if it matches exitPlayerTimerId
2502 if (timer_id == m_exitPlayerTimerId)
2503 {
2505 emit DialogQuit();
2506 emit HideAll();
2507
2509 {
2511 {
2512 emit ChangeOSDMessage(tr("Last Program: \"%1\" Doesn't Exist")
2513 .arg(m_lastProgram->GetTitle()));
2514 lastProgramStringList.clear();
2515 SetLastProgram(nullptr);
2516 LOG(VB_PLAYBACK, LOG_ERR, LOC + "Last Program File does not exist");
2517 m_jumpToProgram = false;
2518 }
2519 else
2520 {
2522 }
2523 }
2524 else
2525 {
2527 }
2528
2530
2533 handled = true;
2534 }
2535
2536 if (handled)
2537 return;
2538
2539 if (timer_id == m_ccInputTimerId)
2540 {
2542 // Clear closed caption input mode when timer expires
2543 if (m_ccInputMode)
2544 {
2545 m_ccInputMode = false;
2546 ClearInputQueues(true);
2547 }
2549
2551 m_ccInputTimerId = 0;
2552 handled = true;
2553 }
2554
2555 if (handled)
2556 return;
2557
2558 if (timer_id == m_asInputTimerId)
2559 {
2561 // Clear closed caption input mode when timer expires
2562 if (m_asInputMode)
2563 {
2564 m_asInputMode = false;
2565 ClearInputQueues(true);
2566 }
2568
2570 m_asInputTimerId = 0;
2571 handled = true;
2572 }
2573
2574 if (handled)
2575 return;
2576
2577 if (timer_id == m_queueInputTimerId)
2578 {
2580 // Commit input when the OSD fades away
2581 if (HasQueuedChannel())
2582 {
2583 OSD *osd = GetOSDL();
2584 if (osd && !osd->IsWindowVisible(OSD_WIN_INPUT))
2585 {
2586 ReturnOSDLock();
2588 }
2589 else
2590 {
2591 ReturnOSDLock();
2592 }
2593 }
2595
2597 {
2600 }
2601 handled = true;
2602 }
2603
2604 if (handled)
2605 return;
2606
2607 if (timer_id == m_browseTimerId)
2608 {
2610 BrowseEnd(false);
2612 handled = true;
2613 }
2614
2615 if (handled)
2616 return;
2617
2618 if (timer_id == m_errorRecoveryTimerId)
2619 {
2623 {
2624 SetExitPlayer(true, false);
2626 }
2628
2632 return;
2633 }
2634
2635 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Unknown timer: %1").arg(timer_id));
2636}
2637
2639{
2641 LCD *lcd = LCD::Get();
2642 if (lcd)
2643 {
2644 float progress = 0.0F;
2645 QString lcd_time_string;
2646 bool showProgress = true;
2647
2648 if (StateIsLiveTV(GetState()))
2650
2652 {
2655 }
2656
2657 if (showProgress)
2658 {
2659 osdInfo info;
2661 progress = info.values["position"] * 0.001F;
2662
2663 lcd_time_string = info.text["playedtime"] + " / " + info.text["totaltime"];
2664 // if the string is longer than the LCD width, remove all spaces
2665 if (lcd_time_string.length() > lcd->getLCDWidth())
2666 lcd_time_string.remove(' ');
2667 }
2668 }
2669 lcd->setChannelProgress(lcd_time_string, progress);
2670 }
2672
2674 m_lcdTimerId = StartTimer(kLCDTimeout, __LINE__);
2675
2676 return true;
2677}
2678
2680{
2682 LCD *lcd = LCD::Get();
2683 if (lcd)
2684 {
2687 }
2689
2692}
2693
2694int TV::StartTimer(std::chrono::milliseconds Interval, int Line)
2695{
2696 int timer = startTimer(Interval);
2697 if (!timer)
2698 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to start timer on line %1 of %2").arg(Line).arg(__FILE__));
2699 return timer;
2700}
2701
2702void TV::KillTimer(int Id)
2703{
2704 killTimer(Id);
2705}
2706
2708{
2711}
2712
2714{
2715 auto StateChange = [&]()
2716 {
2718 if (!m_playerContext.m_nextState.empty())
2719 {
2723 {
2727 m_player = nullptr;
2728 }
2729 }
2731 };
2732
2733 QTimer::singleShot(0, this, StateChange);
2734}
2735
2737{
2738 auto InputChange = [&]()
2739 {
2742 {
2745 SwitchInputs(0, QString(), tmp);
2746 }
2748 };
2749
2750 QTimer::singleShot(0, this, InputChange);
2751}
2752
2754{
2757 m_errorRecoveryTimerId = StartTimer(1ms, __LINE__);
2758}
2759
2761{
2762 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Switching to program: %1")
2763 .arg(ProgInfo.toString(ProgramInfo::kTitleSubtitle)));
2765 PrepareToExitPlayer(__LINE__);
2766 m_jumpToProgram = true;
2767 SetExitPlayer(true, true);
2768}
2769
2771{
2772 m_playerContext.LockDeletePlayer(__FILE__, Line);
2774 {
2775 // Clear last play position when we're at the end of a recording.
2776 // unless the recording is in-progress.
2777 bool at_end = !StateIsRecording(m_playerContext.GetState()) &&
2779
2780 // Clear last play position on exit when the user requested this
2781 if (m_clearPosOnExit)
2782 {
2783 at_end = true;
2784 }
2785
2786 // Save total frames for video file if not already present
2788 {
2789 auto totalFrames = m_playerContext.m_playingInfo->QueryTotalFrames();
2790 if (!totalFrames)
2791 {
2794 }
2795 }
2796
2797 // Clear/Save play position without notification
2798 // The change must be broadcast when file is no longer in use
2799 // to update previews, ie. with the MarkNotInUse notification
2800 uint64_t frame = at_end ? 0 : m_playerContext.m_player->GetFramesPlayed();
2802 emit UpdateLastPlayPosition(frame);
2805 }
2806 m_playerContext.UnlockDeletePlayer(__FILE__, Line);
2807}
2808
2809void TV::SetExitPlayer(bool SetIt, bool WantsTo)
2810{
2811 if (SetIt)
2812 {
2813 m_wantsToQuit = WantsTo;
2815 m_exitPlayerTimerId = StartTimer(1ms, __LINE__);
2816 }
2817 else
2818 {
2822 m_wantsToQuit = WantsTo;
2823 }
2824}
2825
2827{
2831
2832 bool is_playing = false;
2834 if (StateIsPlaying(GetState()))
2835 {
2837 {
2838 is_playing = true;
2839 }
2840 // If the end of playback is destined to pop up the end of
2841 // recording delete prompt, then don't exit the player here.
2842 else if (GetState() != kState_WatchingPreRecorded ||
2844 {
2846 m_endOfRecording = true;
2847 PrepareToExitPlayer(__LINE__);
2848 SetExitPlayer(true, true);
2849 }
2850 }
2852
2853 if (is_playing)
2855}
2856
2858{
2861 {
2862 return;
2863 }
2864
2866 OSD *osd = GetOSDL();
2867 if (osd && osd->DialogVisible())
2868 {
2869 ReturnOSDLock();
2871 return;
2872 }
2873 ReturnOSDLock();
2874
2875 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
2876 bool do_prompt = (m_playerContext.GetState() == kState_WatchingPreRecorded &&
2878 !m_player->IsPlaying());
2879 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
2880
2881 if (do_prompt)
2882 ShowOSDPromptDeleteRecording(tr("End Of Recording"));
2883
2885}
2886
2888{
2892
2893 // disable dialog and exit playback after timeout
2895 OSD *osd = GetOSDL();
2896 if (!osd || !osd->DialogVisible(OSD_DLG_VIDEOEXIT))
2897 {
2898 ReturnOSDLock();
2900 return;
2901 }
2902 ReturnOSDLock();
2903 DoTogglePause(true);
2904 ClearOSD();
2905 PrepareToExitPlayer(__LINE__);
2907
2908 m_requestDelete = false;
2909 SetExitPlayer(true, true);
2910}
2911
2913{
2916
2917 bool restartTimer = false;
2920 {
2922 {
2923 restartTimer = true;
2924 }
2925 else
2926 {
2927 LOG(VB_CHANNEL, LOG_INFO, "REC_PROGRAM -- channel change");
2928
2930 QString channum = m_playerContext.m_pseudoLiveTVRec->GetChanNum();
2932
2934 ChangeChannel(chanid, channum);
2937 }
2938 }
2940
2941 if (restartTimer)
2943 m_pseudoChangeChanTimerId = StartTimer(25ms, __LINE__);
2944}
2945
2946void TV::SetSpeedChangeTimer(std::chrono::milliseconds When, int Line)
2947{
2950 m_speedChangeTimerId = StartTimer(When, Line);
2951}
2952
2954{
2958
2962 if (update_msg)
2965}
2966
2990bool TV::eventFilter(QObject* Object, QEvent* Event)
2991{
2992 // We want to intercept all resize events sent to the main window
2993 if ((Event->type() == QEvent::Resize))
2994 return (m_mainWindow != Object) ? false : event(Event);
2995
2996 // Intercept keypress events unless they need to be handled by a main UI
2997 // screen (e.g. GuideGrid, ProgramFinder)
2998
2999 if ( (QEvent::KeyPress == Event->type() || QEvent::KeyRelease == Event->type())
3001 return TVPlaybackState::eventFilter(Object, Event);
3002
3003 QScopedPointer<QEvent> sNewEvent(nullptr);
3004 if (m_mainWindow->KeyLongPressFilter(&Event, sNewEvent))
3005 return true;
3006
3007 if (QEvent::KeyPress == Event->type())
3008 return event(Event);
3009
3010 if (MythGestureEvent::kEventType == Event->type())
3011 return m_ignoreKeyPresses ? false : event(Event);
3012
3013 if (Event->type() == MythEvent::kMythEventMessage ||
3017 {
3019 return true;
3020 }
3021
3022 switch (Event->type())
3023 {
3024 case QEvent::Paint:
3025 case QEvent::UpdateRequest:
3026 case QEvent::Enter:
3027 {
3028 event(Event);
3029 return TVPlaybackState::eventFilter(Object, Event);
3030 }
3031 default:
3032 return TVPlaybackState::eventFilter(Object, Event);
3033 }
3034}
3035
3037bool TV::event(QEvent* Event)
3038{
3039 if (Event == nullptr)
3040 return TVPlaybackState::event(Event);
3041
3042 if (QEvent::Resize == Event->type())
3043 {
3044 // These events probably aren't received by a direct call from
3045 // the Qt event dispacther, but are received by way of the event
3046 // dispatcher calling TV::eventFilter(MainWindow, Event).
3047 const auto *qre = dynamic_cast<const QResizeEvent*>(Event);
3048 if (qre)
3049 emit WindowResized(qre->size());
3050 return TVPlaybackState::event(Event);
3051 }
3052
3053 if (QEvent::KeyPress == Event->type() || MythGestureEvent::kEventType == Event->type())
3054 {
3055 // These events aren't received by a direct call from the Qt
3056 // event dispacther, but are received by way of the event
3057 // dispatcher calling TV::eventFilter(MainWindow, Event).
3058#if DEBUG_ACTIONS
3059 if (QEvent::KeyPress == Event->type())
3060 {
3061 const auto * ke = dynamic_cast<QKeyEvent*>(Event);
3062 if (ke)
3063 {
3064 LOG(VB_GENERAL, LOG_INFO, LOC + QString("keypress: %1 '%2'")
3065 .arg(ke->key()).arg(ke->text()));
3066 }
3067 }
3068 else
3069 {
3070 const auto * ge = dynamic_cast<MythGestureEvent*>(Event);
3071 if (ge)
3072 {
3073 LOG(VB_GENERAL, LOG_INFO, LOC + QString("mythgesture: g:%1 pos:%2,%3 b:%4")
3074 .arg(ge->GetGesture()).arg(ge->GetPosition().x())
3075 .arg(ge->GetPosition().y()).arg(ge->GetButton()));
3076 }
3077 }
3078#endif
3079 bool handled = false;
3084 if (handled)
3085 return true;
3086 }
3087
3088 switch (Event->type())
3089 {
3090 case QEvent::Paint:
3091 case QEvent::UpdateRequest:
3092 case QEvent::Enter:
3093 // These events aren't received by a direct call from the Qt
3094 // event dispacther, but are received by way of the event
3095 // dispatcher calling TV::eventFilter(MainWindow, Event).
3096 return true;
3097 default:
3098 break;
3099 }
3100
3101 return QObject::event(Event);
3102}
3103
3104bool TV::HandleTrackAction(const QString &Action)
3105{
3106 bool handled = true;
3107
3110 else if (ACTION_ENABLEEXTTEXT == Action)
3112 else if (ACTION_DISABLEEXTTEXT == Action)
3114 else if (ACTION_ENABLEFORCEDSUBS == Action)
3115 emit ChangeAllowForcedSubtitles(true);
3116 else if (ACTION_DISABLEFORCEDSUBS == Action)
3117 emit ChangeAllowForcedSubtitles(false);
3118 else if (Action == ACTION_ENABLESUBS)
3119 emit SetCaptionsEnabled(true, true);
3120 else if (Action == ACTION_DISABLESUBS)
3121 emit SetCaptionsEnabled(false, true);
3123 {
3124 if (m_ccInputMode)
3125 {
3126 bool valid = false;
3127 int page = GetQueuedInputAsInt(&valid, 16);
3128 if (m_vbimode == VBIMode::PAL_TT && valid)
3129 emit SetTeletextPage(static_cast<uint>(page));
3130 else if (m_vbimode == VBIMode::NTSC_CC)
3131 emit SetTrack(kTrackTypeCC608, static_cast<uint>(std::clamp(page - 1, 0, 1)));
3132
3133 ClearInputQueues(true);
3134
3135 m_ccInputMode = false;
3136 if (m_ccInputTimerId)
3137 {
3139 m_ccInputTimerId = 0;
3140 }
3141 }
3143 {
3144 ClearInputQueues(false);
3146
3147 m_ccInputMode = true;
3148 m_asInputMode = false;
3150 if (m_asInputTimerId)
3151 {
3153 m_asInputTimerId = 0;
3154 }
3155 }
3156 else
3157 {
3158 emit ToggleCaptions();
3159 }
3160 }
3161 else if (Action.startsWith("TOGGLE"))
3162 {
3163 int type = to_track_type(Action.mid(6));
3165 emit EnableTeletext();
3166 else if (type >= kTrackTypeSubtitle)
3167 emit ToggleCaptionsByType(static_cast<uint>(type));
3168 else
3169 handled = false;
3170 }
3171 else if (Action.startsWith("SELECT"))
3172 {
3173 int type = to_track_type(Action.mid(6));
3174 uint num = Action.section("_", -1).toUInt();
3175 if (type >= kTrackTypeAudio)
3176 emit SetTrack(static_cast<uint>(type), num);
3177 else
3178 handled = false;
3179 }
3180 else if (Action.startsWith("NEXT") || Action.startsWith("PREV"))
3181 {
3182 int dir = (Action.startsWith("NEXT")) ? +1 : -1;
3183 int type = to_track_type(Action.mid(4));
3184 if (type >= kTrackTypeAudio)
3185 emit ChangeTrack(static_cast<uint>(type), dir);
3186 else if (Action.endsWith("CC"))
3187 emit ChangeCaptionTrack(dir);
3188 else
3189 handled = false;
3190 }
3191 else
3192 {
3193 handled = false;
3194 }
3195 return handled;
3196}
3197
3198// Make a special check for global system-related events.
3199//
3200// This check needs to be done early in the keypress event processing,
3201// because FF/REW processing causes unknown events to stop FF/REW, and
3202// manual zoom mode processing consumes all but a few event types.
3203// Ideally, we would just call MythScreenType::keyPressEvent()
3204// unconditionally, but we only want certain keypresses handled by
3205// that method.
3206//
3207// As a result, some of the MythScreenType::keyPressEvent() string
3208// compare logic is copied here.
3209static bool SysEventHandleAction(MythMainWindow* MainWindow, QKeyEvent *e, const QStringList &actions)
3210{
3211 QStringList::const_iterator it;
3212 for (it = actions.begin(); it != actions.end(); ++it)
3213 {
3214 if ((*it).startsWith("SYSEVENT") ||
3215 *it == ACTION_SCREENSHOT ||
3216 *it == ACTION_TVPOWERON ||
3217 *it == ACTION_TVPOWEROFF)
3218 {
3219 return MainWindow->GetMainStack()->GetTopScreen()->keyPressEvent(e);
3220 }
3221 }
3222 return false;
3223}
3224
3225QList<QKeyEvent*> TV::ConvertScreenPressKeyMap(const QString &KeyList)
3226{
3227 QList<QKeyEvent*> keyPressList;
3228 int i = 0;
3229 QStringList stringKeyList = KeyList.split(',');
3230 for (const auto & str : std::as_const(stringKeyList))
3231 {
3232 QKeySequence keySequence(str);
3233 for (i = 0; i < keySequence.count(); i++)
3234 {
3235#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
3236 int keynum = keySequence[i];
3237 int keyCode = keynum & ~Qt::KeyboardModifierMask;
3238 auto modifiers = static_cast<Qt::KeyboardModifiers>(keynum & Qt::KeyboardModifierMask);
3239#else
3240 int keyCode = keySequence[i].key();
3241 Qt::KeyboardModifiers modifiers = keySequence[i].keyboardModifiers();
3242#endif
3243 auto * keyEvent = new QKeyEvent(QEvent::None, keyCode, modifiers);
3244 keyPressList.append(keyEvent);
3245 }
3246 }
3247 if (stringKeyList.count() < kScreenPressRegionCount)
3248 {
3249 // add default remainders
3250 for(; i < kScreenPressRegionCount; i++)
3251 {
3252 auto * keyEvent = new QKeyEvent(QEvent::None, Qt::Key_Escape, Qt::NoModifier);
3253 keyPressList.append(keyEvent);
3254 }
3255 }
3256 return keyPressList;
3257}
3258
3259bool TV::TranslateGesture(const QString &Context, MythGestureEvent *Event,
3260 QStringList &Actions, bool IsLiveTV)
3261{
3262 if (Event && Context == "TV Playback")
3263 {
3264 // TODO make this configuable via a similar mechanism to
3265 // TranslateKeyPress
3266 // possibly with configurable hot zones of various sizes in a theme
3267 // TODO enhance gestures to support other non Click types too
3268 if ((Event->GetGesture() == MythGestureEvent::Click) &&
3269 (Event->GetButton() == Qt::LeftButton))
3270 {
3271 // divide screen into 12 regions
3272 QSize size = m_mainWindow->size();
3273 QPoint pos = Event->GetPosition();
3274 int region = 0;
3275 const int widthDivider = 4;
3276 int w4 = size.width() / widthDivider;
3277 region = pos.x() / w4;
3278 int h3 = size.height() / 3;
3279 region += (pos.y() / h3) * widthDivider;
3280
3281 if (IsLiveTV)
3282 return m_mainWindow->TranslateKeyPress(Context, m_screenPressKeyMapLiveTV[region], Actions, true);
3283 return m_mainWindow->TranslateKeyPress(Context, m_screenPressKeyMapPlayback[region], Actions, true);
3284 }
3285 return false;
3286 }
3287 return false;
3288}
3289
3290bool TV::TranslateKeyPressOrGesture(const QString &Context, QEvent *Event,
3291 QStringList &Actions, bool IsLiveTV, bool AllowJumps)
3292{
3293 if (Event)
3294 {
3295 if (QEvent::KeyPress == Event->type())
3296 return m_mainWindow->TranslateKeyPress(Context, dynamic_cast<QKeyEvent*>(Event), Actions, AllowJumps);
3297 if (MythGestureEvent::kEventType == Event->type())
3298 return TranslateGesture(Context, dynamic_cast<MythGestureEvent*>(Event), Actions, IsLiveTV);
3299 }
3300 return false;
3301}
3302
3304{
3305 if (Event == nullptr)
3306 return false;
3307
3308 bool ignoreKeys = m_playerContext.IsPlayerChangingBuffers();
3309
3310#if DEBUG_ACTIONS
3311 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("ignoreKeys: %1").arg(ignoreKeys));
3312#endif
3313
3314 if (m_idleTimerId)
3315 {
3318 }
3319
3320#ifdef Q_OS_LINUX
3321 // Fixups for _some_ linux native codes that QT doesn't know
3322 auto* eKeyEvent = dynamic_cast<QKeyEvent*>(Event);
3323 if (eKeyEvent) {
3324 if (eKeyEvent->key() <= 0)
3325 {
3326 int keycode = 0;
3327 switch(eKeyEvent->nativeScanCode())
3328 {
3329 case 209: // XF86AudioPause
3330 keycode = Qt::Key_MediaPause;
3331 break;
3332 default:
3333 break;
3334 }
3335
3336 if (keycode > 0)
3337 {
3338 auto *key = new QKeyEvent(QEvent::KeyPress, keycode, eKeyEvent->modifiers());
3339 QCoreApplication::postEvent(this, key);
3340 }
3341 }
3342 }
3343#endif
3344
3345 QStringList actions;
3346 bool handled = false;
3347 bool alreadyTranslatedPlayback = false;
3348
3349 TVState state = GetState();
3350 bool isLiveTV = StateIsLiveTV(state);
3351
3352 if (ignoreKeys)
3353 {
3354 handled = TranslateKeyPressOrGesture("TV Playback", Event, actions, isLiveTV);
3355 alreadyTranslatedPlayback = true;
3356
3357 if (handled || actions.isEmpty())
3358 return handled;
3359
3360 bool esc = IsActionable({ "ESCAPE", "BACK" }, actions);
3361 bool pause = IsActionable(ACTION_PAUSE, actions);
3362 bool play = IsActionable(ACTION_PLAY, actions);
3363
3364 if ((!esc || m_overlayState.m_browsing) && !pause && !play)
3365 return false;
3366 }
3367
3368 OSD *osd = GetOSDL();
3369 if (osd && osd->DialogVisible())
3370 {
3371 if (QEvent::KeyPress == Event->type())
3372 {
3373 auto *qke = dynamic_cast<QKeyEvent*>(Event);
3374 handled = (qke != nullptr) && osd->DialogHandleKeypress(qke);
3375 }
3376 if (MythGestureEvent::kEventType == Event->type())
3377 {
3378 auto *mge = dynamic_cast<MythGestureEvent*>(Event);
3379 handled = (mge != nullptr) && osd->DialogHandleGesture(mge);
3380 }
3381 }
3382 ReturnOSDLock();
3383
3384 if (m_overlayState.m_editing && !handled)
3385 {
3386 handled |= TranslateKeyPressOrGesture("TV Editing", Event, actions, isLiveTV);
3387
3388 if (!handled && m_player)
3389 {
3390 if (IsActionable("MENU", actions))
3391 {
3392 ShowOSDCutpoint("EDIT_CUT_POINTS");
3393 handled = true;
3394 }
3395 if (IsActionable(ACTION_MENUCOMPACT, actions))
3396 {
3397 ShowOSDCutpoint("EDIT_CUT_POINTS_COMPACT");
3398 handled = true;
3399 }
3400 if (IsActionable("ESCAPE", actions))
3401 {
3402 emit RefreshEditorState(true);
3404 ShowOSDCutpoint("EXIT_EDIT_MODE");
3405 else
3406 emit DisableEdit(0);
3407 handled = true;
3408 }
3409 else
3410 {
3411 emit RefreshEditorState();
3414 {
3415 ShowOSDCutpoint("EDIT_CUT_POINTS");
3416 handled = true;
3417 }
3418 else
3419 {
3420 handled |= m_player->HandleProgramEditorActions(actions);
3421 }
3422 }
3423 }
3424 }
3425
3426 if (handled)
3427 return true;
3428
3429 // If text is already queued up, be more lax on what is ok.
3430 // This allows hex teletext entry and minor channel entry.
3431 if (QEvent::KeyPress == Event->type())
3432 {
3433 auto *qke = dynamic_cast<QKeyEvent*>(Event);
3434 if (qke == nullptr)
3435 return false;
3436 const QString txt = qke->text();
3437 if (HasQueuedInput() && (1 == txt.length()))
3438 {
3439 bool ok = false;
3440 (void)txt.toInt(&ok, 16);
3441 if (ok || txt=="_" || txt=="-" || txt=="#" || txt==".")
3442 {
3443 AddKeyToInputQueue(txt.at(0).toLatin1());
3444 return true;
3445 }
3446 }
3447 }
3448
3449 // Teletext menu
3451 {
3452 QStringList tt_actions;
3453 handled = TranslateKeyPressOrGesture("Teletext Menu", Event, tt_actions, isLiveTV);
3454
3455 if (!handled && !tt_actions.isEmpty())
3456 {
3457 for (const QString& action : std::as_const(tt_actions))
3458 {
3459 emit HandleTeletextAction(action, handled);
3460 if (handled)
3461 return true;
3462 }
3463 }
3464 }
3465
3466 // Interactive television
3468 {
3469 if (!alreadyTranslatedPlayback)
3470 {
3471 handled = TranslateKeyPressOrGesture("TV Playback", Event, actions, isLiveTV);
3472 alreadyTranslatedPlayback = true;
3473 }
3474
3475 if (!handled && !actions.isEmpty())
3476 {
3477 for (const QString& action : std::as_const(actions))
3478 {
3479 emit HandleITVAction(action, handled);
3480 if (handled)
3481 return true;
3482 }
3483 }
3484 }
3485
3486 if (!alreadyTranslatedPlayback)
3487 handled = TranslateKeyPressOrGesture("TV Playback", Event, actions, isLiveTV);
3488
3489 if (handled || actions.isEmpty())
3490 return handled;
3491
3492 handled = false;
3493
3496
3497 if (QEvent::KeyPress == Event->type())
3498 handled = handled || SysEventHandleAction(m_mainWindow, dynamic_cast<QKeyEvent*>(Event), actions);
3499 handled = handled || BrowseHandleAction(actions);
3500 handled = handled || ManualZoomHandleAction(actions);
3501 handled = handled || PictureAttributeHandleAction(actions);
3502 handled = handled || TimeStretchHandleAction(actions);
3503 handled = handled || AudioSyncHandleAction(actions);
3504 handled = handled || SubtitleZoomHandleAction(actions);
3505 handled = handled || SubtitleDelayHandleAction(actions);
3506 handled = handled || DiscMenuHandleAction(actions);
3507 handled = handled || ActiveHandleAction(actions, isDVD, isMenuOrStill);
3508 handled = handled || ToggleHandleAction(actions, isDVD);
3509 handled = handled || FFRewHandleAction(actions);
3510 handled = handled || ActivePostQHandleAction(actions);
3511
3512#if DEBUG_ACTIONS
3513 for (int i = 0; i < actions.size(); ++i)
3514 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("handled(%1) actions[%2](%3)")
3515 .arg(handled).arg(i).arg(actions[i]));
3516#endif // DEBUG_ACTIONS
3517
3518 if (handled)
3519 return true;
3520
3521 if (!handled)
3522 {
3523 for (int i = 0; i < actions.size() && !handled; i++)
3524 {
3525 const QString& action = actions[i];
3526 bool ok = false;
3527 int val = action.toInt(&ok);
3528
3529 if (ok)
3530 {
3531 AddKeyToInputQueue(static_cast<char>('0' + val));
3532 handled = true;
3533 }
3534 }
3535 }
3536
3537 return true;
3538}
3539
3540bool TV::BrowseHandleAction(const QStringList &Actions)
3541{
3543 return false;
3544
3545 bool handled = true;
3546
3547 if (IsActionable({ ACTION_UP, ACTION_CHANNELUP }, Actions))
3549 else if (IsActionable( { ACTION_DOWN, ACTION_CHANNELDOWN }, Actions))
3551 else if (IsActionable(ACTION_LEFT, Actions))
3553 else if (IsActionable(ACTION_RIGHT, Actions))
3555 else if (IsActionable("NEXTFAV", Actions))
3557 else if (IsActionable(ACTION_SELECT, Actions))
3558 BrowseEnd(true);
3559 else if (IsActionable({ ACTION_CLEAROSD, "ESCAPE", "BACK", "TOGGLEBROWSE" }, Actions))
3560 BrowseEnd(false);
3561 else if (IsActionable(ACTION_TOGGLERECORD, Actions))
3562 QuickRecord();
3563 else
3564 {
3565 handled = false;
3566 for (const auto& action : std::as_const(Actions))
3567 {
3568 if (action.length() == 1 && action[0].isDigit())
3569 {
3570 AddKeyToInputQueue(action[0].toLatin1());
3571 handled = true;
3572 }
3573 }
3574 }
3575
3576 // only pass-through actions listed below
3577 static const QStringList passthrough =
3578 {
3579 ACTION_VOLUMEUP, ACTION_VOLUMEDOWN, "STRETCHINC", "STRETCHDEC",
3580 ACTION_MUTEAUDIO, "CYCLEAUDIOCHAN", "BOTTOMLINEMOVE", "BOTTOMLINESAVE", "TOGGLEASPECT"
3581 };
3582 return handled || !IsActionable(passthrough, Actions);
3583}
3584
3585bool TV::ManualZoomHandleAction(const QStringList &Actions)
3586{
3587 if (!m_zoomMode)
3588 return false;
3589
3590 bool endmanualzoom = false;
3591 bool handled = true;
3592 bool updateOSD = true;
3593 ZoomDirection zoom = kZoom_END;
3595 zoom = kZoomUp;
3597 zoom = kZoomDown;
3598 else if (IsActionable({ ACTION_ZOOMLEFT, ACTION_LEFT }, Actions))
3599 zoom = kZoomLeft;
3600 else if (IsActionable({ ACTION_ZOOMRIGHT, ACTION_RIGHT }, Actions))
3601 zoom = kZoomRight;
3602 else if (IsActionable({ ACTION_ZOOMASPECTUP, ACTION_VOLUMEUP }, Actions))
3603 zoom = kZoomAspectUp;
3605 zoom = kZoomAspectDown;
3606 else if (IsActionable({ ACTION_ZOOMIN, ACTION_JUMPFFWD }, Actions))
3607 zoom = kZoomIn;
3608 else if (IsActionable({ ACTION_ZOOMOUT, ACTION_JUMPRWND }, Actions))
3609 zoom = kZoomOut;
3610 else if (IsActionable(ACTION_ZOOMVERTICALIN, Actions))
3611 zoom = kZoomVerticalIn;
3612 else if (IsActionable(ACTION_ZOOMVERTICALOUT, Actions))
3613 zoom = kZoomVerticalOut;
3614 else if (IsActionable(ACTION_ZOOMHORIZONTALIN, Actions))
3615 zoom = kZoomHorizontalIn;
3616 else if (IsActionable(ACTION_ZOOMHORIZONTALOUT, Actions))
3617 zoom = kZoomHorizontalOut;
3618 else if (IsActionable({ ACTION_ZOOMQUIT, "ESCAPE", "BACK" }, Actions))
3619 {
3620 zoom = kZoomHome;
3621 endmanualzoom = true;
3622 }
3623 else if (IsActionable({ ACTION_ZOOMCOMMIT, ACTION_SELECT }, Actions))
3624 {
3625 endmanualzoom = true;
3626 SetManualZoom(false, tr("Zoom Committed"));
3627 }
3628 else
3629 {
3630 updateOSD = false;
3631 // only pass-through actions listed below
3632 static const QStringList passthrough =
3633 {
3634 "STRETCHINC", "STRETCHDEC", ACTION_MUTEAUDIO,
3635 "CYCLEAUDIOCHAN", ACTION_PAUSE, ACTION_CLEAROSD
3636 };
3637 handled = !IsActionable(passthrough, Actions);
3638 }
3639
3640 QString msg = tr("Zoom Committed");
3641 if (zoom != kZoom_END)
3642 {
3643 emit ChangeZoom(zoom);
3644 msg = endmanualzoom ? tr("Zoom Ignored") :
3648 }
3649 else if (endmanualzoom)
3650 {
3651 msg = tr("%1 Committed").arg(GetZoomString(m_videoBoundsState.m_manualHorizScale,
3654 }
3655
3656 if (updateOSD)
3657 SetManualZoom(!endmanualzoom, msg);
3658
3659 return handled;
3660}
3661
3662bool TV::PictureAttributeHandleAction(const QStringList &Actions)
3663{
3664 if (!m_adjustingPicture)
3665 return false;
3666
3667 bool up = IsActionable(ACTION_RIGHT, Actions);
3668 bool down = up ? false : IsActionable(ACTION_LEFT, Actions);
3669 if (!(up || down))
3670 return false;
3671
3673 {
3675 VolumeChange(up);
3676 else
3678 return true;
3679 }
3680
3681 int value = 99;
3685 UpdateOSDStatus(toTitleString(m_adjustingPicture), text, QString::number(value),
3687 emit ChangeOSDPositionUpdates(false);
3688 return true;
3689}
3690
3691bool TV::TimeStretchHandleAction(const QStringList &Actions)
3692{
3694 return false;
3695
3696 bool handled = true;
3697
3698 if (IsActionable(ACTION_LEFT, Actions))
3700 else if (IsActionable(ACTION_RIGHT, Actions))
3702 else if (IsActionable(ACTION_DOWN, Actions))
3704 else if (IsActionable(ACTION_UP, Actions))
3706 else if (IsActionable("ADJUSTSTRETCH", Actions))
3708 else if (IsActionable(ACTION_SELECT, Actions))
3709 ClearOSD();
3710 else
3711 handled = false;
3712
3713 return handled;
3714}
3715
3716bool TV::AudioSyncHandleAction(const QStringList& Actions)
3717{
3719 return false;
3720
3721 bool handled = true;
3722
3723 if (IsActionable(ACTION_LEFT, Actions))
3724 emit ChangeAudioOffset(-1ms);
3725 else if (IsActionable(ACTION_RIGHT, Actions))
3726 emit ChangeAudioOffset(1ms);
3727 else if (IsActionable(ACTION_UP, Actions))
3728 emit ChangeAudioOffset(10ms);
3729 else if (IsActionable(ACTION_DOWN, Actions))
3730 emit ChangeAudioOffset(-10ms);
3731 else if (IsActionable({ ACTION_TOGGELAUDIOSYNC, ACTION_SELECT }, Actions))
3732 ClearOSD();
3733 else
3734 handled = false;
3735
3736 return handled;
3737}
3738
3739bool TV::SubtitleZoomHandleAction(const QStringList &Actions)
3740{
3742 return false;
3743
3744 bool handled = true;
3745
3746 if (IsActionable(ACTION_LEFT, Actions))
3747 emit AdjustSubtitleZoom(-1);
3748 else if (IsActionable(ACTION_RIGHT, Actions))
3749 emit AdjustSubtitleZoom(1);
3750 else if (IsActionable(ACTION_UP, Actions))
3751 emit AdjustSubtitleZoom(10);
3752 else if (IsActionable(ACTION_DOWN, Actions))
3753 emit AdjustSubtitleZoom(-10);
3755 ClearOSD();
3756 else
3757 handled = false;
3758
3759 return handled;
3760}
3761
3762bool TV::SubtitleDelayHandleAction(const QStringList &Actions)
3763{
3765 return false;
3766
3767 bool handled = true;
3768
3769 if (IsActionable(ACTION_LEFT, Actions))
3770 emit AdjustSubtitleDelay(-5ms);
3771 else if (IsActionable(ACTION_RIGHT, Actions))
3772 emit AdjustSubtitleDelay(5ms);
3773 else if (IsActionable(ACTION_UP, Actions))
3774 emit AdjustSubtitleDelay(25ms);
3775 else if (IsActionable(ACTION_DOWN, Actions))
3776 emit AdjustSubtitleDelay(-25ms);
3778 ClearOSD();
3779 else
3780 handled = false;
3781
3782 return handled;
3783}
3784
3785bool TV::DiscMenuHandleAction(const QStringList& Actions) const
3786{
3787 mpeg::chrono::pts pts = 0_pts;
3789 if (output)
3790 {
3791 MythVideoFrame *frame = output->GetLastShownFrame();
3792 // convert timecode (msec) to pts (90kHz)
3793 if (frame)
3794 pts = duration_cast<mpeg::chrono::pts>(frame->m_timecode);
3795 }
3797 return m_playerContext.m_buffer->HandleAction(Actions, pts);
3798 return false;
3799}
3800
3801bool TV::ActiveHandleAction(const QStringList &Actions,
3802 bool IsDVD, bool IsDVDStillFrame)
3803{
3804 bool handled = true;
3805
3806 if (IsActionable("SKIPCOMMERCIAL", Actions) && !IsDVD)
3808 else if (IsActionable("SKIPCOMMBACK", Actions) && !IsDVD)
3810 else if (IsActionable("QUEUETRANSCODE", Actions) && !IsDVD)
3811 DoQueueTranscode("Default");
3812 else if (IsActionable("QUEUETRANSCODE_AUTO", Actions) && !IsDVD)
3813 DoQueueTranscode("Autodetect");
3814 else if (IsActionable("QUEUETRANSCODE_HIGH", Actions) && !IsDVD)
3815 DoQueueTranscode("High Quality");
3816 else if (IsActionable("QUEUETRANSCODE_MEDIUM", Actions) && !IsDVD)
3817 DoQueueTranscode("Medium Quality");
3818 else if (IsActionable("QUEUETRANSCODE_LOW", Actions) && !IsDVD)
3819 DoQueueTranscode("Low Quality");
3820 else if (IsActionable(ACTION_PLAY, Actions))
3821 DoPlay();
3822 else if (IsActionable(ACTION_PAUSE, Actions))
3823 DoTogglePause(true);
3824 else if (IsActionable("SPEEDINC", Actions) && !IsDVDStillFrame)
3825 ChangeSpeed(1);
3826 else if (IsActionable("SPEEDDEC", Actions) && !IsDVDStillFrame)
3827 ChangeSpeed(-1);
3828 else if (IsActionable("ADJUSTSTRETCH", Actions))
3829 ChangeTimeStretch(0); // just display
3830 else if (IsActionable("CYCLECOMMSKIPMODE",Actions) && !IsDVD)
3832 else if (IsActionable("NEXTSCAN", Actions))
3833 {
3834 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
3836 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
3838 }
3839 else if (IsActionable(ACTION_SEEKARB, Actions) && !IsDVD)
3840 {
3841 if (m_asInputMode)
3842 {
3843 ClearInputQueues(true);
3844 emit ChangeOSDText(OSD_WIN_INPUT, {{"osd_number_entry", tr("Seek:")}}, kOSDTimeout_Med);
3845 m_asInputMode = false;
3846 if (m_asInputTimerId)
3847 {
3849 m_asInputTimerId = 0;
3850 }
3851 }
3852 else
3853 {
3854 ClearInputQueues(false);
3856 m_asInputMode = true;
3857 m_ccInputMode = false;
3859 if (m_ccInputTimerId)
3860 {
3862 m_ccInputTimerId = 0;
3863 }
3864 }
3865 }
3866 else if (IsActionable(ACTION_JUMPRWND, Actions))
3867 {
3868 DoJumpRWND();
3869 }
3870 else if (IsActionable(ACTION_JUMPFFWD, Actions))
3871 {
3872 DoJumpFFWD();
3873 }
3874 else if (IsActionable(ACTION_JUMPBKMRK, Actions))
3875 {
3876 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
3877 uint64_t bookmark = m_player->GetBookmark();
3878 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
3879
3880 if (bookmark)
3881 {
3882 DoPlayerSeekToFrame(bookmark);
3883 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
3884 UpdateOSDSeekMessage(tr("Jump to Bookmark"), kOSDTimeout_Med);
3885 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
3886 }
3887 }
3888 else if (IsActionable(ACTION_JUMPSTART,Actions))
3889 {
3890 DoSeek(0, tr("Jump to Beginning"), /*timeIsOffset*/false, /*honorCutlist*/true);
3891 }
3892 else if (IsActionable(ACTION_CLEAROSD, Actions))
3893 {
3894 ClearOSD();
3895 }
3896 else if (IsActionable(ACTION_VIEWSCHEDULED, Actions))
3897 {
3899 }
3900 else if (HandleJumpToProgramAction(Actions))
3901 { // NOLINT(bugprone-branch-clone)
3902 }
3903 else if (IsActionable(ACTION_SIGNALMON, Actions))
3904 {
3906 {
3907 QString input = m_playerContext.m_recorder->GetInput();
3909
3910 if (timeout == 0xffffffff)
3911 {
3912 emit ChangeOSDMessage("No Signal Monitor");
3913 return false;
3914 }
3915
3916 std::chrono::milliseconds rate = m_sigMonMode ? 0ms : 100ms;
3917 bool notify = !m_sigMonMode;
3918
3919 PauseLiveTV();
3921 UnpauseLiveTV();
3922
3923 m_lockTimerOn = false;
3925 }
3926 }
3927 else if (IsActionable(ACTION_SCREENSHOT, Actions))
3928 {
3930 }
3931 else if (IsActionable(ACTION_STOP, Actions))
3932 {
3933 PrepareToExitPlayer(__LINE__);
3934 SetExitPlayer(true, true);
3935 }
3936 else if (IsActionable(ACTION_EXITSHOWNOPROMPTS, Actions))
3937 {
3938 m_requestDelete = false;
3939 PrepareToExitPlayer(__LINE__);
3940 SetExitPlayer(true, true);
3941 }
3942 else if (IsActionable({ "ESCAPE", "BACK" }, Actions))
3943 {
3946 {
3947 ClearOSD();
3948 }
3949 else
3950 {
3951 bool visible = false;
3952 emit IsOSDVisible(visible);
3953 if (visible)
3954 {
3955 ClearOSD();
3956 return handled;
3957 }
3958 }
3959
3960 NormalSpeed();
3961 StopFFRew();
3962 bool exit = false;
3963 if (StateIsLiveTV(GetState()))
3964 {
3966 {
3968 return handled;
3969 }
3970 exit = true;
3971 }
3972 else
3973 {
3975 !m_underNetworkControl && !IsDVDStillFrame)
3976 {
3978 return handled;
3979 }
3980 if (16 & m_dbPlaybackExitPrompt)
3981 {
3982 m_clearPosOnExit = true;
3983 }
3984 PrepareToExitPlayer(__LINE__);
3985 m_requestDelete = false;
3986 exit = true;
3987 }
3988
3989 if (exit)
3990 {
3991 // If it's a DVD, and we're not trying to execute a
3992 // jumppoint, try to back up.
3993 if (IsDVD && !m_mainWindow->IsExitingToMain() && IsActionable("BACK", Actions) &&
3995 {
3996 return handled;
3997 }
3998 SetExitPlayer(true, true);
3999 }
4000 }
4001 else if (IsActionable(ACTION_ENABLEUPMIX, Actions))
4002 {
4003 emit ChangeUpmix(true);
4004 }
4005 else if (IsActionable(ACTION_DISABLEUPMIX, Actions))
4006 {
4007 emit ChangeUpmix(false);
4008 }
4009 else if (IsActionable(ACTION_VOLUMEDOWN, Actions))
4010 {
4011 VolumeChange(false);
4012 }
4013 else if (IsActionable(ACTION_VOLUMEUP, Actions))
4014 {
4015 VolumeChange(true);
4016 }
4017 else if (IsActionable("CYCLEAUDIOCHAN", Actions))
4018 {
4019 emit ChangeMuteState(true);
4020 }
4021 else if (IsActionable(ACTION_MUTEAUDIO, Actions))
4022 {
4023 emit ChangeMuteState();
4024 }
4025 else if (IsActionable("STRETCHINC", Actions))
4026 {
4028 }
4029 else if (IsActionable("STRETCHDEC", Actions))
4030 {
4032 }
4033 else if (IsActionable("MENU", Actions))
4034 {
4035 ShowOSDMenu();
4036 }
4037 else if (IsActionable(ACTION_MENUCOMPACT, Actions))
4038 {
4039 ShowOSDMenu(true);
4040 }
4041 else if (IsActionable({ "INFO", "INFOWITHCUTLIST" }, Actions))
4042 {
4043 if (HasQueuedInput())
4044 DoArbSeek(ARBSEEK_SET, IsActionable("INFOWITHCUTLIST", Actions));
4045 else
4046 ToggleOSD(true);
4047 }
4048 else if (IsActionable(ACTION_TOGGLEOSDDEBUG, Actions))
4049 {
4050 emit ChangeOSDDebug();
4051 }
4052 else if (!IsDVDStillFrame && SeekHandleAction(Actions, IsDVD))
4053 {
4054 }
4055 else if (IsActionable(ACTION_SELECT, Actions) && HasQueuedChannel())
4056 {
4058 }
4059 else
4060 {
4061 handled = false;
4062 for (auto it = Actions.cbegin(); it != Actions.cend() && !handled; ++it)
4063 handled = HandleTrackAction(*it);
4064 }
4065
4066 return handled;
4067}
4068
4069bool TV::FFRewHandleAction(const QStringList &Actions)
4070{
4071 bool handled = false;
4072
4074 {
4075 for (int i = 0; i < Actions.size() && !handled; i++)
4076 {
4077 const QString& action = Actions[i];
4078 bool ok = false;
4079 int val = action.toInt(&ok);
4080
4081 if (ok && val < static_cast<int>(m_ffRewSpeeds.size()))
4082 {
4083 SetFFRew(val);
4084 handled = true;
4085 }
4086 }
4087
4088 if (!handled)
4089 {
4092 handled = true;
4093 }
4094 }
4095
4097 {
4098 NormalSpeed();
4100 handled = true;
4101 }
4102
4103 return handled;
4104}
4105
4106bool TV::ToggleHandleAction(const QStringList &Actions, bool IsDVD)
4107{
4108 bool handled = true;
4109 bool islivetv = StateIsLiveTV(GetState());
4110
4111 if (IsActionable(ACTION_BOTTOMLINEMOVE, Actions))
4112 emit ToggleMoveBottomLine();
4113 else if (IsActionable(ACTION_BOTTOMLINESAVE, Actions))
4114 emit SaveBottomLine();
4115 else if (IsActionable("TOGGLEASPECT", Actions))
4116 emit ChangeAspectOverride();
4117 else if (IsActionable("TOGGLEFILL", Actions))
4118 emit ChangeAdjustFill();
4119 else if (IsActionable(ACTION_TOGGELAUDIOSYNC, Actions))
4120 emit ChangeAudioOffset(0ms); // just display
4121 else if (IsActionable(ACTION_TOGGLESUBTITLEZOOM, Actions))
4122 emit AdjustSubtitleZoom(0); // just display
4123 else if (IsActionable(ACTION_TOGGLESUBTITLEDELAY, Actions))
4124 emit AdjustSubtitleDelay(0ms); // just display
4125 else if (IsActionable(ACTION_TOGGLEVISUALISATION, Actions))
4126 emit EnableVisualiser(false, true);
4127 else if (IsActionable(ACTION_ENABLEVISUALISATION, Actions))
4128 emit EnableVisualiser(true);
4129 else if (IsActionable(ACTION_DISABLEVISUALISATION, Actions))
4130 emit EnableVisualiser(false);
4131 else if (IsActionable("TOGGLEPICCONTROLS", Actions))
4133 else if (IsActionable("TOGGLESTRETCH", Actions))
4135 else if (IsActionable(ACTION_TOGGLEUPMIX, Actions))
4136 emit ChangeUpmix(false, true);
4137 else if (IsActionable(ACTION_TOGGLESLEEP, Actions))
4139 else if (IsActionable(ACTION_TOGGLERECORD, Actions) && islivetv)
4140 QuickRecord();
4141 else if (IsActionable(ACTION_TOGGLEFAV, Actions) && islivetv)
4143 else if (IsActionable(ACTION_TOGGLECHANCONTROLS, Actions) && islivetv)
4145 else if (IsActionable(ACTION_TOGGLERECCONTROLS, Actions) && islivetv)
4147 else if (IsActionable("TOGGLEBROWSE", Actions))
4148 {
4149 if (islivetv)
4150 BrowseStart();
4151 else if (!IsDVD)
4152 ShowOSDMenu();
4153 else
4154 handled = false;
4155 }
4156 else if (IsActionable("EDIT", Actions))
4157 {
4158 if (islivetv)
4160 else if (!IsDVD)
4162 }
4163 else if (IsActionable(ACTION_OSDNAVIGATION, Actions))
4164 {
4166 }
4167 else
4168 {
4169 handled = false;
4170 }
4171
4172 return handled;
4173}
4174
4176{
4177 if (Clear)
4178 {
4179 emit UpdateBookmark(true);
4180 emit ChangeOSDMessage(tr("Bookmark Cleared"));
4181 }
4182 else // if (IsBookmarkAllowed(ctx))
4183 {
4184 emit UpdateBookmark();
4185 osdInfo info;
4187 info.text["title"] = tr("Position");
4189 emit ChangeOSDMessage(tr("Bookmark Saved"));
4190 }
4191}
4192
4193bool TV::ActivePostQHandleAction(const QStringList &Actions)
4194{
4195 bool handled = true;
4196 TVState state = GetState();
4197 bool islivetv = StateIsLiveTV(state);
4198 bool isdvd = state == kState_WatchingDVD;
4199 bool isdisc = isdvd || state == kState_WatchingBD;
4200
4201 if (IsActionable(ACTION_SETBOOKMARK, Actions))
4202 {
4203 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4204 SetBookmark(false);
4205 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4206 }
4207 if (IsActionable(ACTION_TOGGLEBOOKMARK, Actions))
4208 {
4209 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4211 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4212 }
4213 else if (IsActionable("NEXTFAV", Actions) && islivetv)
4214 {
4216 }
4217 else if (IsActionable("NEXTSOURCE", Actions) && islivetv)
4218 {
4220 }
4221 else if (IsActionable("PREVSOURCE", Actions) && islivetv)
4222 {
4224 }
4225 else if (IsActionable("NEXTINPUT", Actions) && islivetv)
4226 {
4227 SwitchInputs();
4228 }
4229 else if (IsActionable(ACTION_GUIDE, Actions))
4230 {
4232 }
4233 else if (IsActionable("PREVCHAN", Actions) && islivetv)
4234 {
4235 PopPreviousChannel(false);
4236 }
4237 else if (IsActionable(ACTION_CHANNELUP, Actions))
4238 {
4239 if (islivetv)
4240 {
4241 if (m_dbBrowseAlways)
4243 else
4245 }
4246 else
4247 {
4248 DoJumpRWND();
4249 }
4250 }
4251 else if (IsActionable(ACTION_CHANNELDOWN, Actions))
4252 {
4253 if (islivetv)
4254 {
4255 if (m_dbBrowseAlways)
4257 else
4259 }
4260 else
4261 {
4262 DoJumpFFWD();
4263 }
4264 }
4265 else if (IsActionable("DELETE", Actions) && !islivetv)
4266 {
4267 NormalSpeed();
4268 StopFFRew();
4269 PrepareToExitPlayer(__LINE__);
4270 ShowOSDPromptDeleteRecording(tr("Are you sure you want to delete:"));
4271 }
4272 else if (IsActionable(ACTION_JUMPTODVDROOTMENU, Actions) && isdisc)
4273 {
4274 emit GoToMenu("root");
4275 }
4276 else if (IsActionable(ACTION_JUMPTODVDCHAPTERMENU, Actions) && isdisc)
4277 {
4278 emit GoToMenu("chapter");
4279 }
4280 else if (IsActionable(ACTION_JUMPTODVDTITLEMENU, Actions) && isdisc)
4281 {
4282 emit GoToMenu("title");
4283 }
4284 else if (IsActionable(ACTION_JUMPTOPOPUPMENU, Actions) && isdisc)
4285 {
4286 emit GoToMenu("popup");
4287 }
4288 else if (IsActionable(ACTION_FINDER, Actions))
4289 {
4291 }
4292 else
4293 {
4294 handled = false;
4295 }
4296
4297 return handled;
4298}
4299
4300
4302{
4303 bool ignoreKeys = m_playerContext.IsPlayerChangingBuffers();
4304
4305#ifdef DEBUG_ACTIONS
4306 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("(%1) ignoreKeys: %2").arg(Command).arg(ignoreKeys));
4307#endif
4308
4309 if (ignoreKeys)
4310 {
4311 LOG(VB_GENERAL, LOG_WARNING, LOC + "Ignoring network control command because ignoreKeys is set");
4312 return;
4313 }
4314
4315 QStringList tokens = Command.split(" ", Qt::SkipEmptyParts);
4316 if (tokens.size() < 2)
4317 {
4318 LOG(VB_GENERAL, LOG_ERR, LOC + "Not enough tokens in network control command " + QString("'%1'").arg(Command));
4319 return;
4320 }
4321
4322 OSD *osd = GetOSDL();
4323 bool dlg = false;
4324 if (osd)
4325 dlg = osd->DialogVisible();
4326 ReturnOSDLock();
4327
4328 if (dlg)
4329 {
4330 LOG(VB_GENERAL, LOG_WARNING, LOC +
4331 "Ignoring network control command\n\t\t\t" +
4332 QString("because dialog is waiting for a response"));
4333 return;
4334 }
4335
4336 if (tokens[1] != "QUERY")
4337 ClearOSD();
4338
4339 if (tokens.size() == 3 && tokens[1] == "CHANID")
4340 {
4341 m_queuedChanID = tokens[2].toUInt();
4342 m_queuedChanNum.clear();
4344 }
4345 else if (tokens.size() == 3 && tokens[1] == "CHANNEL")
4346 {
4347 if (StateIsLiveTV(GetState()))
4348 {
4349 static const QRegularExpression kChannelNumRE { R"(^[-\.\d_#]+$)" };
4350 if (tokens[2] == "UP")
4352 else if (tokens[2] == "DOWN")
4354 else if (tokens[2].contains(kChannelNumRE))
4355 ChangeChannel(0, tokens[2]);
4356 }
4357 }
4358 else if (tokens.size() == 3 && tokens[1] == "SPEED")
4359 {
4360 bool paused = ContextIsPaused(__FILE__, __LINE__);
4361
4362 if (tokens[2] == "0x")
4363 {
4364 NormalSpeed();
4365 StopFFRew();
4366 if (!paused)
4367 DoTogglePause(true);
4368 }
4369 else if (tokens[2] == "normal")
4370 {
4371 NormalSpeed();
4372 StopFFRew();
4373 if (paused)
4374 DoTogglePause(true);
4375 return;
4376 }
4377 else
4378 {
4379 static const QRegularExpression kSpeedRE { R"(^\-*(\d*\.)?\d+x$)" };
4380 float tmpSpeed = 1.0F;
4381 bool ok = false;
4382
4383 if (tokens[2].contains(kSpeedRE))
4384 {
4385 QString speed = tokens[2].left(tokens[2].length()-1);
4386 tmpSpeed = speed.toFloat(&ok);
4387 }
4388 else
4389 {
4390 static const QRegularExpression re { R"(^(\-*\d+)\/(\d+)x$)" };
4391 auto match = re.match(tokens[2]);
4392 if (match.hasMatch())
4393 {
4394 QStringList matches = match.capturedTexts();
4395 int numerator = matches[1].toInt(&ok);
4396 int denominator = matches[2].toInt(&ok);
4397
4398 if (ok && denominator != 0)
4399 tmpSpeed = static_cast<float>(numerator) / static_cast<float>(denominator);
4400 else
4401 ok = false;
4402 }
4403 }
4404
4405 if (ok)
4406 {
4407 float searchSpeed = fabs(tmpSpeed);
4408
4409 if (paused)
4410 DoTogglePause(true);
4411
4412 if (tmpSpeed == 0.0F)
4413 {
4414 NormalSpeed();
4415 StopFFRew();
4416
4417 if (!paused)
4418 DoTogglePause(true);
4419 }
4420 else if (tmpSpeed == 1.0F)
4421 {
4422 StopFFRew();
4424 ChangeTimeStretch(0, false);
4425 return;
4426 }
4427
4428 NormalSpeed();
4429
4430 size_t index = 0;
4431 for ( ; index < m_ffRewSpeeds.size(); index++)
4432 if (m_ffRewSpeeds[index] == static_cast<int>(searchSpeed))
4433 break;
4434
4435 if ((index < m_ffRewSpeeds.size()) && (m_ffRewSpeeds[index] == static_cast<int>(searchSpeed)))
4436 {
4437 if (tmpSpeed < 0)
4439 else if (tmpSpeed > 1)
4441 else
4442 StopFFRew();
4443
4445 SetFFRew(static_cast<int>(index));
4446 }
4447 else if (0.125F <= tmpSpeed && tmpSpeed <= 2.0F)
4448 {
4449 StopFFRew();
4450 m_playerContext.m_tsNormal = tmpSpeed; // alter speed before display
4451 ChangeTimeStretch(0, false);
4452 }
4453 else
4454 {
4455 LOG(VB_GENERAL, LOG_WARNING, QString("Couldn't find %1 speed. Setting Speed to 1x")
4456 .arg(static_cast<double>(searchSpeed)));
4459 }
4460 }
4461 else
4462 {
4463 LOG(VB_GENERAL, LOG_ERR, QString("Found an unknown speed of %1").arg(tokens[2]));
4464 }
4465 }
4466 }
4467 else if (tokens.size() == 2 && tokens[1] == "STOP")
4468 {
4469 PrepareToExitPlayer(__LINE__);
4470 SetExitPlayer(true, true);
4471 }
4472 else if (tokens.size() >= 3 && tokens[1] == "SEEK" && m_playerContext.HasPlayer())
4473 {
4474 static const QRegularExpression kDigitsRE { "^\\d+$" };
4476 return;
4477
4478 if (tokens[2] == "BEGINNING")
4479 {
4480 DoSeek(0, tr("Jump to Beginning"), /*timeIsOffset*/false, /*honorCutlist*/true);
4481 }
4482 else if (tokens[2] == "FORWARD")
4483 {
4484 DoSeek(m_playerContext.m_fftime, tr("Skip Ahead"), /*timeIsOffset*/true, /*honorCutlist*/true);
4485 }
4486 else if (tokens[2] == "BACKWARD")
4487 {
4488 DoSeek(-m_playerContext.m_rewtime, tr("Skip Back"), /*timeIsOffset*/true, /*honorCutlist*/true);
4489 }
4490 else if ((tokens[2] == "POSITION" ||
4491 tokens[2] == "POSITIONWITHCUTLIST") &&
4492 (tokens.size() == 4) &&
4493 (tokens[3].contains(kDigitsRE)))
4494 {
4495 DoSeekAbsolute(tokens[3].toInt(), tokens[2] == "POSITIONWITHCUTLIST");
4496 }
4497 }
4498 else if (tokens.size() >= 3 && tokens[1] == "SUBTITLES")
4499 {
4500 bool ok = false;
4501 uint track = tokens[2].toUInt(&ok);
4502
4503 if (!ok)
4504 return;
4505
4506 if (track == 0)
4507 {
4508 emit SetCaptionsEnabled(false, true);
4509 }
4510 else
4511 {
4512 QStringList subs = m_player->GetTracks(kTrackTypeSubtitle);
4513 uint size = static_cast<uint>(subs.size());
4514 uint start = 1;
4515 uint finish = start + size;
4516 if (track >= start && track < finish)
4517 {
4518 emit SetTrack(kTrackTypeSubtitle, track - start);
4520 return;
4521 }
4522
4523 start = finish + 1;
4525 finish = start + size;
4526 if (track >= start && track < finish)
4527 {
4528 emit SetTrack(kTrackTypeCC708, track - start);
4530 return;
4531 }
4532
4533 start = finish + 1;
4535 finish = start + size;
4536 if (track >= start && track < finish)
4537 {
4538 emit SetTrack(kTrackTypeCC608, track - start);
4540 return;
4541 }
4542
4543 start = finish + 1;
4545 finish = start + size;
4546 if (track >= start && track < finish)
4547 {
4550 return;
4551 }
4552
4553 start = finish + 1;
4555 finish = start + size;
4556 if (track >= start && track < finish)
4557 {
4558 emit SetTrack(kTrackTypeTeletextMenu, track - start);
4560 return;
4561 }
4562
4563 start = finish + 1;
4565 finish = start + size;
4566 if (track >= start && track < finish)
4567 {
4568 emit SetTrack(kTrackTypeRawText, track - start);
4570 return;
4571 }
4572 }
4573 }
4574 else if (tokens.size() >= 3 && tokens[1] == "VOLUME")
4575 {
4576 static const QRegularExpression re { "(\\d+)%?" };
4577 auto match = re.match(tokens[2]);
4578 if (match.hasMatch())
4579 {
4580 QStringList matches = match.capturedTexts();
4581
4582 LOG(VB_GENERAL, LOG_INFO, QString("Set Volume to %1%").arg(matches[1]));
4583
4584 bool ok = false;
4585 int vol = matches[1].toInt(&ok);
4586 if (!ok)
4587 return;
4588
4589 if (0 <= vol && vol <= 100)
4590 emit ChangeVolume(true, vol);
4591 }
4592 }
4593 else if (tokens.size() >= 3 && tokens[1] == "QUERY")
4594 {
4595 if (tokens[2] == "POSITION")
4596 {
4597 if (!m_player)
4598 return;
4599 QString speedStr;
4600 if (ContextIsPaused(__FILE__, __LINE__))
4601 {
4602 speedStr = "pause";
4603 }
4605 {
4606 speedStr = QString("%1x").arg(m_playerContext.m_ffRewSpeed);
4607 }
4608 else
4609 {
4610 static const QRegularExpression re { "Play (.*)x" };
4611 auto match = re.match(m_playerContext.GetPlayMessage());
4612 if (match.hasMatch())
4613 {
4614 QStringList matches = match.capturedTexts();
4615 speedStr = QString("%1x").arg(matches[1]);
4616 }
4617 else
4618 {
4619 speedStr = "1x";
4620 }
4621 }
4622
4623 osdInfo info;
4625
4626 QDateTime respDate = MythDate::current(true);
4627 QString infoStr = "";
4628
4629 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4630 uint64_t fplay = 0;
4631 double rate = 30.0;
4632 if (m_player)
4633 {
4634 fplay = m_player->GetFramesPlayed();
4635 rate = static_cast<double>(m_player->GetFrameRate()); // for display only
4636 }
4637 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4638
4639 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
4641 {
4642 infoStr = "LiveTV";
4645 }
4646 else
4647 {
4649 infoStr = "DVD";
4651 infoStr = "Recorded";
4652 else
4653 infoStr = "Video";
4654
4657 }
4658
4659 QString bufferFilename =
4660 m_playerContext.m_buffer ? m_playerContext.m_buffer->GetFilename() : QString("no buffer");
4661 if ((infoStr == "Recorded") || (infoStr == "LiveTV"))
4662 {
4663 infoStr += QString(" %1 %2 %3 %4 %5 %6 %7")
4664 .arg(info.text["description"],
4665 speedStr,
4667 ? QString::number(m_playerContext.m_playingInfo->GetChanID()) : "0",
4668 respDate.toString(Qt::ISODate),
4669 QString::number(fplay),
4670 bufferFilename,
4671 QString::number(rate));
4672 }
4673 else
4674 {
4675 QString position = info.text["description"].section(" ",0,0);
4676 infoStr += QString(" %1 %2 %3 %4 %5")
4677 .arg(position,
4678 speedStr,
4679 bufferFilename,
4680 QString::number(fplay),
4681 QString::number(rate));
4682 }
4683
4684 infoStr += QString(" Subtitles:");
4685
4687
4688 if (subtype == kDisplayNone)
4689 infoStr += QString(" *0:[None]*");
4690 else
4691 infoStr += QString(" 0:[None]");
4692
4693 uint n = 1;
4694
4695 QStringList subs = m_player->GetTracks(kTrackTypeSubtitle);
4696 for (int i = 0; i < subs.size(); i++)
4697 {
4698 if ((subtype & kDisplayAVSubtitle) && (m_player->GetTrack(kTrackTypeSubtitle) == i))
4699 infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
4700 else
4701 infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
4702 n++;
4703 }
4704
4706 for (int i = 0; i < subs.size(); i++)
4707 {
4708 if ((subtype & kDisplayCC708) && (m_player->GetTrack(kTrackTypeCC708) == i))
4709 infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
4710 else
4711 infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
4712 n++;
4713 }
4714
4716 for (int i = 0; i < subs.size(); i++)
4717 {
4718 if ((subtype & kDisplayCC608) && (m_player->GetTrack(kTrackTypeCC608) == i))
4719 infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
4720 else
4721 infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
4722 n++;
4723 }
4724
4726 for (int i = 0; i < subs.size(); i++)
4727 {
4729 infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
4730 else
4731 infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
4732 n++;
4733 }
4734
4736 for (int i = 0; i < subs.size(); i++)
4737 {
4739 infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
4740 else
4741 infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
4742 n++;
4743 }
4744
4746 for (int i = 0; i < subs.size(); i++)
4747 {
4749 infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
4750 else
4751 infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
4752 n++;
4753 }
4754
4755 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
4756
4757 QString message = QString("NETWORK_CONTROL ANSWER %1").arg(infoStr);
4758 MythEvent me(message);
4760 }
4761 else if (tokens[2] == "VOLUME")
4762 {
4763 QString infoStr = QString("%1%").arg(m_audioState.m_volume);
4764 QString message = QString("NETWORK_CONTROL ANSWER %1").arg(infoStr);
4765 MythEvent me(message);
4767 }
4768 }
4769}
4770
4771bool TV::StartPlayer(TVState desiredState)
4772{
4773 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("(%1) -- begin").arg(StateToString(desiredState)));
4774
4775 bool ok = CreatePlayer(desiredState);
4777
4778 if (ok)
4779 {
4780 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Created player."));
4781 SetSpeedChangeTimer(25ms, __LINE__);
4782 }
4783 else
4784 {
4785 LOG(VB_GENERAL, LOG_CRIT, LOC + QString("Failed to create player."));
4786 }
4787
4788 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("(%1) -- end %2")
4789 .arg(StateToString(desiredState), (ok) ? "ok" : "error"));
4790
4791 return ok;
4792}
4793
4795{
4796 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4797 if (!m_player)
4798 {
4799 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4800 return;
4801 }
4802
4803 float time = 0.0;
4804
4806 m_player->IsPaused())
4807 {
4809 time = StopFFRew();
4810 else if (m_player->IsPaused())
4812
4816 }
4817 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4818
4819 DoPlayerSeek(time);
4821
4823
4824 SetSpeedChangeTimer(0ms, __LINE__);
4826}
4827
4829{
4830
4832 return 0.0F;
4833
4835 float time = 0.0F;
4836
4837 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4838 if (!m_player)
4839 {
4840 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4841 return 0.0F;
4842 }
4843 if (m_player->IsPaused())
4844 {
4846 }
4847 else
4848 {
4850 time = StopFFRew();
4851 m_player->Pause();
4852 }
4853 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4854 return time;
4855}
4856
4857void TV::DoTogglePauseFinish(float Time, bool ShowOSD)
4858{
4860 return;
4861
4863 return;
4864
4865 if (ContextIsPaused(__FILE__, __LINE__))
4866 {
4869
4870 DoPlayerSeek(Time);
4871 if (ShowOSD)
4874 }
4875 else
4876 {
4877 DoPlayerSeek(Time);
4878 if (ShowOSD)
4881 }
4882
4883 SetSpeedChangeTimer(0ms, __LINE__);
4884}
4885
4893{
4894 bool paused = false;
4895 int dummy = 0;
4896 TV* tv = AcquireRelease(dummy, true);
4897 if (tv)
4898 {
4899 tv->GetPlayerReadLock();
4900 PlayerContext* context = tv->GetPlayerContext();
4901 if (!context->IsErrored())
4902 {
4903 context->LockDeletePlayer(__FILE__, __LINE__);
4904 if (context->m_player)
4905 paused = context->m_player->IsPaused();
4906 context->UnlockDeletePlayer(__FILE__, __LINE__);
4907 }
4908 tv->ReturnPlayerLock();
4909 AcquireRelease(dummy, false);
4910 }
4911 return paused;
4912}
4913
4914void TV::DoTogglePause(bool ShowOSD)
4915{
4916 bool ignore = false;
4917 bool paused = false;
4918 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4919 if (m_player)
4920 {
4921 ignore = m_player->GetEditMode();
4922 paused = m_player->IsPaused();
4923 }
4924 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4925
4926 if (paused)
4928 else
4930
4931 if (!ignore)
4933 // Emit Pause or Unpaused signal
4935}
4936
4937bool TV::DoPlayerSeek(float Time)
4938{
4940 return false;
4941
4942 if (Time > -0.001F && Time < +0.001F)
4943 return false;
4944
4945 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("%1 seconds").arg(static_cast<double>(Time)));
4946
4947 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4948 if (!m_player)
4949 {
4950 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4951 return false;
4952 }
4953
4955 {
4956 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4957 return false;
4958 }
4959
4960 emit PauseAudioUntilReady();
4961
4962 bool res = false;
4963
4964 if (Time > 0.0F)
4965 res = m_player->FastForward(Time);
4966 else if (Time < 0.0F)
4967 res = m_player->Rewind(-Time);
4968 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4969
4970 return res;
4971}
4972
4973bool TV::DoPlayerSeekToFrame(uint64_t FrameNum)
4974{
4976 return false;
4977
4978 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("%1").arg(FrameNum));
4979
4980 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4981 if (!m_player)
4982 {
4983 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4984 return false;
4985 }
4986
4988 {
4989 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4990 return false;
4991 }
4992
4993 emit PauseAudioUntilReady();
4994
4995 bool res = m_player->JumpToFrame(FrameNum);
4996
4997 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4998
4999 return res;
5000}
5001
5002bool TV::SeekHandleAction(const QStringList& Actions, const bool IsDVD)
5003{
5004 const int kRewind = 4;
5005 const int kForward = 8;
5006 const int kSticky = 16;
5007 const int kSlippery = 32;
5008 const int kRelative = 64;
5009 const int kAbsolute = 128;
5010 const int kIgnoreCutlist = 256;
5011 const int kWhenceMask = 3;
5012 int flags = 0;
5013 if (IsActionable(ACTION_SEEKFFWD, Actions))
5014 flags = ARBSEEK_FORWARD | kForward | kSlippery | kRelative;
5015 else if (IsActionable("FFWDSTICKY", Actions))
5016 flags = ARBSEEK_END | kForward | kSticky | kAbsolute;
5017 else if (IsActionable(ACTION_RIGHT, Actions))
5018 flags = ARBSEEK_FORWARD | kForward | kSticky | kRelative;
5019 else if (IsActionable(ACTION_SEEKRWND, Actions))
5020 flags = ARBSEEK_REWIND | kRewind | kSlippery | kRelative;
5021 else if (IsActionable("RWNDSTICKY", Actions))
5022 flags = ARBSEEK_SET | kRewind | kSticky | kAbsolute;
5023 else if (IsActionable(ACTION_LEFT, Actions))
5024 flags = ARBSEEK_REWIND | kRewind | kSticky | kRelative;
5025 else
5026 return false;
5027
5028 int direction = (flags & kRewind) ? -1 : 1;
5029 if (HasQueuedInput())
5030 {
5031 DoArbSeek(static_cast<ArbSeekWhence>(flags & kWhenceMask), (flags & kIgnoreCutlist) == 0);
5032 }
5033 else if (ContextIsPaused(__FILE__, __LINE__))
5034 {
5035 if (!IsDVD)
5036 {
5037 QString message = (flags & kRewind) ? tr("Rewind") :
5038 tr("Forward");
5039 if (flags & kAbsolute) // FFWDSTICKY/RWNDSTICKY
5040 {
5041 float time = direction;
5042 DoSeek(time, message, /*timeIsOffset*/true, /*honorCutlist*/(flags & kIgnoreCutlist) == 0);
5043 }
5044 else
5045 {
5046 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5047 uint64_t frameAbs = m_player->GetFramesPlayed();
5048 uint64_t frameRel = m_player->TranslatePositionAbsToRel(frameAbs);
5049 uint64_t targetRel = frameRel + static_cast<uint64_t>(direction);
5050 if (frameRel == 0 && direction < 0)
5051 targetRel = 0;
5052 uint64_t maxAbs = m_player->GetCurrentFrameCount();
5053 uint64_t maxRel = m_player->TranslatePositionAbsToRel(maxAbs);
5054 targetRel = std::min(targetRel, maxRel);
5055 uint64_t targetAbs = m_player->TranslatePositionRelToAbs(targetRel);
5056 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5057 DoPlayerSeekToFrame(targetAbs);
5059 }
5060 }
5061 }
5062 else if (flags & kSticky)
5063 {
5064 ChangeFFRew(direction);
5065 }
5066 else if (flags & kRewind)
5067 {
5068 if (m_smartForward)
5069 m_doSmartForward = true;
5070 DoSeek(-m_playerContext.m_rewtime, tr("Skip Back"), /*timeIsOffset*/true, /*honorCutlist*/(flags & kIgnoreCutlist) == 0);
5071 }
5072 else
5073 {
5075 {
5076 DoSeek(m_playerContext.m_rewtime, tr("Skip Ahead"), /*timeIsOffset*/true, /*honorCutlist*/(flags & kIgnoreCutlist) == 0);
5077 }
5078 else
5079 {
5080 DoSeek(m_playerContext.m_fftime, tr("Skip Ahead"), /*timeIsOffset*/true, /*honorCutlist*/(flags & kIgnoreCutlist) == 0);
5081 }
5082 }
5083 return true;
5084}
5085
5086void TV::DoSeek(float Time, const QString &Msg, bool TimeIsOffset, bool HonorCutlist)
5087{
5088 if (!m_player)
5089 return;
5090
5091 bool limitkeys = false;
5092
5093 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5095 limitkeys = true;
5096
5097 if (!limitkeys || (m_keyRepeatTimer.elapsed() > kKeyRepeatTimeout))
5098 {
5100 NormalSpeed();
5101 Time += StopFFRew();
5102 if (TimeIsOffset)
5103 {
5104 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5105 DoPlayerSeek(Time);
5106 }
5107 else
5108 {
5109 auto time = millisecondsFromFloat(Time * 1000);
5110 uint64_t desiredFrameRel = m_player->TranslatePositionMsToFrame(time, HonorCutlist);
5111 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5112 DoPlayerSeekToFrame(desiredFrameRel);
5113 }
5114 bool paused = m_player->IsPaused();
5116 }
5117 else
5118 {
5119 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5120 }
5121}
5122
5123void TV::DoSeekAbsolute(long long Seconds, bool HonorCutlist)
5124{
5125 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5126 if (!m_player)
5127 {
5128 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5130 return;
5131 }
5132 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5133 DoSeek(Seconds, tr("Jump To"), /*timeIsOffset*/false, HonorCutlist);
5135}
5136
5137void TV::DoArbSeek(ArbSeekWhence Whence, bool HonorCutlist)
5138{
5139 bool ok = false;
5140 int seek = GetQueuedInputAsInt(&ok);
5141 ClearInputQueues(true);
5142 if (!ok)
5143 return;
5144
5145 int64_t time = ((seek / 100) * 3600) + ((seek % 100) * 60);
5146
5147 if (Whence == ARBSEEK_FORWARD)
5148 {
5149 DoSeek(time, tr("Jump Ahead"), /*timeIsOffset*/true, HonorCutlist);
5150 }
5151 else if (Whence == ARBSEEK_REWIND)
5152 {
5153 DoSeek(-time, tr("Jump Back"), /*timeIsOffset*/true, HonorCutlist);
5154 }
5155 else if (Whence == ARBSEEK_END)
5156 {
5157 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5158 if (!m_player)
5159 {
5160 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5161 return;
5162 }
5163 uint64_t total_frames = m_player->GetCurrentFrameCount();
5164 float dur = m_player->ComputeSecs(total_frames, HonorCutlist);
5165 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5166 DoSeek(std::max(0.0F, dur - static_cast<float>(time)), tr("Jump To"), /*timeIsOffset*/false, HonorCutlist);
5167 }
5168 else
5169 {
5170 DoSeekAbsolute(time, HonorCutlist);
5171 }
5172}
5173
5175{
5177 return;
5178
5180
5181 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5182 if (m_player)
5184 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5185
5186 SetSpeedChangeTimer(0ms, __LINE__);
5187}
5188
5189void TV::ChangeSpeed(int Direction)
5190{
5191 int old_speed = m_playerContext.m_ffRewSpeed;
5192
5193 if (ContextIsPaused(__FILE__, __LINE__))
5195
5196 m_playerContext.m_ffRewSpeed += Direction;
5197
5198 float time = StopFFRew();
5199 float speed {NAN};
5200
5201 // Make sure these values for m_ffRewSpeed in TV::ChangeSpeed()
5202 // and PlayerContext::GetPlayMessage() stay in sync.
5205 else if (m_playerContext.m_ffRewSpeed == -1)
5206 speed = 1.0F / 3;
5207 else if (m_playerContext.m_ffRewSpeed == -2)
5208 speed = 1.0F / 8;
5209 else if (m_playerContext.m_ffRewSpeed == -3)
5210 speed = 1.0F / 16;
5211 else if (m_playerContext.m_ffRewSpeed == -4)
5212 {
5213 DoTogglePause(true);
5214 return;
5215 }
5216 else
5217 {
5218 m_playerContext.m_ffRewSpeed = old_speed;
5219 return;
5220 }
5221
5222 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5223 if (m_player && !m_player->Play(speed, m_playerContext.m_ffRewSpeed == 0))
5224 {
5225 m_playerContext.m_ffRewSpeed = old_speed;
5226 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5227 return;
5228 }
5229 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5230 DoPlayerSeek(time);
5231 QString mesg = m_playerContext.GetPlayMessage();
5233
5234 SetSpeedChangeTimer(0ms, __LINE__);
5235}
5236
5238{
5239 float time = 0.0;
5240
5242 return time;
5243
5245 time = -m_ffRewSpeeds[static_cast<size_t>(m_playerContext.m_ffRewIndex)] * m_ffRewRepos;
5246 else
5247 time = m_ffRewSpeeds[static_cast<size_t>(m_playerContext.m_ffRewIndex)] * m_ffRewRepos;
5248
5251
5252 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5253 if (m_player)
5255 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5256
5257 SetSpeedChangeTimer(0ms, __LINE__);
5258
5259 return time;
5260}
5261
5262void TV::ChangeFFRew(int Direction)
5263{
5264 if (m_playerContext.m_ffRewState == Direction)
5265 {
5266 while (++m_playerContext.m_ffRewIndex < static_cast<int>(m_ffRewSpeeds.size()))
5267 if (m_ffRewSpeeds[static_cast<size_t>(m_playerContext.m_ffRewIndex)])
5268 break;
5269 if (m_playerContext.m_ffRewIndex >= static_cast<int>(m_ffRewSpeeds.size()))
5272 }
5273 else if (!m_ffRewReverse && m_playerContext.m_ffRewState == -Direction)
5274 {
5276 if (m_ffRewSpeeds[static_cast<size_t>(m_playerContext.m_ffRewIndex)])
5277 break;
5280 else
5281 {
5282 float time = StopFFRew();
5283 DoPlayerSeek(time);
5285 }
5286 }
5287 else
5288 {
5289 NormalSpeed();
5290 m_playerContext.m_ffRewState = Direction;
5292 }
5293}
5294
5295void TV::SetFFRew(int Index)
5296{
5298 return;
5299
5300 auto index = static_cast<size_t>(Index);
5301 if (!m_ffRewSpeeds[index])
5302 return;
5303
5304 auto ffrewindex = static_cast<size_t>(m_playerContext.m_ffRewIndex);
5305 int speed = 0;
5306 QString mesg;
5308 {
5309 speed = m_ffRewSpeeds[index];
5310 // Don't allow ffwd if seeking is needed but not available
5312 return;
5313
5315 mesg = tr("Forward %1X").arg(m_ffRewSpeeds[ffrewindex]);
5317 }
5318 else
5319 {
5320 // Don't rewind if we cannot seek
5322 return;
5323
5325 mesg = tr("Rewind %1X").arg(m_ffRewSpeeds[ffrewindex]);
5326 speed = -m_ffRewSpeeds[ffrewindex];
5328 }
5329
5330 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5331 if (m_player)
5332 m_player->Play(static_cast<float>(speed), (speed == 1) && (m_playerContext.m_ffRewState > 0));
5333 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5334
5336
5337 SetSpeedChangeTimer(0ms, __LINE__);
5338}
5339
5340void TV::DoQueueTranscode(const QString& Profile)
5341{
5342 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
5343
5345 {
5346 bool stop = false;
5347 if (m_queuedTranscode ||
5352 {
5353 stop = true;
5354 }
5355
5356 if (stop)
5357 {
5362 m_queuedTranscode = false;
5363 emit ChangeOSDMessage(tr("Stopping Transcode"));
5364 }
5365 else
5366 {
5368 recinfo.ApplyTranscoderProfileChange(Profile);
5369 QString jobHost = "";
5370
5373
5374 QString msg = tr("Try Again");
5378 jobHost, "", "", JOB_USE_CUTLIST))
5379 {
5380 m_queuedTranscode = true;
5381 msg = tr("Transcoding");
5382 }
5383 emit ChangeOSDMessage(msg);
5384 }
5385 }
5386 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
5387}
5388
5390{
5391 int num_chapters = 0;
5392 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5393 if (m_player)
5394 num_chapters = m_player->GetNumChapters();
5395 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5396 return num_chapters;
5397}
5398
5399void TV::GetChapterTimes(QList<std::chrono::seconds> &Times)
5400{
5401 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5402 if (m_player)
5403 m_player->GetChapterTimes(Times);
5404 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5405}
5406
5408{
5409 int chapter = 0;
5410 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5411 if (m_player)
5412 chapter = m_player->GetCurrentChapter();
5413 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5414 return chapter;
5415}
5416
5417void TV::DoJumpChapter(int Chapter)
5418{
5419 NormalSpeed();
5420 StopFFRew();
5421
5422 emit PauseAudioUntilReady();
5423
5424 UpdateOSDSeekMessage(tr("Jump Chapter"), kOSDTimeout_Med);
5425
5426 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5427 if (m_player)
5428 m_player->JumpChapter(Chapter);
5429 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5430}
5431
5433{
5434 int num_titles = 0;
5435 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5436 if (m_player)
5437 num_titles = m_player->GetNumTitles();
5438 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5439 return num_titles;
5440}
5441
5443{
5444 int currentTitle = 0;
5445 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5446 if (m_player)
5447 currentTitle = m_player->GetCurrentTitle();
5448 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5449 return currentTitle;
5450}
5451
5453{
5454 int num_angles = 0;
5455 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5456 if (m_player)
5457 num_angles = m_player->GetNumAngles();
5458 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5459 return num_angles;
5460}
5461
5463{
5464 int currentAngle = 0;
5465 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5466 if (m_player)
5467 currentAngle = m_player->GetCurrentAngle();
5468 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5469 return currentAngle;
5470}
5471
5472QString TV::GetAngleName(int Angle)
5473{
5474 QString name;
5475 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5476 if (m_player)
5477 name = m_player->GetAngleName(Angle);
5478 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5479 return name;
5480}
5481
5482std::chrono::seconds TV::GetTitleDuration(int Title)
5483{
5484 std::chrono::seconds seconds = 0s;
5485 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5486 if (m_player)
5487 seconds = m_player->GetTitleDuration(Title);
5488 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5489 return seconds;
5490}
5491
5492
5493QString TV::GetTitleName(int Title)
5494{
5495 QString name;
5496 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5497 if (m_player)
5498 name = m_player->GetTitleName(Title);
5499 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5500 return name;
5501}
5502
5503void TV::DoSwitchTitle(int Title)
5504{
5505 NormalSpeed();
5506 StopFFRew();
5507
5508 emit PauseAudioUntilReady();
5509
5510 UpdateOSDSeekMessage(tr("Switch Title"), kOSDTimeout_Med);
5511 emit ChangeOSDPositionUpdates(true);
5512
5513 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5514 if (m_player)
5515 m_player->SwitchTitle(Title);
5516 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5517}
5518
5519void TV::DoSwitchAngle(int Angle)
5520{
5521 NormalSpeed();
5522 StopFFRew();
5523
5524 emit PauseAudioUntilReady();
5525
5526 UpdateOSDSeekMessage(tr("Switch Angle"), kOSDTimeout_Med);
5527 emit ChangeOSDPositionUpdates(true);
5528
5529 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5530 if (m_player)
5531 m_player->SwitchAngle(Angle);
5532 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5533}
5534
5535void TV::DoSkipCommercials(int Direction)
5536{
5537 NormalSpeed();
5538 StopFFRew();
5539
5540 if (StateIsLiveTV(GetState()))
5541 return;
5542
5543 emit PauseAudioUntilReady();
5544
5545 osdInfo info;
5547 info.text["title"] = tr("Skip");
5548 info.text["description"] = tr("Searching");
5550 emit ChangeOSDPositionUpdates(true);
5551
5552 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5553 if (m_player)
5554 m_player->SkipCommercials(Direction);
5555 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5556}
5557
5558void TV::SwitchSource(uint Direction)
5559{
5560 QMap<uint,InputInfo> sources;
5561 uint cardid = m_playerContext.GetCardID();
5562
5563 InfoMap info;
5565 uint sourceid = info["sourceid"].toUInt();
5566
5567 std::vector<InputInfo> inputs = RemoteRequestFreeInputInfo(cardid);
5568 for (auto & input : inputs)
5569 {
5570 // prefer the current card's input in sources list
5571 if ((!sources.contains(input.m_sourceId)) ||
5572 ((cardid == input.m_inputId) && (cardid != sources[input.m_sourceId].m_inputId)))
5573 {
5574 sources[input.m_sourceId] = input;
5575 }
5576 }
5577
5578 // Source switching
5579 QMap<uint,InputInfo>::const_iterator beg = sources.constFind(sourceid);
5580 QMap<uint,InputInfo>::const_iterator sit = beg;
5581
5582 if (sit == sources.constEnd())
5583 return;
5584
5585 if (kNextSource == Direction)
5586 {
5587 ++sit;
5588 if (sit == sources.constEnd())
5589 sit = sources.constBegin();
5590 }
5591
5592 if (kPreviousSource == Direction)
5593 {
5594 if (sit != sources.constBegin())
5595 --sit;
5596 else
5597 {
5598 QMap<uint,InputInfo>::const_iterator tmp = sources.constBegin();
5599 while (tmp != sources.constEnd())
5600 {
5601 sit = tmp;
5602 ++tmp;
5603 }
5604 }
5605 }
5606
5607 if (sit == beg)
5608 return;
5609
5610 m_switchToInputId = (*sit).m_inputId;
5612}
5613
5614void TV::SwitchInputs(uint ChanID, QString ChanNum, uint InputID)
5615{
5617 return;
5618
5619 // this will re-create the player. Ensure any outstanding events are delivered
5620 // and processed before the player is deleted so that we don't confuse the
5621 // state of the new player e.g. when switching inputs from the guide grid,
5622 // "EPG_EXITING" may not be received until after the player is re-created
5623 // and we inadvertantly disable drawing...
5624 // TODO with recent changes, embedding should be ended synchronously and hence
5625 // this extra call should no longer be needed
5626 QCoreApplication::processEvents();
5627
5628 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("(%1,'%2',%3)").arg(ChanID).arg(ChanNum).arg(InputID));
5629
5630 RemoteEncoder *testrec = nullptr;
5631
5632 if (!StateIsLiveTV(GetState()))
5633 return;
5634
5635 QStringList reclist;
5636 if (InputID)
5637 {
5638 reclist.push_back(QString::number(InputID));
5639 }
5640 else if (ChanID || !ChanNum.isEmpty())
5641 {
5642 // If we are switching to a channel not on the current recorder
5643 // we need to find the next free recorder with that channel.
5644 reclist = ChannelUtil::GetValidRecorderList(ChanID, ChanNum);
5645 }
5646
5647 if (!reclist.empty())
5649
5650 if (testrec && testrec->IsValidRecorder())
5651 {
5652 InputID = static_cast<uint>(testrec->GetRecorderNumber());
5653
5654 // We are switching to a specific channel...
5655 if (ChanID && ChanNum.isEmpty())
5656 ChanNum = ChannelUtil::GetChanNum(static_cast<int>(ChanID));
5657
5658 if (!ChanNum.isEmpty())
5659 CardUtil::SetStartChannel(InputID, ChanNum);
5660 }
5661
5662 // If we are just switching recorders find first available recorder.
5663 if (!testrec)
5664 testrec = RemoteRequestNextFreeRecorder(static_cast<int>(m_playerContext.GetCardID()));
5665
5666 if (testrec && testrec->IsValidRecorder())
5667 {
5668 // Switching inputs so clear the pseudoLiveTVState.
5670 bool muted = m_audioState.m_muteState == kMuteAll;
5671
5672 // pause the decoder first, so we're not reading too close to the end.
5674 {
5677 }
5678
5679 if (m_player)
5681
5682 // shutdown stuff
5684 {
5687 }
5688
5691 m_playerContext.SetPlayer(nullptr);
5692 m_player = nullptr;
5693
5694 // now restart stuff
5696 m_lockTimerOn = false;
5697
5700 // We need to set channum for SpawnLiveTV..
5701 if (ChanNum.isEmpty() && ChanID)
5702 ChanNum = ChannelUtil::GetChanNum(static_cast<int>(ChanID));
5703 if (ChanNum.isEmpty() && InputID)
5704 ChanNum = CardUtil::GetStartChannel(InputID);
5706
5708 {
5709 LOG(VB_GENERAL, LOG_ERR, LOC + "LiveTV not successfully restarted");
5712 SetErrored();
5713 SetExitPlayer(true, false);
5714 }
5715 else
5716 {
5717 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
5718 QString playbackURL = m_playerContext.m_playingInfo->GetPlaybackURL(true);
5719 bool opennow = (m_playerContext.m_tvchain->GetInputType(-1) != "DUMMY");
5722 playbackURL, false, true,
5723 opennow ? MythMediaBuffer::kLiveTVOpenTimeout : -1ms));
5724
5728 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
5729 }
5730
5731 bool ok = false;
5733 {
5735 {
5737 ok = true;
5739 SetSpeedChangeTimer(25ms, __LINE__);
5740 }
5741 else
5742 {
5743 StopStuff(true, true, true);
5744 }
5745 }
5746
5747 if (!ok)
5748 {
5749 LOG(VB_GENERAL, LOG_ERR, LOC + "LiveTV not successfully started");
5752 SetErrored();
5753 SetExitPlayer(true, false);
5754 }
5755 else
5756 {
5757 m_lockTimer.start();
5758 m_lockTimerOn = true;
5759 }
5760 }
5761 else
5762 {
5763 LOG(VB_GENERAL, LOG_ERR, LOC + "No recorder to switch to...");
5764 delete testrec;
5765 }
5766
5767 UnpauseLiveTV();
5769
5770 ITVRestart(true);
5771}
5772
5774{
5775 // TOGGLEFAV was broken in [20523], this just prints something
5776 // out so as not to cause further confusion. See #8948.
5777 LOG(VB_GENERAL, LOG_ERR, "TV::ToggleChannelFavorite() -- currently disabled");
5778}
5779
5780void TV::ToggleChannelFavorite(const QString& ChangroupName) const
5781{
5784}
5785
5786QString TV::GetQueuedInput() const
5787{
5788 return m_queuedInput;
5789}
5790
5791int TV::GetQueuedInputAsInt(bool *OK, int Base) const
5792{
5793 return m_queuedInput.toInt(OK, Base);
5794}
5795
5797{
5798 if (m_queuedChanNum.isEmpty())
5799 return "";
5800
5801 // strip initial zeros and other undesirable characters
5802 int i = 0;
5803 for (; i < m_queuedChanNum.length(); i++)
5804 {
5805 if ((m_queuedChanNum[i] > '0') && (m_queuedChanNum[i] <= '9'))
5806 break;
5807 }
5808 m_queuedChanNum = m_queuedChanNum.right(m_queuedChanNum.length() - i);
5809
5810 // strip whitespace at end of string
5811 m_queuedChanNum = m_queuedChanNum.trimmed();
5812
5813 return m_queuedChanNum;
5814}
5815
5820void TV::ClearInputQueues(bool Hideosd)
5821{
5822 if (Hideosd)
5824
5825 m_queuedInput = "";
5826 m_queuedChanNum = "";
5827 m_queuedChanID = 0;
5829 {
5832 }
5833}
5834
5836{
5837 if (Key)
5838 {
5839 m_queuedInput = m_queuedInput.append(Key).right(kInputKeysMax);
5840 m_queuedChanNum = m_queuedChanNum.append(Key).right(kInputKeysMax);
5842 m_queueInputTimerId = StartTimer(10ms, __LINE__);
5843 }
5844
5845 bool commitSmart = false;
5846 QString inputStr = GetQueuedInput();
5847
5848 // Always use immediate channel change when channel numbers are entered
5849 // in browse mode because in browse mode space/enter exit browse
5850 // mode and change to the currently browsed channel.
5852 {
5853 commitSmart = ProcessSmartChannel(inputStr);
5854 }
5855
5856 // Handle OSD...
5857 inputStr = inputStr.isEmpty() ? "?" : inputStr;
5858 if (m_ccInputMode)
5859 {
5860 QString entryStr = (m_vbimode==VBIMode::PAL_TT) ? tr("TXT:") : tr("CC:");
5861 inputStr = entryStr + " " + inputStr;
5862 }
5863 else if (m_asInputMode)
5864 {
5865 inputStr = tr("Seek:", "seek to location") + " " + inputStr;
5866 }
5867 // NOLINTNEXTLINE(readability-misleading-indentation)
5868 emit ChangeOSDText(OSD_WIN_INPUT, {{ "osd_number_entry", inputStr}}, kOSDTimeout_Med);
5869
5870 // Commit the channel if it is complete and smart changing is enabled.
5871 if (commitSmart)
5873}
5874
5875static QString add_spacer(const QString &chan, const QString &spacer)
5876{
5877 if ((chan.length() >= 2) && !spacer.isEmpty())
5878 return chan.left(chan.length()-1) + spacer + chan.right(1);
5879 return chan;
5880}
5881
5882bool TV::ProcessSmartChannel(QString &InputStr)
5883{
5884 QString chan = GetQueuedChanNum();
5885
5886 if (chan.isEmpty())
5887 return false;
5888
5889 // Check for and remove duplicate separator characters
5890#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
5891 int size = chan.size();
5892#else
5893 qsizetype size = chan.size();
5894#endif
5895 if ((size > 2) && (chan.at(size - 1) == chan.at(size - 2)))
5896 {
5897 bool ok = false;
5898 chan.right(1).toUInt(&ok);
5899 if (!ok)
5900 {
5901 chan = chan.left(chan.length()-1);
5902 m_queuedChanNum = chan;
5904 m_queueInputTimerId = StartTimer(10ms, __LINE__);
5905 }
5906 }
5907
5908 // Look for channel in line-up
5909 QString needed_spacer;
5910 uint pref_cardid = 0;
5911 bool is_not_complete = true;
5912
5913 bool valid_prefix = false;
5915 {
5917 chan, pref_cardid, is_not_complete, needed_spacer);
5918 }
5919
5920#if DEBUG_CHANNEL_PREFIX
5921 LOG(VB_GENERAL, LOG_DEBUG, QString("valid_pref(%1) cardid(%2) chan(%3) "
5922 "pref_cardid(%4) complete(%5) sp(%6)")
5923 .arg(valid_prefix).arg(0).arg(chan)
5924 .arg(pref_cardid).arg(is_not_complete).arg(needed_spacer));
5925#endif
5926
5927 if (!valid_prefix)
5928 {
5929 // not a valid prefix.. reset...
5930 m_queuedChanNum = "";
5931 }
5932 else if (!needed_spacer.isEmpty())
5933 {
5934 // need a spacer..
5935 m_queuedChanNum = add_spacer(chan, needed_spacer);
5936 }
5937
5938#if DEBUG_CHANNEL_PREFIX
5939 LOG(VB_GENERAL, LOG_DEBUG, QString(" ValidPref(%1) CardId(%2) Chan(%3) "
5940 " PrefCardId(%4) Complete(%5) Sp(%6)")
5941 .arg(valid_prefix).arg(0).arg(GetQueuedChanNum())
5942 .arg(pref_cardid).arg(is_not_complete).arg(needed_spacer));
5943#endif
5944
5945 InputStr = m_queuedChanNum;
5947 m_queueInputTimerId = StartTimer(10ms, __LINE__);
5948
5949 return !is_not_complete;
5950}
5951
5953{
5954 bool commited = false;
5955
5956 LOG(VB_PLAYBACK, LOG_INFO, LOC +
5957 QString("livetv(%1) qchannum(%2) qchanid(%3)")
5958 .arg(StateIsLiveTV(GetState()))
5959 .arg(GetQueuedChanNum())
5960 .arg(GetQueuedChanID()));
5961
5962 if (m_ccInputMode)
5963 {
5964 commited = true;
5965 if (HasQueuedInput())
5967 }
5968 else if (m_asInputMode)
5969 {
5970 commited = true;
5971 if (HasQueuedInput())
5972 // XXX Should the cutlist be honored?
5973 DoArbSeek(ARBSEEK_FORWARD, /*honorCutlist*/false);
5974 }
5975 else if (StateIsLiveTV(GetState()))
5976 {
5977 QString channum = GetQueuedChanNum();
5979 {
5980 uint sourceid = 0;
5981 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
5984 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
5985
5986 commited = true;
5987 if (channum.isEmpty())
5988 channum = GetBrowsedInfo().m_chanNum;
5989 uint chanid = GetBrowseChanId(channum, m_playerContext.GetCardID(), sourceid);
5990 if (chanid)
5991 BrowseChannel(channum);
5992
5994 }
5995 else if (GetQueuedChanID() || !channum.isEmpty())
5996 {
5997 commited = true;
5998 ChangeChannel(GetQueuedChanID(), channum);
5999 }
6000 }
6001
6002 ClearInputQueues(true);
6003 return commited;
6004}
6005
6007{
6009 {
6010 uint old_chanid = 0;
6011 if (m_channelGroupId > -1)
6012 {
6013 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
6015 {
6016 LOG(VB_GENERAL, LOG_ERR, LOC +
6017 "no active ctx playingInfo.");
6018 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
6020 return;
6021 }
6022 // Collect channel info
6023 old_chanid = m_playerContext.m_playingInfo->GetChanID();
6024 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
6025 }
6026
6027 if (old_chanid)
6028 {
6029 QMutexLocker locker(&m_channelGroupLock);
6030 if (m_channelGroupId > -1)
6031 {
6033 m_channelGroupChannelList, old_chanid, 0, 0, Direction);
6034 if (chanid)
6035 ChangeChannel(chanid, "");
6036 return;
6037 }
6038 }
6039 }
6040
6041 if (Direction == CHANNEL_DIRECTION_FAVORITE)
6042 Direction = CHANNEL_DIRECTION_UP;
6043
6044 QString oldinputname = m_playerContext.m_recorder->GetInput();
6045
6046 if (ContextIsPaused(__FILE__, __LINE__))
6047 {
6050 }
6051
6052 // Save the current channel if this is the first time
6053 if (m_playerContext.m_prevChan.empty())
6055
6056 emit PauseAudioUntilReady();
6057 PauseLiveTV();
6058
6059 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
6060 if (m_player)
6061 {
6062 emit ResetCaptions();
6063 emit ResetTeletext();
6064 }
6065 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
6066
6068 ClearInputQueues(false);
6069
6070 emit ResetAudio();
6071
6072 UnpauseLiveTV();
6073
6074 if (oldinputname != m_playerContext.m_recorder->GetInput())
6076}
6077
6079 uint cardid, const QString &channum)
6080{
6081 uint chanid = 0;
6082 uint cur_sourceid = 0;
6083
6084 // try to find channel on current input
6085 if (ctx && ctx->m_playingInfo && ctx->m_playingInfo->GetSourceID())
6086 {
6087 cur_sourceid = ctx->m_playingInfo->GetSourceID();
6088 chanid = std::max(ChannelUtil::GetChanID(cur_sourceid, channum), 0);
6089 if (chanid)
6090 return chanid;
6091 }
6092
6093 // try to find channel on specified input
6094 uint sourceid = CardUtil::GetSourceID(cardid);
6095 if (cur_sourceid != sourceid && sourceid)
6096 chanid = std::max(ChannelUtil::GetChanID(sourceid, channum), 0);
6097 return chanid;
6098}
6099
6100void TV::ChangeChannel(uint Chanid, const QString &Channum)
6101{
6102 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("(%1, '%2')").arg(Chanid).arg(Channum));
6103
6104 if ((!Chanid && Channum.isEmpty()) || !m_playerContext.m_recorder)
6105 return;
6106
6107 QString channum = Channum;
6108 QStringList reclist;
6109 QVector<uint> tunable_on;
6110
6111 QString oldinputname = m_playerContext.m_recorder->GetInput();
6112
6113 if (channum.isEmpty() && Chanid)
6114 channum = ChannelUtil::GetChanNum(static_cast<int>(Chanid));
6115
6116 bool getit = false;
6118 {
6120 {
6121 getit = false;
6122 }
6124 {
6125 getit = true;
6126 }
6127 else if (Chanid)
6128 {
6129 tunable_on = IsTunableOn(&m_playerContext, Chanid);
6130 getit = !tunable_on.contains(m_playerContext.GetCardID());
6131 }
6132 else
6133 {
6134 QString needed_spacer;
6135 uint pref_cardid = 0;
6136 uint cardid = m_playerContext.GetCardID();
6137 bool dummy = false;
6138
6140 dummy, needed_spacer);
6141
6142 LOG(VB_CHANNEL, LOG_INFO, LOC +
6143 QString("CheckChannelPrefix(%1, pref_cardid %2, %3, '%4') "
6144 "cardid %5")
6145 .arg(Channum).arg(pref_cardid).arg(dummy).arg(needed_spacer)
6146 .arg(cardid));
6147
6148 channum = add_spacer(Channum, needed_spacer);
6149 if (pref_cardid != cardid)
6150 {
6151 getit = true;
6152 }
6153 else
6154 {
6155 if (!Chanid)
6156 Chanid = get_chanid(&m_playerContext, cardid, Channum);
6157 tunable_on = IsTunableOn(&m_playerContext, Chanid);
6158 getit = !tunable_on.contains(cardid);
6159 }
6160 }
6161
6162 if (getit)
6163 {
6164 QStringList tmp =
6165 ChannelUtil::GetValidRecorderList(Chanid, channum);
6166 if (tunable_on.empty())
6167 {
6168 if (!Chanid)
6170 tunable_on = IsTunableOn(&m_playerContext, Chanid);
6171 }
6172 for (const auto& rec : std::as_const(tmp))
6173 {
6174 if ((Chanid == 0U) || tunable_on.contains(rec.toUInt()))
6175 reclist.push_back(rec);
6176 }
6177 }
6178 }
6179
6180 if (!reclist.empty())
6181 {
6183 if (!testrec || !testrec->IsValidRecorder())
6184 {
6185 ClearInputQueues(true);
6187 delete testrec;
6188 return;
6189 }
6190
6191 if (!m_playerContext.m_prevChan.empty() &&
6192 m_playerContext.m_prevChan.back() == channum)
6193 {
6194 // need to remove it if the new channel is the same as the old.
6195 m_playerContext.m_prevChan.pop_back();
6196 }
6197
6198 // found the card on a different recorder.
6199 uint inputid = static_cast<uint>(testrec->GetRecorderNumber());
6200 delete testrec;
6201 // Save the current channel if this is the first time
6202 if (m_playerContext.m_prevChan.empty())
6204 SwitchInputs(Chanid, channum, inputid);
6205 return;
6206 }
6207
6209 return;
6210
6211 if (ContextIsPaused(__FILE__, __LINE__))
6212 {
6215 }
6216
6217 // Save the current channel if this is the first time
6218 if (m_playerContext.m_prevChan.empty())
6220
6221 emit PauseAudioUntilReady();
6222 PauseLiveTV();
6223
6224 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
6225 if (m_player)
6226 {
6227 emit ResetCaptions();
6228 emit ResetTeletext();
6229 }
6230 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
6231
6233
6234 emit ResetAudio();
6235
6236 UnpauseLiveTV((Chanid != 0U) && (GetQueuedChanID() != 0U));
6237
6238 if (oldinputname != m_playerContext.m_recorder->GetInput())
6240}
6241
6243{
6244 for (const auto & option : Options)
6245 {
6246 uint chanid = option.m_chanId;
6247 QString channum = option.m_chanNum;
6248
6249 if (chanid && !channum.isEmpty() && IsTunablePriv(chanid))
6250 {
6251 // hide the channel number, activated by certain signal monitors
6253 m_queuedInput = channum;
6254 m_queuedChanNum = channum;
6255 m_queuedChanID = chanid;
6257 m_queueInputTimerId = StartTimer(10ms, __LINE__);
6258 break;
6259 }
6260 }
6261}
6262
6264{
6265 QString channum = m_playerContext.GetPreviousChannel();
6266 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Previous channel number '%1'").arg(channum));
6267 if (channum.isEmpty())
6268 return;
6269 emit ChangeOSDText(OSD_WIN_INPUT, {{ "osd_number_entry", channum }}, kOSDTimeout_Med);
6270}
6271
6272void TV::PopPreviousChannel(bool ImmediateChange)
6273{
6275 return;
6276
6277 if (!ImmediateChange)
6279
6280 QString prev_channum = m_playerContext.PopPreviousChannel();
6281 QString cur_channum = m_playerContext.m_tvchain->GetChannelName(-1);
6282
6283 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("'%1'->'%2'")
6284 .arg(cur_channum, prev_channum));
6285
6286 // Only change channel if previous channel != current channel
6287 if (cur_channum != prev_channum && !prev_channum.isEmpty())
6288 {
6289 m_queuedInput = prev_channum;
6290 m_queuedChanNum = prev_channum;
6291 m_queuedChanID = 0;
6293 m_queueInputTimerId = StartTimer(10ms, __LINE__);
6294 }
6295
6296 if (ImmediateChange)
6297 {
6298 // Turn off OSD Channel Num so the channel changes right away
6300 }
6301}
6302
6304{
6306 ClearInputQueues(true);
6307
6308 emit DialogQuit();
6309 // pop OSD screen
6310 emit HideAll(true, nullptr, true);
6311
6313 BrowseEnd(false);
6314}
6315
6319void TV::ToggleOSD(bool IncludeStatusOSD)
6320{
6321 OSD *osd = GetOSDL();
6322 if (!osd)
6323 {
6324 ReturnOSDLock();
6325 return;
6326 }
6327
6328 bool hideAll = false;
6329 bool showStatus = false;
6330 bool paused = ContextIsPaused(__FILE__, __LINE__);
6331 bool is_status_disp = osd->IsWindowVisible(OSD_WIN_STATUS);
6332 bool has_prog_info = osd->HasWindow(OSD_WIN_PROGINFO);
6333 bool is_prog_info_disp = osd->IsWindowVisible(OSD_WIN_PROGINFO);
6334
6335 ReturnOSDLock();
6336
6337 if (is_status_disp)
6338 {
6339 if (has_prog_info)
6341 else
6342 hideAll = true;
6343 }
6344 else if (is_prog_info_disp && !paused)
6345 {
6346 hideAll = true;
6347 }
6348 else if (IncludeStatusOSD)
6349 {
6350 showStatus = true;
6351 }
6352 else
6353 {
6354 if (has_prog_info)
6356 }
6357
6358 if (hideAll || showStatus)
6359 emit HideAll();
6360
6361 if (showStatus)
6362 {
6363 osdInfo info;
6365 {
6366 info.text["title"] = (paused ? tr("Paused") : tr("Position"));
6369 emit ChangeOSDPositionUpdates(true);
6370 }
6371 else
6372 {
6373 emit ChangeOSDPositionUpdates(false);
6374 }
6375 }
6376 else
6377 {
6378 emit ChangeOSDPositionUpdates(false);
6379 }
6380}
6381
6385void TV::UpdateOSDProgInfo(const char *WhichInfo)
6386{
6387 InfoMap infoMap;
6389 if (m_player)
6390 m_player->GetCodecDescription(infoMap);
6391
6392 // Clear previous osd and add new info
6393 emit HideAll();
6394 emit ChangeOSDText(WhichInfo, infoMap, kOSDTimeout_Long);
6395}
6396
6397void TV::UpdateOSDStatus(osdInfo &Info, int Type, OSDTimeout Timeout)
6398{
6399 OSD *osd = GetOSDL();
6400 if (osd)
6401 {
6403 osd->SetValues(OSD_WIN_STATUS, Info.values, Timeout);
6404 emit ChangeOSDText(OSD_WIN_STATUS, Info.text, Timeout);
6405 if (Type != kOSDFunctionalType_Default)
6406 osd->SetFunctionalWindow(OSD_WIN_STATUS, static_cast<OSDFunctionalType>(Type));
6407 }
6408 ReturnOSDLock();
6409}
6410
6411void TV::UpdateOSDStatus(const QString& Title, const QString& Desc,
6412 const QString& Value, int Type, const QString& Units,
6413 int Position, OSDTimeout Timeout)
6414{
6415 osdInfo info;
6416 info.values.insert("position", Position);
6417 info.values.insert("relposition", Position);
6418 info.text.insert("title", Title);
6419 info.text.insert("description", Desc);
6420 info.text.insert("value", Value);
6421 info.text.insert("units", Units);
6422 UpdateOSDStatus(info, Type, Timeout);
6423}
6424
6425void TV::UpdateOSDSeekMessage(const QString &Msg, enum OSDTimeout Timeout)
6426{
6427 LOG(VB_PLAYBACK, LOG_INFO, QString("UpdateOSDSeekMessage(%1, %2)").arg(Msg).arg(Timeout));
6428
6429 osdInfo info;
6431 {
6433 info.text["title"] = Msg;
6434 UpdateOSDStatus(info, osdtype, Timeout);
6435 emit ChangeOSDPositionUpdates(true);
6436 }
6437}
6438
6440{
6442 return;
6443 QString displayName = CardUtil::GetDisplayName(m_playerContext.GetCardID());
6444 emit ChangeOSDMessage(displayName);
6445}
6446
6450void TV::UpdateOSDSignal(const QStringList &List)
6451{
6452 OSD *osd = GetOSDL();
6453 if (!osd || m_overlayState.m_browsing || !m_queuedChanNum.isEmpty())
6454 {
6455 if (&m_playerContext.m_lastSignalMsg != &List)
6457 ReturnOSDLock();
6458 m_signalMonitorTimerId = StartTimer(1ms, __LINE__);
6459 return;
6460 }
6461 ReturnOSDLock();
6462
6464
6468 infoMap["callsign"].isEmpty())
6469 {
6472 if (m_player)
6473 m_player->GetCodecDescription(infoMap);
6474
6477 }
6478
6479 int i = 0;
6480 SignalMonitorList::const_iterator it;
6481 for (it = slist.begin(); it != slist.end(); ++it)
6482 if ("error" == it->GetShortName())
6483 infoMap[QString("error%1").arg(i++)] = it->GetName();
6484 i = 0;
6485 for (it = slist.begin(); it != slist.end(); ++it)
6486 if ("message" == it->GetShortName())
6487 infoMap[QString("message%1").arg(i++)] = it->GetName();
6488
6489 int sig = 0;
6490 double snr = 0.0;
6491 uint ber = 0xffffffff;
6492 int pos = -1;
6493 int tuned = -1;
6494 QString pat("");
6495 QString pmt("");
6496 QString mgt("");
6497 QString vct("");
6498 QString nit("");
6499 QString sdt("");
6500 QString crypt("");
6501 QString err;
6502 QString msg;
6503 for (it = slist.begin(); it != slist.end(); ++it)
6504 {
6505 if ("error" == it->GetShortName())
6506 {
6507 err = it->GetName();
6508 continue;
6509 }
6510
6511 if ("message" == it->GetShortName())
6512 {
6513 msg = it->GetName();
6514 LOG(VB_GENERAL, LOG_INFO, "msg: " + msg);
6515 continue;
6516 }
6517
6518 infoMap[it->GetShortName()] = QString::number(it->GetValue());
6519 if ("signal" == it->GetShortName())
6520 sig = it->GetNormalizedValue(0, 100);
6521 else if ("snr" == it->GetShortName())
6522 snr = it->GetValue();
6523 else if ("ber" == it->GetShortName())
6524 ber = static_cast<uint>(it->GetValue());
6525 else if ("pos" == it->GetShortName())
6526 pos = it->GetValue();
6527 else if ("script" == it->GetShortName())
6528 tuned = it->GetValue();
6529 else if ("seen_pat" == it->GetShortName())
6530 pat = it->IsGood() ? "a" : "_";
6531 else if ("matching_pat" == it->GetShortName())
6532 pat = it->IsGood() ? "A" : pat;
6533 else if ("seen_pmt" == it->GetShortName())
6534 pmt = it->IsGood() ? "m" : "_";
6535 else if ("matching_pmt" == it->GetShortName())
6536 pmt = it->IsGood() ? "M" : pmt;
6537 else if ("seen_mgt" == it->GetShortName())
6538 mgt = it->IsGood() ? "g" : "_";
6539 else if ("matching_mgt" == it->GetShortName())
6540 mgt = it->IsGood() ? "G" : mgt;
6541 else if ("seen_vct" == it->GetShortName())
6542 vct = it->IsGood() ? "v" : "_";
6543 else if ("matching_vct" == it->GetShortName())
6544 vct = it->IsGood() ? "V" : vct;
6545 else if ("seen_nit" == it->GetShortName())
6546 nit = it->IsGood() ? "n" : "_";
6547 else if ("matching_nit" == it->GetShortName())
6548 nit = it->IsGood() ? "N" : nit;
6549 else if ("seen_sdt" == it->GetShortName())
6550 sdt = it->IsGood() ? "s" : "_";
6551 else if ("matching_sdt" == it->GetShortName())
6552 sdt = it->IsGood() ? "S" : sdt;
6553 else if ("seen_crypt" == it->GetShortName())
6554 crypt = it->IsGood() ? "c" : "_";
6555 else if ("matching_crypt" == it->GetShortName())
6556 crypt = it->IsGood() ? "C" : crypt;
6557 }
6558 if (sig)
6559 infoMap["signal"] = QString::number(sig); // use normalized value
6560
6561 bool allGood = SignalMonitorValue::AllGood(slist);
6562 QString tuneCode;
6563 QString slock = ("1" == infoMap["slock"]) ? "L" : "l";
6564 QString lockMsg = (slock=="L") ? tr("Partial Lock") : tr("No Lock");
6565 QString sigMsg = allGood ? tr("Lock") : lockMsg;
6566
6567 QString sigDesc = tr("Signal %1%").arg(sig,2);
6568 if (snr > 0.0)
6569 sigDesc += " | " + tr("S/N %1dB").arg(log10(snr), 3, 'f', 1);
6570 if (ber != 0xffffffff)
6571 sigDesc += " | " + tr("BE %1", "Bit Errors").arg(ber, 2);
6572 if ((pos >= 0) && (pos < 100))
6573 sigDesc += " | " + tr("Rotor %1%").arg(pos,2);
6574
6575 if (tuned == 1)
6576 tuneCode = "t";
6577 else if (tuned == 2)
6578 tuneCode = "F";
6579 else if (tuned == 3)
6580 tuneCode = "T";
6581 else
6582 tuneCode = "_";
6583
6584 sigDesc = sigDesc + QString(" | (%1%2%3%4%5%6%7%8%9) %10")
6585 .arg(tuneCode, slock, pat, pmt, mgt, vct,
6586 nit, sdt, crypt)
6587 .arg(sigMsg);
6588
6589 if (!err.isEmpty())
6590 sigDesc = err;
6591 else if (!msg.isEmpty())
6592 sigDesc = msg;
6593
6594 infoMap["description"] = sigDesc;
6596
6599
6600 // Turn off lock timer if we have an "All Good" or good PMT
6601 if (allGood || (pmt == "M"))
6602 {
6603 m_lockTimerOn = false;
6605 }
6606}
6607
6609{
6610 bool timed_out = false;
6611
6613 {
6614 QString input = m_playerContext.m_recorder->GetInput();
6616 timed_out = m_lockTimerOn && m_lockTimer.hasExpired(timeout);
6617 }
6618
6619 OSD *osd = GetOSDL();
6620
6621 if (!osd)
6622 {
6623 if (timed_out)
6624 {
6625 LOG(VB_GENERAL, LOG_ERR, LOC +
6626 "You have no OSD, but tuning has already taken too long.");
6627 }
6628 ReturnOSDLock();
6629 return;
6630 }
6631
6632 bool showing = osd->DialogVisible(OSD_DLG_INFO);
6633 if (!timed_out)
6634 {
6635 if (showing)
6636 emit DialogQuit();
6637 ReturnOSDLock();
6638 return;
6639 }
6640
6641 if (showing)
6642 {
6643 ReturnOSDLock();
6644 return;
6645 }
6646
6647 ReturnOSDLock();
6648
6649 // create dialog...
6650 static QString s_chanUp = GET_KEY("TV Playback", ACTION_CHANNELUP);
6651 static QString s_chanDown = GET_KEY("TV Playback", ACTION_CHANNELDOWN);
6652 static QString s_nextSrc = GET_KEY("TV Playback", "NEXTSOURCE");
6653 static QString s_togCards = GET_KEY("TV Playback", "NEXTINPUT");
6654
6655 QString message = tr(
6656 "You should have received a channel lock by now. "
6657 "You can continue to wait for a signal, or you "
6658 "can change the channel with %1 or %2, change "
6659 "video source (%3), inputs (%4), etc.")
6660 .arg(s_chanUp, s_chanDown, s_nextSrc, s_togCards);
6661
6662 emit ChangeOSDDialog({ OSD_DLG_INFO, message, 0ms,
6663 { {tr("OK"), "DIALOG_INFO_CHANNELLOCK_0" } },
6664 { "", "DIALOG_INFO_CHANNELLOCK_0", true } });
6665}
6666
6667bool TV::CalcPlayerSliderPosition(osdInfo &info, bool paddedFields) const
6668{
6669 bool result = false;
6671 if (m_player)
6672 {
6673 m_player->UpdateSliderInfo(info, paddedFields);
6674 result = true;
6675 }
6677 return result;
6678}
6679
6680void TV::HideOSDWindow(const char *window)
6681{
6682 OSD *osd = GetOSDL();
6683 if (osd)
6684 osd->HideWindow(window);
6685 ReturnOSDLock();
6686}
6687
6689{
6690 // Make sure the LCD information gets updated shortly
6691 if (m_lcdTimerId)
6693 m_lcdTimerId = StartTimer(1ms, __LINE__);
6694}
6695
6697{
6698 LCD *lcd = LCD::Get();
6699 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
6700 if (!lcd || !m_playerContext.m_playingInfo)
6701 {
6702 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
6703 return;
6704 }
6705
6706 QString title = m_playerContext.m_playingInfo->GetTitle();
6707 QString subtitle = m_playerContext.m_playingInfo->GetSubtitle();
6709
6710 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
6711
6712 if ((callsign != m_lcdCallsign) || (title != m_lcdTitle) ||
6713 (subtitle != m_lcdSubtitle))
6714 {
6715 lcd->switchToChannel(callsign, title, subtitle);
6716 m_lcdCallsign = callsign;
6717 m_lcdTitle = title;
6718 m_lcdSubtitle = subtitle;
6719 }
6720}
6721
6723{
6724 LCD *lcd = LCD::Get();
6726 return;
6727
6729 QString dvdName;
6730 QString dvdSerial;
6731 QString mainStatus;
6732 QString subStatus;
6733
6734 if (!dvd->GetNameAndSerialNum(dvdName, dvdSerial))
6735 dvdName = tr("DVD");
6736
6737 if (dvd->IsInMenu())
6738 {
6739 mainStatus = tr("Menu");
6740 }
6741 else if (dvd->IsInStillFrame())
6742 {
6743 mainStatus = tr("Still Frame");
6744 }
6745 else
6746 {
6747 int playingTitle = 0;
6748 int playingPart = 0;
6749
6750 dvd->GetPartAndTitle(playingPart, playingTitle);
6751 int totalParts = dvd->NumPartsInTitle();
6752
6753 mainStatus = tr("Title: %1 (%2)").arg(playingTitle)
6754 .arg(MythDate::formatTime(dvd->GetTotalTimeOfTitle(), "HH:mm"));
6755 subStatus = tr("Chapter: %1/%2").arg(playingPart).arg(totalParts);
6756 }
6757 if ((dvdName != m_lcdCallsign) || (mainStatus != m_lcdTitle) || (subStatus != m_lcdSubtitle))
6758 {
6759 lcd->switchToChannel(dvdName, mainStatus, subStatus);
6760 m_lcdCallsign = dvdName;
6761 m_lcdTitle = mainStatus;
6762 m_lcdSubtitle = subStatus;
6763 }
6764}
6765
6767{
6768 int dummy = 0;
6769 TV* tv = AcquireRelease(dummy, true);
6770 if (tv)
6771 {
6772 tv->GetPlayerReadLock();
6773 bool result = !TV::IsTunableOn(tv->GetPlayerContext(), ChanId).empty();
6774 tv->ReturnPlayerLock();
6775 AcquireRelease(dummy, false);
6776 return result;
6777 }
6778
6779 return !TV::IsTunableOn(nullptr, ChanId).empty();
6780}
6781
6783{
6784 return !IsTunableOn(&m_playerContext, ChanId).empty();
6785}
6786
6787static QString toCommaList(const QVector<uint> &list)
6788{
6789 QString ret = "";
6790 for (uint i : std::as_const(list))
6791 ret += QString("%1,").arg(i);
6792
6793 if (!ret.isEmpty())
6794 return ret.left(ret.length()-1);
6795
6796 return "";
6797}
6798
6799QVector<uint> TV::IsTunableOn(PlayerContext* Context, uint ChanId)
6800{
6801 QVector<uint> tunable_cards;
6802
6803 if (!ChanId)
6804 {
6805 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("ChanId (%1) - no").arg(ChanId));
6806 return tunable_cards;
6807 }
6808
6809 uint mplexid = ChannelUtil::GetMplexID(ChanId);
6810 mplexid = (32767 == mplexid) ? 0 : mplexid;
6811
6812 uint excluded_input = 0;
6813 if (Context && Context->m_recorder && Context->m_pseudoLiveTVState == kPseudoNormalLiveTV)
6814 excluded_input = Context->GetCardID();
6815
6816 uint sourceid = ChannelUtil::GetSourceIDForChannel(ChanId);
6817
6818 std::vector<InputInfo> inputs = RemoteRequestFreeInputInfo(excluded_input);
6819
6820 for (auto & input : inputs)
6821 {
6822 if (input.m_sourceId != sourceid)
6823 continue;
6824
6825 if (input.m_mplexId &&
6826 input.m_mplexId != mplexid)
6827 continue;
6828
6829 if (!input.m_mplexId && input.m_chanId &&
6830 input.m_chanId != ChanId)
6831 continue;
6832
6833 tunable_cards.push_back(input.m_inputId);
6834 }
6835
6836 if (tunable_cards.empty())
6837 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("ChanId (%1) - no").arg(ChanId));
6838 else
6839 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("ChanId (%1) yes { %2 }").arg(ChanId).arg(toCommaList(tunable_cards)));
6840 return tunable_cards;
6841}
6842
6843void TV::Embed(bool Embed, QRect Rect, const QStringList& Data)
6844{
6845 emit EmbedPlayback(Embed, Rect);
6846 if (Embed)
6847 return;
6848
6849 emit ResizeScreenForVideo();
6850
6851 // m_playerBounds is not applicable when switching modes so
6852 // skip this logic in that case.
6853 if (!m_dbUseVideoModes)
6855
6856 // Restore pause
6858
6859 if (!m_weDisabledGUI)
6860 {
6861 m_weDisabledGUI = true;
6863 }
6864
6865 m_ignoreKeyPresses = false;
6866
6867 // additional data returned by PlaybackBox
6868 if (!Data.isEmpty())
6869 {
6870 ProgramInfo pginfo(Data);
6871 if (pginfo.HasPathname() || pginfo.GetChanID())
6873 }
6874}
6875
6876bool TV::DoSetPauseState(bool Pause)
6877{
6878 bool waspaused = ContextIsPaused(__FILE__, __LINE__);
6879 float time = 0.0F;
6880 if (Pause ^ waspaused)
6881 time = DoTogglePauseStart();
6882 if (Pause ^ waspaused)
6883 DoTogglePauseFinish(time, false);
6884 return waspaused;
6885}
6886
6887void TV::DoEditSchedule(int EditType, const QString & EditArg)
6888{
6889 // Prevent nesting of the pop-up UI
6891 return;
6892
6893 if ((EditType == kScheduleProgramGuide && !RunProgramGuidePtr) ||
6894 (EditType == kScheduleProgramFinder && !RunProgramFinderPtr) ||
6895 (EditType == kScheduledRecording && !RunScheduleEditorPtr) ||
6896 (EditType == kViewSchedule && !RunViewScheduledPtr) ||
6897 (EditType == kPlaybackBox && !RunPlaybackBoxPtr))
6898 {
6899 return;
6900 }
6901
6903
6904 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
6906 {
6907 LOG(VB_GENERAL, LOG_ERR, LOC + "no active ctx playingInfo.");
6908 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
6910 return;
6911 }
6912
6913 // Collect channel info
6915 uint chanid = pginfo.GetChanID();
6916 QString channum = pginfo.GetChanNum();
6917 QDateTime starttime = MythDate::current();
6918 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
6919
6920 ClearOSD();
6921
6922 // Pause playback as needed...
6923 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
6924 bool pause = !m_player || (!StateIsLiveTV(GetState()) && !m_dbContinueEmbedded);
6925 if (m_player)
6926 {
6927 pause |= !m_player->GetVideoOutput();
6928 pause |= m_player->IsPaused();
6929 if (!pause)
6930 pause |= (!StateIsLiveTV(GetState()) && m_player->IsNearEnd());
6931 }
6932 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
6933
6934 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Pausing player: %1").arg(pause));
6936
6937 // Resize window to the MythTV GUI size
6938 MythDisplay* display = m_mainWindow->GetDisplay();
6939 if (display->UsingVideoModes())
6940 {
6941 bool hide = display->NextModeIsLarger(display->GetGUIResolution());
6942 if (hide)
6943 m_mainWindow->hide();
6944 display->SwitchToGUI(true);
6945 if (hide)
6946 m_mainWindow->Show();
6947 }
6948
6951#ifdef Q_OS_ANDROID
6952 m_mainWindow->Show();
6953#else
6954 m_mainWindow->show();
6955#endif
6957
6958
6959 // Actually show the pop-up UI
6960 switch (EditType)
6961 {
6963 {
6964 RunProgramGuidePtr(chanid, channum, starttime, this,
6965 !pause, true, m_channelGroupId);
6966 m_ignoreKeyPresses = true;
6967 break;
6968 }
6970 {
6971 RunProgramFinderPtr(this, !pause, true);
6972 m_ignoreKeyPresses = true;
6973 break;
6974 }
6976 {
6977 /*
6978 4 = plPeopleSearch in mythfrontend/proglist.h
6979 This could be expanded to view other program lists...
6980 */
6981 RunProgramListPtr(this, 4, EditArg);
6982 m_ignoreKeyPresses = true;
6983 break;
6984 }
6986 {
6987 RunScheduleEditorPtr(&pginfo, reinterpret_cast<void*>(this));
6988 m_ignoreKeyPresses = true;
6989 break;
6990 }
6991 case kViewSchedule:
6992 {
6993 RunViewScheduledPtr(reinterpret_cast<void*>(this), !pause);
6994 m_ignoreKeyPresses = true;
6995 break;
6996 }
6997 case kPlaybackBox:
6998 {
6999 RunPlaybackBoxPtr(reinterpret_cast<void*>(this), !pause);
7000 m_ignoreKeyPresses = true;
7001 break;
7002 }
7003 }
7004
7005 // We are embedding in a mythui window so assuming no one
7006 // else has disabled painting show the MythUI window again.
7007 if (m_weDisabledGUI)
7008 {
7010 m_weDisabledGUI = false;
7011 }
7012}
7013
7014void TV::EditSchedule(int EditType, const QString& arg)
7015{
7016 // post the request so the guide will be created in the UI thread
7017 QString message = QString("START_EPG %1 %2").arg(EditType).arg(arg);
7018 auto* me = new MythEvent(message);
7019 QCoreApplication::postEvent(this, me);
7020}
7021
7022void TV::VolumeChange(bool Up, int NewVolume)
7023{
7025 return;
7026
7027 if ((m_audioState.m_muteState == kMuteAll) && (Up || NewVolume >= 0))
7028 emit ChangeMuteState();
7029
7030 emit ChangeVolume(Up, NewVolume);
7031
7033 {
7034 if (LCD *lcd = LCD::Get())
7035 {
7036 QString appName = tr("Video");
7037
7038 if (StateIsLiveTV(GetState()))
7039 appName = tr("TV");
7040
7042 appName = tr("DVD");
7043
7044 lcd->switchToVolume(appName);
7045 lcd->setVolumeLevel(static_cast<float>(m_audioState.m_volume) / 100);
7046
7049 m_lcdVolumeTimerId = StartTimer(2s, __LINE__);
7050 }
7051 }
7052}
7053
7055{
7056 if (m_playerContext.m_tsNormal == 1.0F)
7057 {
7059 }
7060 else
7061 {
7064 }
7065 ChangeTimeStretch(0, false);
7066}
7067
7068void TV::ChangeTimeStretch(int Dir, bool AllowEdit)
7069{
7070 const float kTimeStretchMin = 0.125;
7071 const float kTimeStretchMax = 2.0;
7072 const float kTimeStretchStep = 0.05F;
7073 float new_ts_normal = m_playerContext.m_tsNormal + (kTimeStretchStep * Dir);
7074 m_stretchAdjustment = AllowEdit;
7075
7076 if (new_ts_normal > kTimeStretchMax &&
7077 m_playerContext.m_tsNormal < kTimeStretchMax)
7078 {
7079 new_ts_normal = kTimeStretchMax;
7080 }
7081 else if (new_ts_normal < kTimeStretchMin &&
7082 m_playerContext.m_tsNormal > kTimeStretchMin)
7083 {
7084 new_ts_normal = kTimeStretchMin;
7085 }
7086
7087 if (new_ts_normal > kTimeStretchMax ||
7088 new_ts_normal < kTimeStretchMin)
7089 {
7090 return;
7091 }
7092
7093 m_playerContext.m_tsNormal = kTimeStretchStep * lroundf(new_ts_normal / kTimeStretchStep);
7094
7095 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
7096 if (m_player && !m_player->IsPaused())
7098 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
7099
7101 {
7102 if (!AllowEdit)
7103 {
7105 }
7106 else
7107 {
7108 UpdateOSDStatus(tr("Adjust Time Stretch"), tr("Time Stretch"),
7109 QString::number(static_cast<double>(m_playerContext.m_tsNormal), 'f', 2),
7111 static_cast<int>(m_playerContext.m_tsNormal * (1000 / kTimeStretchMax)),
7113 emit ChangeOSDPositionUpdates(false);
7114 }
7115 }
7116
7117 SetSpeedChangeTimer(0ms, __LINE__);
7118}
7119
7121{
7122 QString text;
7123
7124 // increment sleep index, cycle through
7125 if (++m_sleepIndex == s_sleepTimes.size())
7126 m_sleepIndex = 0;
7127
7128 // set sleep timer to next sleep_index timeout
7129 if (m_sleepTimerId)
7130 {
7132 m_sleepTimerId = 0;
7133 m_sleepTimerTimeout = 0ms;
7134 }
7135
7136 if (s_sleepTimes[m_sleepIndex].milliseconds != 0ms)
7137 {
7140 }
7141
7142 text = tr("Sleep ") + " " + s_sleepTimes[m_sleepIndex].dispString;
7143 emit ChangeOSDMessage(text);
7144}
7145
7147{
7149 m_sleepTimerId = 0;
7150
7151 QString message = tr("MythTV was set to sleep after %1 minutes and will exit in %d seconds.\n"
7152 "Do you wish to continue watching?")
7153 .arg(duration_cast<std::chrono::minutes>(m_sleepTimerTimeout).count());
7154
7156 { { tr("Yes"), "DIALOG_SLEEP_YES_0" },
7157 { tr("No"), "DIALOG_SLEEP_NO_0" } }});
7158
7160}
7161
7162void TV::HandleOSDSleep(const QString& Action)
7163{
7165 return;
7166
7167 if (Action == "YES")
7168 {
7170 {
7173 }
7175 }
7176 else
7177 {
7178 LOG(VB_GENERAL, LOG_INFO, LOC + "No longer watching TV, exiting");
7179 SetExitPlayer(true, true);
7180 }
7181}
7182
7184{
7187
7188 LOG(VB_GENERAL, LOG_INFO, LOC + "Sleep timeout reached, exiting player.");
7189
7190 SetExitPlayer(true, true);
7191}
7192
7202{
7204 m_idleTimerId = 0;
7205
7206 QString message = tr("MythTV has been idle for %1 minutes and "
7207 "will exit in %d seconds. Are you still watching?")
7208 .arg(duration_cast<std::chrono::minutes>(m_dbIdleTimeout).count());
7209
7211 { { tr("Yes"), "DIALOG_IDLE_YES_0" },
7212 { tr("No"), "DIALOG_IDLE_NO_0" }}});
7213
7215}
7216
7217void TV::HandleOSDIdle(const QString& Action)
7218{
7220 return;
7221
7222 if (Action == "YES")
7223 {
7225 {
7228 }
7229 if (m_idleTimerId)
7232 }
7233 else
7234 {
7235 LOG(VB_GENERAL, LOG_INFO, LOC + "No longer watching LiveTV, exiting");
7236 SetExitPlayer(true, true);
7237 }
7238}
7239
7241{
7244
7247 {
7248 LOG(VB_GENERAL, LOG_INFO, LOC + "Idle timeout reached, leaving LiveTV");
7249 SetExitPlayer(true, true);
7250 }
7252}
7253
7254// Retrieve the proper MythTVMenu object from The TV object, given its
7255// id number. This is used to find the original menu again, instead of
7256// serializing/deserializing the entire MythTVMenu object to/from a
7257// QVariant.
7259{
7260 switch (id) {
7261 case kMenuIdPlayback:
7262 return m_playbackMenu;
7264 return m_playbackCompactMenu;
7265 case kMenuIdCutlist:
7266 return m_cutlistMenu;
7268 return m_cutlistCompactMenu;
7269 default:
7270 return dummy_menubase;
7271 }
7272}
7273
7276{
7278 {
7280 return;
7281 }
7282
7283 if (Event->type() == MythEvent::kMythUserMessage)
7284 {
7285 auto *me = dynamic_cast<MythEvent*>(Event);
7286 if (me == nullptr)
7287 return;
7288 QString message = me->Message();
7289
7290 if (message.isEmpty())
7291 return;
7292
7293 std::chrono::milliseconds timeout = 0ms;
7294 if (me->ExtraDataCount() == 1)
7295 {
7296 auto t = std::chrono::seconds(me->ExtraData(0).toInt());
7297 if (t > 0s && t < 1000s)
7298 timeout = t;
7299 }
7300
7301 if (timeout > 0ms)
7302 message += " (%d)";
7303
7304 emit ChangeOSDDialog( { OSD_DLG_CONFIRM, message, timeout });
7305 return;
7306 }
7307
7309 {
7310 auto *b = reinterpret_cast<UpdateBrowseInfoEvent*>(Event);
7312 return;
7313 }
7314
7316 {
7317 auto *dce = reinterpret_cast<DialogCompletionEvent*>(Event);
7318 if (dce->GetData().userType() == qMetaTypeId<MythTVMenuNodeTuple>())
7319 {
7320 auto data = dce->GetData().value<MythTVMenuNodeTuple>();
7321 const MythTVMenu& Menu = getMenuFromId(data.m_id);
7322 QDomNode Node = Menu.GetNodeFromPath(data.m_path);
7323 if (dce->GetResult() == -1) // menu exit/back
7324 PlaybackMenuShow(Menu, Node.parentNode(), Node);
7325 else
7326 PlaybackMenuShow(Menu, Node, QDomNode());
7327 }
7328 else
7329 {
7330 OSDDialogEvent(dce->GetResult(), dce->GetResultText(), dce->GetData().toString());
7331 }
7332 return;
7333 }
7334
7335 // Stop DVD playback cleanly when the DVD is ejected
7336 if (Event->type() == MythMediaEvent::kEventType)
7337 {
7340 if (state != kState_WatchingDVD)
7341 {
7343 return;
7344 }
7345
7346 auto *me = dynamic_cast<MythMediaEvent*>(Event);
7347 if (me == nullptr)
7348 return;
7349 MythMediaDevice *device = me->getDevice();
7350
7352
7353 if (device && filename.endsWith(device->getDevicePath()) && (device->getStatus() == MEDIASTAT_OPEN))
7354 {
7355 LOG(VB_GENERAL, LOG_NOTICE, "DVD has been ejected, exiting playback");
7356 PrepareToExitPlayer(__LINE__);
7357 SetExitPlayer(true, true);
7358 }
7360 return;
7361 }
7362
7363 if (Event->type() != MythEvent::kMythEventMessage)
7364 return;
7365
7366 uint cardnum = 0;
7367 auto *me = dynamic_cast<MythEvent*>(Event);
7368 if (me == nullptr)
7369 return;
7370 QString message = me->Message();
7371
7372 // TODO Go through these and make sure they make sense...
7373 QStringList tokens = message.split(" ", Qt::SkipEmptyParts);
7374
7375 if (me->ExtraDataCount() == 1)
7376 {
7378 int value = me->ExtraData(0).toInt();
7379 if (message == ACTION_SETVOLUME)
7380 VolumeChange(false, value);
7381 else if (message == ACTION_SETAUDIOSYNC)
7382 emit ChangeAudioOffset(0ms, std::chrono::milliseconds(value));
7383 else if (message == ACTION_SETBRIGHTNESS)
7385 else if (message == ACTION_SETCONTRAST)
7387 else if (message == ACTION_SETCOLOUR)
7389 else if (message == ACTION_SETHUE)
7391 else if (message == ACTION_JUMPCHAPTER)
7392 DoJumpChapter(value);
7393 else if (message == ACTION_SWITCHTITLE)
7394 DoSwitchTitle(value - 1);
7395 else if (message == ACTION_SWITCHANGLE)
7396 DoSwitchAngle(value);
7397 else if (message == ACTION_SEEKABSOLUTE)
7398 DoSeekAbsolute(value, /*honorCutlist*/true);
7400 }
7401
7402 if (message == ACTION_SCREENSHOT)
7403 {
7404 int width = 0;
7405 int height = 0;
7406 QString filename;
7407
7408 if (me->ExtraDataCount() >= 2)
7409 {
7410 width = me->ExtraData(0).toInt();
7411 height = me->ExtraData(1).toInt();
7412
7413 if (me->ExtraDataCount() == 3)
7414 filename = me->ExtraData(2);
7415 }
7416 MythMainWindow::ScreenShot(width, height, filename);
7417 }
7418 else if (message == ACTION_GETSTATUS)
7419 {
7420 GetStatus();
7421 }
7422 else if (message.startsWith("DONE_RECORDING"))
7423 {
7424 std::chrono::seconds seconds = 0s;
7425 //long long frames = 0;
7426 int NUMTOKENS = 4; // Number of tokens expected
7427 if (tokens.size() == NUMTOKENS)
7428 {
7429 cardnum = tokens[1].toUInt();
7430 seconds = std::chrono::seconds(tokens[2].toInt());
7431 //frames = tokens[3].toLongLong();
7432 }
7433 else
7434 {
7435 LOG(VB_GENERAL, LOG_ERR, QString("DONE_RECORDING event received "
7436 "with invalid number of arguments, "
7437 "%1 expected, %2 actual")
7438 .arg(NUMTOKENS-1)
7439 .arg(tokens.size()-1));
7440 return;
7441 }
7442
7445 {
7447 {
7448 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
7449 if (m_player)
7450 {
7452 if (seconds > 0s)
7453 m_player->SetLength(seconds);
7454 }
7455 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
7456
7459 }
7460 }
7462 {
7465 {
7466 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
7467 if (m_player)
7468 {
7470 if (seconds > 0s)
7471 m_player->SetLength(seconds);
7472 }
7473 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
7474 }
7475 }
7477 }
7478
7479 if (message.startsWith("ASK_RECORDING "))
7480 {
7481 int timeuntil = 0;
7482 bool hasrec = false;
7483 bool haslater = false;
7484 if (tokens.size() >= 5)
7485 {
7486 cardnum = tokens[1].toUInt();
7487 timeuntil = tokens[2].toInt();
7488 hasrec = (tokens[3].toInt() != 0);
7489 haslater = (tokens[4].toInt() != 0);
7490 }
7491 LOG(VB_GENERAL, LOG_DEBUG,
7492 LOC + message + QString(" hasrec: %1 haslater: %2")
7493 .arg(hasrec).arg(haslater));
7494
7497 AskAllowRecording(me->ExtraDataList(), timeuntil, hasrec, haslater);
7498
7500 }
7501
7502 if (message.startsWith("QUIT_LIVETV"))
7503 {
7504 cardnum = (tokens.size() >= 2) ? tokens[1].toUInt() : 0;
7505
7507 bool match = m_playerContext.GetCardID() == cardnum;
7508 if (match && m_playerContext.m_recorder)
7509 {
7510 SetLastProgram(nullptr);
7511 m_jumpToProgram = true;
7512 SetExitPlayer(true, false);
7513 }
7515 }
7516
7517 if (message.startsWith("LIVETV_WATCH"))
7518 {
7519 int watch = 0;
7520 if (tokens.size() >= 3)
7521 {
7522 cardnum = tokens[1].toUInt();
7523 watch = tokens[2].toInt();
7524 }
7525
7527 if (m_playerContext.GetCardID() == cardnum)
7528 {
7529 if (watch)
7530 {
7531 ProgramInfo pi(me->ExtraDataList());
7532 if (pi.HasPathname() || pi.GetChanID())
7533 {
7536 m_pseudoChangeChanTimerId = StartTimer(0ms, __LINE__);
7537 }
7538 }
7539 else
7540 {
7542 }
7543 }
7545 }
7546
7547 if (message.startsWith("LIVETV_CHAIN"))
7548 {
7549 QString id;
7550 if ((tokens.size() >= 2) && tokens[1] == "UPDATE")
7551 id = tokens[2];
7552
7555 m_playerContext.UpdateTVChain(me->ExtraDataList());
7557 }
7558
7559 if (message.startsWith("EXIT_TO_MENU"))
7560 {
7562 PrepareToExitPlayer(__LINE__);
7563 SetExitPlayer(true, true);
7564 emit DisableEdit(-1);
7566 }
7567
7568 if (message.startsWith("SIGNAL"))
7569 {
7570 cardnum = (tokens.size() >= 2) ? tokens[1].toUInt() : 0;
7571 const QStringList& signalList = me->ExtraDataList();
7572
7574 OSD *osd = GetOSDL();
7575 if (osd)
7576 {
7577 if (m_playerContext.m_recorder && (m_playerContext.GetCardID() == cardnum) && !signalList.empty())
7578 {
7579 UpdateOSDSignal(signalList);
7581 }
7582 }
7583 ReturnOSDLock();
7585 }
7586
7587 if (message.startsWith("NETWORK_CONTROL"))
7588 {
7589 if ((tokens.size() >= 2) &&
7590 (tokens[1] != "ANSWER") && (tokens[1] != "RESPONSE"))
7591 {
7592 QStringList tokens2 = message.split(" ", Qt::SkipEmptyParts);
7593 if ((tokens2.size() >= 2) &&
7594 (tokens2[1] != "ANSWER") && (tokens2[1] != "RESPONSE"))
7595 {
7598 m_networkControlTimerId = StartTimer(1ms, __LINE__);
7599 }
7600 }
7601 }
7602
7603 if (message.startsWith("START_EPG"))
7604 {
7605 int editType = tokens[1].toInt();
7606 QString arg = message.section(" ", 2, -1);
7607 DoEditSchedule(editType, arg);
7608 }
7609
7610 if (message.startsWith("COMMFLAG_START") && (tokens.size() >= 2))
7611 {
7612 uint evchanid = 0;
7613 QDateTime evrecstartts;
7614 ProgramInfo::ExtractKey(tokens[1], evchanid, evrecstartts);
7615
7617 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
7618 bool doit = ((m_playerContext.m_playingInfo) &&
7619 (m_playerContext.m_playingInfo->GetChanID() == evchanid) &&
7621 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
7622
7623 if (doit)
7624 {
7625 QString msg = "COMMFLAG_REQUEST ";
7626 msg += ProgramInfo::MakeUniqueKey(evchanid, evrecstartts);
7628 }
7630 }
7631
7632 if (message.startsWith("COMMFLAG_UPDATE") && (tokens.size() >= 3))
7633 {
7634 uint evchanid = 0;
7635 QDateTime evrecstartts;
7636 ProgramInfo::ExtractKey(tokens[1], evchanid, evrecstartts);
7637
7639 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
7640 bool doit = ((m_playerContext.m_playingInfo) &&
7641 (m_playerContext.m_playingInfo->GetChanID() == evchanid) &&
7643 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
7644
7645 if (doit)
7646 {
7647 frm_dir_map_t newMap;
7648 QStringList mark;
7649 QStringList marks = tokens[2].split(",", Qt::SkipEmptyParts);
7650 for (int j = 0; j < marks.size(); j++)
7651 {
7652 mark = marks[j].split(":", Qt::SkipEmptyParts);
7653 if (marks.size() >= 2)
7654 newMap[mark[0].toULongLong()] = static_cast<MarkTypes>(mark[1].toInt());
7655 }
7656 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
7657 if (m_player)
7658 m_player->SetCommBreakMap(newMap);
7659 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
7660 }
7662 }
7663
7664 if (message == "NOTIFICATION")
7665 {
7666 if (!GetNotificationCenter())
7667 return;
7668 MythNotification mn(*me);
7670 }
7671}
7672
7674{
7676 if (bi.m_chanId)
7677 {
7678 InfoMap infoMap;
7679 QDateTime startts = MythDate::fromString(bi.m_startTime);
7680
7682 RecordingInfo recinfo(bi.m_chanId, startts, false, 0h, &status);
7683 if (RecordingInfo::kFoundProgram == status)
7684 recinfo.QuickRecord();
7685 recinfo.ToMap(infoMap);
7686 infoMap["iconpath"] = ChannelUtil::GetIcon(recinfo.GetChanID());
7687 if ((recinfo.IsVideoFile() || recinfo.IsVideoDVD() ||
7688 recinfo.IsVideoBD()) && recinfo.GetPathname() != recinfo.GetBasename())
7689 {
7690 infoMap["coverartpath"] = VideoMetaDataUtil::GetArtPath(
7691 recinfo.GetPathname(), "Coverart");
7692 infoMap["fanartpath"] = VideoMetaDataUtil::GetArtPath(
7693 recinfo.GetPathname(), "Fanart");
7694 infoMap["bannerpath"] = VideoMetaDataUtil::GetArtPath(
7695 recinfo.GetPathname(), "Banners");
7696 infoMap["screenshotpath"] = VideoMetaDataUtil::GetArtPath(
7697 recinfo.GetPathname(), "Screenshots");
7698 }
7699
7701 InfoMap map;
7702 map.insert("message_text", tr("Record"));
7704 return;
7705 }
7706
7707 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
7709 {
7710 LOG(VB_GENERAL, LOG_CRIT, LOC + "Unknown recording during live tv.");
7711 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
7712 return;
7713 }
7714
7715 QString cmdmsg("");
7717 {
7720 recInfo.ApplyRecordRecGroupChange("Default");
7721 *m_playerContext.m_playingInfo = recInfo;
7722
7723 cmdmsg = tr("Record");
7726 LOG(VB_RECORD, LOG_INFO, LOC + "Toggling Record on");
7727 }
7728 else
7729 {
7732 recInfo.ApplyRecordRecGroupChange("LiveTV");
7733 *m_playerContext.m_playingInfo = recInfo;
7734
7735 cmdmsg = tr("Cancel Record");
7738 LOG(VB_RECORD, LOG_INFO, LOC + "Toggling Record off");
7739 }
7740
7741 QString msg = cmdmsg + " \"" + m_playerContext.m_playingInfo->GetTitle() + "\"";
7742
7743 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
7744
7745 emit ChangeOSDMessage(msg);
7746}
7747
7748void TV::HandleOSDClosed(int OSDType)
7749{
7750 switch (OSDType)
7751 {
7755 break;
7757 m_doSmartForward = false;
7758 break;
7760 m_stretchAdjustment = false;
7761 break;
7763 m_audiosyncAdjustment = false;
7764 gCoreContext->SaveSetting("AudioSyncOffset", QString::number(m_audioState.m_audioOffset.count()));
7765 break;
7768 break;
7771 break;
7773 break;
7774 }
7775}
7776
7778{
7780 if ((kAdjustingPicture_Playback == Type))
7781 {
7785 // Filter out range
7786 sup &= ~kPictureAttributeSupported_Range;
7787 }
7788 else if ((kAdjustingPicture_Channel == Type) || (kAdjustingPicture_Recording == Type))
7789 {
7794 }
7795
7796 return ::next_picattr(static_cast<PictureAttributeSupported>(sup), Attr);
7797}
7798
7800{
7802 if (kPictureAttribute_None == attr)
7803 return;
7804
7805 m_adjustingPicture = Type;
7807
7808 QString title = toTitleString(Type);
7809
7810 int value = 99;
7811 if (kAdjustingPicture_Playback == Type)
7812 {
7814 {
7815 value = m_videoColourState.GetValue(attr);
7816 }
7818 {
7819 value = static_cast<int>(m_audioState.m_volume);
7820 title = tr("Adjust Volume");
7821 }
7822 }
7823
7826
7827 QString text = toString(attr) + " " + toTypeString(Type);
7828
7829 UpdateOSDStatus(title, text, QString::number(value),
7831 value * 10, kOSDTimeout_Med);
7832 emit ChangeOSDPositionUpdates(false);
7833}
7834
7835void TV::ShowOSDCutpoint(const QString &Type)
7836{
7837 if (Type == "EDIT_CUT_POINTS")
7838 {
7839 if (!m_cutlistMenu.IsLoaded())
7840 {
7841 // TODO which translation context to use?
7843 "menu_cutlist.xml", tr("Edit Cut Points"),
7844 metaObject()->className(), "TV Editing");
7845 }
7846
7847 if (m_cutlistMenu.IsLoaded())
7849 }
7850 else if (Type == "EDIT_CUT_POINTS_COMPACT")
7851 {
7853 {
7854 // TODO which translation context to use?
7856 "menu_cutlist_compact.xml", tr("Edit Cut Points"),
7857 metaObject()->className(), "TV Editing");
7858 }
7859
7862 }
7863 else if (Type == "EXIT_EDIT_MODE")
7864 {
7865 MythOSDDialogData dialog { OSD_DLG_CUTPOINT, tr("Exit Recording Editor") };
7866 dialog.m_buttons.push_back( { tr("Save Cuts and Exit"), "DIALOG_CUTPOINT_SAVEEXIT_0" } );
7867 dialog.m_buttons.push_back( { tr("Exit Without Saving"), "DIALOG_CUTPOINT_REVERTEXIT_0" } );
7868 dialog.m_buttons.push_back( { tr("Save Cuts"), "DIALOG_CUTPOINT_SAVEMAP_0" } );
7869 dialog.m_buttons.push_back( { tr("Undo Changes"), "DIALOG_CUTPOINT_REVERT_0" } );
7870 dialog.m_back = { "", "DIALOG_CUTPOINT_DONOTHING_0", true };
7871 emit ChangeOSDDialog(dialog);
7872
7873 InfoMap map;
7874 map.insert("title", tr("Edit"));
7876 }
7877}
7878
7879bool TV::HandleOSDCutpoint(const QString& Action)
7880{
7881 bool res = true;
7883 return res;
7884
7885 OSD *osd = GetOSDL();
7886 if (Action == "DONOTHING" && osd)
7887 {
7888 }
7889 else if (osd)
7890 {
7891 QStringList actions(Action);
7892 if (!m_player->HandleProgramEditorActions(actions))
7893 LOG(VB_GENERAL, LOG_ERR, LOC + "Unrecognised cutpoint action");
7894 }
7895 ReturnOSDLock();
7896 return res;
7897}
7898
7903{
7904 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
7905 bool isEditing = m_playerContext.m_playingInfo->QueryIsEditing();
7906 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
7907
7908 if (isEditing)
7909 {
7911 return;
7912 }
7913
7914 emit EnableEdit();
7915}
7916
7918{
7919 bool paused = ContextIsPaused(__FILE__, __LINE__);
7920 if (!paused)
7921 DoTogglePause(true);
7922
7923 QString message = tr("This program is currently being edited");
7924 QString def = QString("DIALOG_EDITING_CONTINUE_%1").arg(static_cast<int>(paused));
7925 emit ChangeOSDDialog( { OSD_DLG_EDITING, message, 0ms,
7926 { { tr("Continue Editing"), def, false, true },
7927 { tr("Do not edit"), QString("DIALOG_EDITING_STOP_%1").arg(static_cast<int>(paused)) }},
7928 { "", def, true} });
7929}
7930
7931void TV::HandleOSDAlreadyEditing(const QString& Action, bool WasPaused)
7932{
7934 return;
7935
7936 bool paused = ContextIsPaused(__FILE__, __LINE__);
7937
7938 if (Action == "STOP")
7939 {
7940 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
7943 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
7944 if (!WasPaused && paused)
7945 DoTogglePause(true);
7946 }
7947 else // action == "CONTINUE"
7948 {
7949 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
7951 emit EnableEdit();
7952 if (!m_overlayState.m_editing && !WasPaused && paused)
7953 DoTogglePause(false);
7954 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
7955 }
7956
7957}
7958
7959static void insert_map(InfoMap &infoMap, const InfoMap &newMap)
7960{
7961 for (auto it = newMap.cbegin(); it != newMap.cend(); ++it)
7962 infoMap.insert(it.key(), *it);
7963}
7964
7969{
7970 OSD *osd = GetOSDL();
7971 if (!m_playerContext.m_recorder || !osd)
7972 {
7973 ReturnOSDLock();
7974 return;
7975 }
7976 ReturnOSDLock();
7977
7978 QMutexLocker locker(&m_chanEditMapLock);
7979
7980 // Get the info available from the backend
7981 m_chanEditMap.clear();
7983
7984 // Update with XDS Info
7986
7987 // Set proper initial values for channel editor, and make it visible..
7988 osd = GetOSDL();
7989 if (osd)
7990 {
7993 }
7994 ReturnOSDLock();
7995}
7996
7998{
7999 OSD *osd = GetOSDL();
8000 if (osd)
8001 {
8002 emit HideAll();
8003 ToggleOSD(true);
8005 }
8006 ReturnOSDLock();
8007}
8008
8013{
8014 QMutexLocker locker(&m_chanEditMapLock);
8015 bool hide = false;
8016
8018 return hide;
8019
8020 OSD *osd = GetOSDL();
8021 if (osd && Action == "PROBE")
8022 {
8023 InfoMap infoMap;
8024 osd->DialogGetText(infoMap);
8025 ChannelEditAutoFill(infoMap);
8026 insert_map(m_chanEditMap, infoMap);
8028 }
8029 else if (osd && Action == "OK")
8030 {
8031 InfoMap infoMap;
8032 osd->DialogGetText(infoMap);
8033 insert_map(m_chanEditMap, infoMap);
8035 hide = true;
8036 }
8037 else if (osd && Action == "QUIT")
8038 {
8039 hide = true;
8040 }
8041 ReturnOSDLock();
8042 return hide;
8043}
8044
8049{
8050#if 0
8051 const QString keys[4] = { "XMLTV", "callsign", "channame", "channum", };
8052#endif
8053
8054 // fill in uninitialized and unchanged fields from XDS
8055 ChannelEditXDSFill(Info);
8056}
8057
8059{
8060 QMap<QString,bool> modifiable;
8061 modifiable["callsign"] = Info["callsign"].isEmpty();
8062 if (!modifiable["callsign"])
8063 {
8064 QString unsetsign = tr("UNKNOWN%1", "Synthesized callsign");
8065 int unsetcmpl = unsetsign.length() - 2;
8066 unsetsign = unsetsign.left(unsetcmpl);
8067 if (Info["callsign"].left(unsetcmpl) == unsetsign) // was unsetcmpl????
8068 modifiable["callsign"] = true;
8069 }
8070 modifiable["channame"] = Info["channame"].isEmpty();
8071
8072 const std::array<const QString,2> xds_keys { "callsign", "channame", };
8073 for (const auto & key : xds_keys)
8074 {
8075 if (!modifiable[key])
8076 continue;
8077
8078 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
8079 QString tmp = m_player->GetXDS(key).toUpper();
8080 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
8081
8082 if (tmp.isEmpty())
8083 continue;
8084
8085 if ((key == "callsign") &&
8086 ((tmp.length() > 5) || (tmp.indexOf(" ") >= 0)))
8087 {
8088 continue;
8089 }
8090
8091 Info[key] = tmp;
8092 }
8093}
8094
8095void TV::OSDDialogEvent(int Result, const QString& Text, QString Action)
8096{
8098 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("result %1 text %2 action %3")
8099 .arg(QString::number(Result), Text, Action));
8100
8101 bool hide = true;
8102 if (Result == 100)
8103 hide = false;
8104
8105 bool handled = true;
8106 if (Action.startsWith("DIALOG_"))
8107 {
8108 Action.remove("DIALOG_");
8109 QStringList desc = Action.split("_");
8110 bool valid = desc.size() == 3;
8111 if (valid && desc[0] == ACTION_JUMPREC)
8112 {
8113 FillOSDMenuJumpRec(desc[1], desc[2].toInt(), Text);
8114 hide = false;
8115 }
8116 else if (valid && desc[0] == "VIDEOEXIT")
8117 {
8118 hide = HandleOSDVideoExit(desc[1]);
8119 }
8120 else if (valid && desc[0] == "SLEEP")
8121 {
8122 HandleOSDSleep(desc[1]);
8123 }
8124 else if (valid && desc[0] == "IDLE")
8125 {
8126 HandleOSDIdle(desc[1]);
8127 }
8128 else if (valid && desc[0] == "INFO")
8129 {
8130 HandleOSDInfo(desc[1]);
8131 }
8132 else if (valid && desc[0] == "EDITING")
8133 {
8134 HandleOSDAlreadyEditing(desc[1], desc[2].toInt() != 0);
8135 }
8136 else if (valid && desc[0] == "ASKALLOW")
8137 {
8138 HandleOSDAskAllow(desc[1]);
8139 }
8140 else if (valid && desc[0] == "EDITOR")
8141 {
8142 hide = HandleOSDChannelEdit(desc[1]);
8143 }
8144 else if (valid && desc[0] == "CUTPOINT")
8145 {
8146 hide = HandleOSDCutpoint(desc[1]);
8147 }
8148 else if ((valid && desc[0] == "DELETE") ||
8149 (valid && desc[0] == "CONFIRM"))
8150 {
8151 }
8152 else if (valid && desc[0] == ACTION_PLAY)
8153 {
8154 DoPlay();
8155 }
8156 else
8157 {
8158 LOG(VB_GENERAL, LOG_ERR, "Unrecognised dialog event.");
8159 }
8160 }
8161 else if (Result < 0)
8162 { // NOLINT(bugprone-branch-clone)
8163 ; // exit dialog
8164 }
8165 else if (HandleTrackAction(Action))
8166 {
8167 ;
8168 }
8169 else if (Action == ACTION_PAUSE)
8170 {
8171 DoTogglePause(true);
8172 }
8173 else if (Action == ACTION_STOP)
8174 {
8175 PrepareToExitPlayer(__LINE__);
8176 SetExitPlayer(true, true);
8177 }
8178 else if (Action == "CANCELPLAYLIST")
8179 {
8180 SetInPlayList(false);
8181 MythEvent xe("CANCEL_PLAYLIST");
8183 }
8184 else if (Action == ACTION_JUMPFFWD)
8185 {
8186 DoJumpFFWD();
8187 }
8188 else if (Action == ACTION_JUMPRWND)
8189 {
8190 DoJumpRWND();
8191 }
8192 else if (Action == ACTION_SEEKFFWD)
8193 {
8194 DoSeekFFWD();
8195 }
8196 else if (Action == ACTION_SEEKRWND)
8197 {
8198 DoSeekRWND();
8199 }
8200 else if (Action == ACTION_TOGGLEOSDDEBUG)
8201 {
8202 emit ChangeOSDDebug();
8203 }
8204 else if (Action == "TOGGLEMANUALZOOM")
8205 {
8206 SetManualZoom(true, tr("Zoom Mode ON"));
8207 }
8208 else if (Action == ACTION_BOTTOMLINEMOVE)
8209 {
8210 emit ToggleMoveBottomLine();
8211 }
8212 else if (Action == ACTION_BOTTOMLINESAVE)
8213 {
8214 emit SaveBottomLine();
8215 }
8216 else if (Action == "TOGGLESTRETCH")
8217 {
8219 }
8220 else if (Action == ACTION_ENABLEUPMIX)
8221 {
8222 emit ChangeUpmix(true);
8223 }
8224 else if (Action == ACTION_DISABLEUPMIX)
8225 {
8226 emit ChangeUpmix(false);
8227 }
8228 else if (Action.startsWith("ADJUSTSTRETCH"))
8229 {
8230 bool floatRead = false;
8231 float stretch = Action.right(Action.length() - 13).toFloat(&floatRead);
8232 if (floatRead &&
8233 stretch <= 2.0F &&
8234 stretch >= 0.48F)
8235 {
8236 m_playerContext.m_tsNormal = stretch; // alter speed before display
8237 }
8238
8239 StopFFRew();
8240
8241 if (ContextIsPaused(__FILE__, __LINE__))
8242 DoTogglePause(true);
8243
8244 ChangeTimeStretch(0, !floatRead); // just display
8245 }
8246 else if (Action.startsWith("SELECTSCAN_"))
8247 {
8248 OverrideScan(static_cast<FrameScanType>(Action.right(1).toInt()));
8249 }
8250 else if (Action.startsWith(ACTION_TOGGELAUDIOSYNC))
8251 {
8252 emit ChangeAudioOffset(0ms);
8253 }
8255 {
8256 emit AdjustSubtitleZoom(0);
8257 }
8259 {
8260 emit AdjustSubtitleDelay(0ms);
8261 }
8263 {
8264 emit EnableVisualiser(false, true);
8265 }
8267 {
8268 emit EnableVisualiser(true);
8269 }
8271 {
8272 emit EnableVisualiser(false);
8273 }
8274 else if (Action.startsWith(ACTION_TOGGLESLEEP))
8275 {
8276 ToggleSleepTimer(Action.left(13));
8277 }
8278 else if (Action.startsWith("TOGGLEPICCONTROLS"))
8279 {
8280 m_adjustingPictureAttribute = static_cast<PictureAttribute>(Action.right(1).toInt() - 1);
8282 }
8283 else if (Action == "TOGGLEASPECT")
8284 {
8285 emit ChangeAspectOverride();
8286 }
8287 else if (Action.startsWith("TOGGLEASPECT"))
8288 {
8289 emit ChangeAspectOverride(static_cast<AspectOverrideMode>(Action.right(1).toInt()));
8290 }
8291 else if (Action == "TOGGLEFILL")
8292 {
8293 emit ChangeAdjustFill();
8294 }
8295 else if (Action.startsWith("TOGGLEFILL"))
8296 {
8297 emit ChangeAdjustFill(static_cast<AdjustFillMode>(Action.right(1).toInt()));
8298 }
8299 else if (Action == "MENU")
8300 {
8301 ShowOSDMenu();
8302 }
8303 else if (Action == "AUTODETECT_FILL")
8304 {
8305 emit ToggleDetectLetterBox();
8306 }
8307 else if (Action == ACTION_GUIDE)
8308 {
8310 }
8311 else if (Action.startsWith("CHANGROUP_") && m_dbUseChannelGroups)
8312 {
8313 if (Action == "CHANGROUP_ALL_CHANNELS")
8314 {
8316 }
8317 else
8318 {
8319 Action.remove("CHANGROUP_");
8320
8321 UpdateChannelList(Action.toInt());
8322
8323 // make sure the current channel is from the selected group
8324 // or tune to the first in the group
8325 QString cur_channum;
8326 QString new_channum;
8328 {
8329 QMutexLocker locker(&m_channelGroupLock);
8331 cur_channum = m_playerContext.m_tvchain->GetChannelName(-1);
8332 new_channum = cur_channum;
8333
8334 auto it = list.cbegin();
8335 for (; it != list.cend(); ++it)
8336 {
8337 if ((*it).m_chanNum == cur_channum)
8338 {
8339 break;
8340 }
8341 }
8342
8343 if (it == list.end())
8344 {
8345 // current channel not found so switch to the
8346 // first channel in the group
8347 it = list.begin();
8348 if (it != list.end())
8349 new_channum = (*it).m_chanNum;
8350 }
8351
8352 LOG(VB_CHANNEL, LOG_INFO, LOC +
8353 QString("Channel Group: '%1'->'%2'")
8354 .arg(cur_channum, new_channum));
8355 }
8356
8358 {
8359 // Only change channel if new channel != current channel
8360 if (cur_channum != new_channum && !new_channum.isEmpty())
8361 {
8362 m_queuedInput = new_channum;
8363 m_queuedChanNum = new_channum;
8364 m_queuedChanID = 0;
8366 m_queueInputTimerId = StartTimer(10ms, __LINE__);
8367 }
8368
8369 // Turn off OSD Channel Num so the channel
8370 // changes right away
8372 }
8373 }
8374 }
8375 else if (Action == ACTION_FINDER)
8376 {
8378 }
8379 else if (Action == "SCHEDULE")
8380 {
8382 }
8383 else if (Action == ACTION_VIEWSCHEDULED)
8384 {
8386 }
8387 else if (Action == ACTION_CAST)
8388 {
8390 hide = false;
8391 }
8392 else if (Action.startsWith("JUMPCAST|"))
8393 {
8394 QStringList tokens = Action.split("|");
8395 if (tokens.size() == 3)
8396 FillOSDMenuActorShows(tokens[1], tokens[2].toInt());
8397 else if (tokens.size() == 4)
8398 FillOSDMenuActorShows(tokens[1], tokens[2].toInt(), tokens[3]);
8399
8400 hide = false;
8401 }
8402 else if (Action.startsWith("VISUALISER"))
8403 {
8404 emit EnableVisualiser(true, false, Action.mid(11));
8405 }
8406 else if (Action.startsWith("3D"))
8407 {
8409 }
8410 else if (HandleJumpToProgramAction(QStringList(Action)))
8411 {
8412 }
8413 else if (StateIsLiveTV(GetState()))
8414 {
8415 if (Action == "TOGGLEBROWSE")
8416 BrowseStart();
8417 else if (Action == "PREVCHAN")
8418 PopPreviousChannel(true);
8419 else if (Action.startsWith("SWITCHTOINPUT_"))
8420 {
8421 m_switchToInputId = Action.mid(14).toUInt();
8423 }
8424 else if (Action == "EDIT")
8425 {
8427 hide = false;
8428 }
8429 else
8430 {
8431 handled = false;
8432 }
8433 }
8434 else
8435 {
8436 handled = false;
8437 }
8438 if (!handled && StateIsPlaying(m_playerContext.GetState()))
8439 {
8440 handled = true;
8443 {
8445 emit GoToMenu("chapter");
8447 emit GoToMenu("title");
8448 else if (Action == ACTION_JUMPTOPOPUPMENU)
8449 emit GoToMenu("popup");
8450 else
8451 emit GoToMenu("root");
8452 }
8453 else if (Action.startsWith(ACTION_JUMPCHAPTER))
8454 {
8455 int chapter = Action.right(3).toInt();
8456 DoJumpChapter(chapter);
8457 }
8458 else if (Action.startsWith(ACTION_SWITCHTITLE))
8459 {
8460 int title = Action.right(3).toInt();
8461 DoSwitchTitle(title);
8462 }
8463 else if (Action.startsWith(ACTION_SWITCHANGLE))
8464 {
8465 int angle = Action.right(3).toInt();
8466 DoSwitchAngle(angle);
8467 }
8468 else if (Action == "EDIT")
8469 {
8471 hide = false;
8472 }
8473 else if (Action == "TOGGLEAUTOEXPIRE")
8474 {
8476 }
8477 else if (Action.startsWith("TOGGLECOMMSKIP"))
8478 {
8479 SetAutoCommercialSkip(static_cast<CommSkipMode>(Action.right(1).toInt()));
8480 }
8481 else if (Action == "QUEUETRANSCODE")
8482 {
8483 DoQueueTranscode("Default");
8484 }
8485 else if (Action == "QUEUETRANSCODE_AUTO")
8486 {
8487 DoQueueTranscode("Autodetect");
8488 }
8489 else if (Action == "QUEUETRANSCODE_HIGH")
8490 {
8491 DoQueueTranscode("High Quality");
8492 }
8493 else if (Action == "QUEUETRANSCODE_MEDIUM")
8494 {
8495 DoQueueTranscode("Medium Quality");
8496 }
8497 else if (Action == "QUEUETRANSCODE_LOW")
8498 {
8499 DoQueueTranscode("Low Quality");
8500 }
8501 else
8502 {
8503 handled = false;
8504 }
8505 }
8506
8507 if (!handled)
8508 {
8511 handled = ActiveHandleAction(QStringList(Action), isDVD, isMenuOrStill);
8512 }
8513
8514 if (!handled)
8515 handled = ActivePostQHandleAction(QStringList(Action));
8516
8517 if (!handled)
8518 {
8519 LOG(VB_GENERAL, LOG_ERR, LOC +
8520 "Unknown menu action selected: " + Action);
8521 hide = false;
8522 }
8523
8524 if (hide)
8525 emit DialogQuit();
8527}
8528
8529bool TV::DialogIsVisible(const QString &Dialog)
8530{
8531 bool visible = false;
8532 OSD *osd = GetOSDL();
8533 if (osd)
8534 visible = osd->DialogVisible(Dialog);
8535 ReturnOSDLock();
8536 return visible;
8537}
8538
8539void TV::HandleOSDInfo(const QString& Action)
8540{
8542 return;
8543
8544 if (Action == "CHANNELLOCK")
8545 m_lockTimerOn = false;
8546}
8547
8548// NOLINTBEGIN(cppcoreguidelines-macro-usage)
8549#define BUTTON(action, text) \
8550 result = Context.AddButton(Menu, active, (action), (text), "", false, "")
8551#define BUTTON2(action, textActive, textInactive) \
8552 result = Context.AddButton(Menu, active, (action), (textActive), (textInactive), false, "")
8553#define BUTTON3(action, textActive, textInactive, isMenu) \
8554 result = Context.AddButton(Menu, active, (action), (textActive), (textInactive), (isMenu), "")
8555// NOLINTEND(cppcoreguidelines-macro-usage)
8556
8558{
8559 if (&Context.m_menu == &m_playbackMenu || &Context.m_menu == &m_playbackCompactMenu)
8560 return MenuItemDisplayPlayback(Context, Menu);
8561 if (&Context.m_menu == &m_cutlistMenu || &Context.m_menu == &m_cutlistCompactMenu)
8562 return MenuItemDisplayCutlist(Context, Menu);
8563 return false;
8564}
8565
8567{
8568 MenuCategory category = Context.m_category;
8569 const QString &actionName = Context.m_action;
8570
8571 bool result = false;
8572 if (category == kMenuCategoryMenu)
8573 {
8574 result = Context.m_menu.Show(Context.m_node, QDomNode(), *this, Menu, false);
8575 if (result && Context.m_visible)
8576 {
8577 QVariant v;
8578 v.setValue(MythTVMenuNodeTuple(Context.m_menu.m_id,
8580 Menu->m_buttons.push_back( { Context.m_menuName, v, true,
8582 }
8583 return result;
8584 }
8585
8586 emit RefreshEditorState();
8587
8588 if (category == kMenuCategoryItem)
8589 {
8590 bool active = true;
8591 if (actionName == "DIALOG_CUTPOINT_MOVEPREV_0")
8592 {
8597 {
8599 BUTTON2(actionName, tr("Move Previous Cut End Here"), tr("Move Start of Cut Here"));
8600 }
8601 }
8602 else if (actionName == "DIALOG_CUTPOINT_MOVENEXT_0")
8603 {
8608 {
8610 BUTTON2(actionName, tr("Move Next Cut Start Here"), tr("Move End of Cut Here"));
8611 }
8612 }
8613 else if (actionName == "DIALOG_CUTPOINT_CUTTOBEGINNING_0")
8614 {
8616 BUTTON(actionName, tr("Cut to Beginning"));
8617 }
8618 else if (actionName == "DIALOG_CUTPOINT_CUTTOEND_0")
8619 {
8622 {
8623 BUTTON(actionName, tr("Cut to End"));
8624 }
8625 }
8626 else if (actionName == "DIALOG_CUTPOINT_DELETE_0")
8627 {
8629 BUTTON2(actionName, tr("Delete This Cut"), tr("Join Surrounding Cuts"));
8630 }
8631 else if (actionName == "DIALOG_CUTPOINT_NEWCUT_0")
8632 {
8634 BUTTON(actionName, tr("Add New Cut"));
8635 }
8636 else if (actionName == "DIALOG_CUTPOINT_UNDO_0")
8637 {
8638 active = m_editorState.m_hasUndo;
8639 //: %1 is the undo message
8640 QString text = tr("Undo - %1");
8641 result = Context.AddButton(Menu, active, actionName, text, "", false,
8643 }
8644 else if (actionName == "DIALOG_CUTPOINT_REDO_0")
8645 {
8646 active = m_editorState.m_hasRedo;
8647 //: %1 is the redo message
8648 QString text = tr("Redo - %1");
8649 result = Context.AddButton(Menu, active, actionName, text, "", false,
8651 }
8652 else if (actionName == "DIALOG_CUTPOINT_CLEARMAP_0")
8653 {
8654 BUTTON(actionName, tr("Clear Cuts"));
8655 }
8656 else if (actionName == "DIALOG_CUTPOINT_INVERTMAP_0")
8657 {
8658 BUTTON(actionName, tr("Reverse Cuts"));
8659 }
8660 else if (actionName == "DIALOG_CUTPOINT_LOADCOMMSKIP_0")
8661 {
8662 BUTTON(actionName, tr("Load Detected Commercials"));
8663 }
8664 else if (actionName == "DIALOG_CUTPOINT_REVERT_0")
8665 {
8666 BUTTON(actionName, tr("Undo Changes"));
8667 }
8668 else if (actionName == "DIALOG_CUTPOINT_REVERTEXIT_0")
8669 {
8670 BUTTON(actionName, tr("Exit Without Saving"));
8671 }
8672 else if (actionName == "DIALOG_CUTPOINT_SAVEMAP_0")
8673 {
8674 BUTTON(actionName, tr("Save Cuts"));
8675 }
8676 else if (actionName == "DIALOG_CUTPOINT_SAVEEXIT_0")
8677 {
8678 BUTTON(actionName, tr("Save Cuts and Exit"));
8679 }
8680 else
8681 {
8682 // Allow an arbitrary action if it has a translated
8683 // description available to be used as the button text.
8684 // Look in the specified keybinding context as well as the
8685 // Global context.
8686 // XXX This doesn't work well (yet) because a keybinding
8687 // action named "foo" is actually a menu action named
8688 // "DIALOG_CUTPOINT_foo_0".
8689 QString text = m_mainWindow->GetActionText(Context.m_menu.GetKeyBindingContext(), actionName);
8690 if (text.isEmpty())
8691 text = m_mainWindow->GetActionText("Global", actionName);
8692 if (!text.isEmpty())
8693 BUTTON(actionName, text);
8694 }
8695 }
8696
8697 return result;
8698}
8699
8700// Returns true if at least one item should be displayed.
8702 MythOSDDialogData *Menu)
8703{
8704 MenuCategory category = Context.m_category;
8705 const QString &actionName = Context.m_action;
8706
8707 bool result = false;
8708 bool active = true;
8709
8710 if (category == kMenuCategoryMenu)
8711 {
8712 result = Context.m_menu.Show(Context.m_node, QDomNode(), *this, Menu, false);
8713 if (result && Context.m_visible)
8714 {
8715 QVariant v;
8716 v.setValue(MythTVMenuNodeTuple(Context.m_menu.m_id,
8718 Menu->m_buttons.push_back( { Context.m_menuName, v, true,
8720 }
8721 return result;
8722 }
8723 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
8724 QString prefix;
8725 if (MythTVMenu::MatchesGroup(actionName, "VISUALISER_", category, prefix) &&
8727 {
8728 for (auto & visualiser : m_visualiserState.m_visualiserList)
8729 {
8730 active = m_visualiserState.m_visualiserName == visualiser;
8731 BUTTON(prefix + visualiser, visualiser);
8732 }
8733 }
8734 else if (MythTVMenu::MatchesGroup(actionName, "TOGGLEASPECT", category, prefix))
8735 {
8736 for (int j = kAspect_Off; j < kAspect_END; j++)
8737 {
8738 // swap 14:9 and 16:9
8739 int i {j};
8740 if (kAspect_14_9 == j)
8741 i = kAspect_16_9;
8742 else if (kAspect_16_9 == j)
8743 i = kAspect_14_9;
8744 QString action = prefix + QString::number(i);
8746 BUTTON(action, toString(static_cast<AspectOverrideMode>(i)));
8747 }
8748 }
8749 else if (MythTVMenu::MatchesGroup(actionName, "TOGGLEFILL", category, prefix))
8750 {
8751 for (int i = kAdjustFill_Off; i < kAdjustFill_END; i++)
8752 {
8753 QString action = prefix + QString::number(i);
8754 active = (m_videoBoundsState.m_adjustFillMode == i);
8755 BUTTON(action, toString(static_cast<AdjustFillMode>(i)));
8756 }
8757 }
8758 else if (MythTVMenu::MatchesGroup(actionName, "TOGGLEPICCONTROLS", category, prefix))
8759 {
8760 for (int i = kPictureAttribute_MIN; i < kPictureAttribute_MAX; i++)
8761 {
8763 {
8764 QString action = prefix + QString::number(i - kPictureAttribute_MIN);
8765 if (static_cast<PictureAttribute>(i) != kPictureAttribute_Range)
8766 BUTTON(action, toString(static_cast<PictureAttribute>(i)));
8767 }
8768 }
8769 }
8770 else if (MythTVMenu::MatchesGroup(actionName, "3D", category, prefix))
8771 {
8773 BUTTON(ACTION_3DNONE, tr("Auto"));
8775 BUTTON(ACTION_3DIGNORE, tr("Ignore"));
8777 BUTTON(ACTION_3DSIDEBYSIDEDISCARD, tr("Discard Side by Side"));
8779 BUTTON(ACTION_3DTOPANDBOTTOMDISCARD, tr("Discard Top and Bottom"));
8780 }
8781 else if (MythTVMenu::MatchesGroup(actionName, "SELECTSCAN_", category, prefix) && m_player)
8782 {
8784 active = (scan == kScan_Detect);
8785 BUTTON("SELECTSCAN_0", ScanTypeToUserString(kScan_Detect));
8786 active = (scan == kScan_Progressive);
8788 active = (scan == kScan_Interlaced);
8790 active = (scan == kScan_Intr2ndField);
8792 }
8793 else if (MythTVMenu::MatchesGroup(actionName, "SELECTSUBTITLE_", category, prefix) ||
8794 MythTVMenu::MatchesGroup(actionName, "SELECTRAWTEXT_", category, prefix) ||
8795 MythTVMenu::MatchesGroup(actionName, "SELECTCC708_", category, prefix) ||
8796 MythTVMenu::MatchesGroup(actionName, "SELECTCC608_", category, prefix) ||
8797 MythTVMenu::MatchesGroup(actionName, "SELECTTTC_", category, prefix) ||
8798 MythTVMenu::MatchesGroup(actionName, "SELECTAUDIO_", category, prefix))
8799 {
8800 int i = 0;
8802 if (prefix == "SELECTSUBTITLE_")
8804 else if (prefix == "SELECTRAWTEXT_")
8806 else if (prefix == "SELECTCC708_")
8808 else if (prefix == "SELECTCC608_")
8810 else if (prefix == "SELECTTTC_")
8812 else if (prefix == "SELECTAUDIO_")
8813 {
8815 if (m_tvmTracks[type].size() <= 1)
8816 i = 1; // don't show choices if only 1 audio track
8817 }
8818
8819 for (; i < m_tvmTracks[type].size(); i++)
8820 {
8821 QString action = prefix + QString::number(i);
8822 active = (i == m_tvmCurtrack[type]);
8824 }
8825 }
8826 else if (MythTVMenu::MatchesGroup(actionName, "ADJUSTSTRETCH", category, prefix))
8827 {
8828 struct speed
8829 {
8830 int m_speedX100;
8831 QString m_suffix;
8832 QString m_trans;
8833 };
8834
8835 static const std::array<const speed,9> s_speeds {{
8836 { 0, "", tr("Adjust")},
8837 { 50, "0.5", tr("0.5x")},
8838 { 90, "0.9", tr("0.9x")},
8839 {100, "1.0", tr("1.0x")},
8840 {110, "1.1", tr("1.1x")},
8841 {120, "1.2", tr("1.2x")},
8842 {130, "1.3", tr("1.3x")},
8843 {140, "1.4", tr("1.4x")},
8844 {150, "1.5", tr("1.5x")},
8845 }};
8846
8847 for (const auto & speed : s_speeds)
8848 {
8849 QString action = prefix + speed.m_suffix;
8850 active = (m_tvmSpeedX100 == speed.m_speedX100);
8851 BUTTON(action, speed.m_trans);
8852 }
8853 }
8854 else if (MythTVMenu::MatchesGroup(actionName, "TOGGLESLEEP", category, prefix))
8855 {
8856 active = false;
8857 if (m_sleepTimerId)
8858 BUTTON(ACTION_TOGGLESLEEP + "ON", tr("Sleep Off"));
8859 BUTTON(ACTION_TOGGLESLEEP + "30", tr("%n minute(s)", "", 30));
8860 BUTTON(ACTION_TOGGLESLEEP + "60", tr("%n minute(s)", "", 60));
8861 BUTTON(ACTION_TOGGLESLEEP + "90", tr("%n minute(s)", "", 90));
8862 BUTTON(ACTION_TOGGLESLEEP + "120", tr("%n minute(s)", "", 120));
8863 }
8864 else if (MythTVMenu::MatchesGroup(actionName, "CHANGROUP_", category, prefix))
8865 {
8867 {
8868 active = false;
8869 BUTTON("CHANGROUP_ALL_CHANNELS", tr("All Channels"));
8870 ChannelGroupList::const_iterator it;
8871 for (it = m_dbChannelGroups.begin();
8872 it != m_dbChannelGroups.end(); ++it)
8873 {
8874 QString action = prefix + QString::number(it->m_grpId);
8875 active = (static_cast<int>(it->m_grpId) == m_channelGroupId);
8876 BUTTON(action, it->m_name);
8877 }
8878 }
8879 }
8880 else if (MythTVMenu::MatchesGroup(actionName, "TOGGLECOMMSKIP", category, prefix))
8881 {
8883 {
8884 static constexpr std::array<const uint,3> kCasOrd { 0, 2, 1 };
8885 for (uint csm : kCasOrd)
8886 {
8887 const auto mode = static_cast<CommSkipMode>(csm);
8888 QString action = prefix + QString::number(csm);
8889 active = (mode == m_tvmCurSkip);
8890 BUTTON(action, toString(static_cast<CommSkipMode>(csm)));
8891 }
8892 }
8893 }
8894 else if (MythTVMenu::MatchesGroup(actionName, "JUMPTOCHAPTER", category, prefix))
8895 {
8896 if (m_tvmNumChapters &&
8898 {
8899 int size = QString::number(m_tvmNumChapters).size();
8900 for (int i = 0; i < m_tvmNumChapters; i++)
8901 {
8902 QString chapter1 = QString("%1").arg(i+1, size, 10, QChar(48));
8903 QString chapter2 = QString("%1").arg(i+1, 3 , 10, QChar(48));
8904 QString timestr = MythDate::formatTime(m_tvmChapterTimes[i], "HH:mm:ss");
8905 QString desc = chapter1 + QString(" (%1)").arg(timestr);
8906 QString action = prefix + chapter2;
8907 active = (m_tvmCurrentChapter == (i + 1));
8908 BUTTON(action, desc);
8909 }
8910 }
8911 }
8912 else if (MythTVMenu::MatchesGroup(actionName, "SWITCHTOANGLE", category, prefix))
8913 {
8914 if (m_tvmNumAngles > 1)
8915 {
8916 for (int i = 1; i <= m_tvmNumAngles; i++)
8917 {
8918 QString angleIdx = QString("%1").arg(i, 3, 10, QChar(48));
8919 QString desc = GetAngleName(i);
8920 QString action = prefix + angleIdx;
8921 active = (m_tvmCurrentAngle == i);
8922 BUTTON(action, desc);
8923 }
8924 }
8925 }
8926 else if (MythTVMenu::MatchesGroup(actionName, "JUMPTOTITLE", category, prefix))
8927 {
8928 for (int i = 0; i < m_tvmNumTitles; i++)
8929 {
8930 if (GetTitleDuration(i) < 2min) // Ignore < 2 minutes long
8931 continue;
8932
8933 QString titleIdx = QString("%1").arg(i, 3, 10, QChar(48));
8934 QString desc = GetTitleName(i);
8935 QString action = prefix + titleIdx;
8936 active = (m_tvmCurrentTitle == i);
8937 BUTTON(action, desc);
8938 }
8939 }
8940 else if (MythTVMenu::MatchesGroup(actionName, "SWITCHTOINPUT_", category, prefix))
8941 {
8943 {
8944 uint inputid = m_playerContext.GetCardID();
8945 std::vector<InputInfo> inputs = RemoteRequestFreeInputInfo(inputid);
8946 QVector <QString> addednames;
8947 addednames += CardUtil::GetDisplayName(inputid);
8948 for (auto & input : inputs)
8949 {
8950 if (input.m_inputId == inputid ||
8951 addednames.contains(input.m_displayName))
8952 continue;
8953 active = false;
8954 addednames += input.m_displayName;
8955 QString action = QString("SWITCHTOINPUT_") +
8956 QString::number(input.m_inputId);
8957 BUTTON(action, input.m_displayName);
8958 }
8959 }
8960 }
8961 else if (MythTVMenu::MatchesGroup(actionName, "SWITCHTOSOURCE_", category, prefix))
8962 {
8964 {
8965 uint inputid = m_playerContext.GetCardID();
8966 InfoMap info;
8968 uint sourceid = info["sourceid"].toUInt();
8969 QMap<uint, bool> sourceids;
8970 std::vector<InputInfo> inputs = RemoteRequestFreeInputInfo(inputid);
8971 for (auto & input : inputs)
8972 {
8973 if (input.m_sourceId == sourceid ||
8974 sourceids[input.m_sourceId])
8975 continue;
8976 active = false;
8977 sourceids[input.m_sourceId] = true;
8978 QString action = QString("SWITCHTOINPUT_") +
8979 QString::number(input.m_inputId);
8980 BUTTON(action, SourceUtil::GetSourceName(input.m_sourceId));
8981 }
8982 }
8983 }
8984 else if (category == kMenuCategoryItem)
8985 {
8986 if (actionName == "TOGGLEAUDIOSYNC")
8987 {
8988 BUTTON(actionName, tr("Adjust Audio Sync"));
8989 }
8990 else if (m_visualiserState.m_canVisualise && (actionName == "DISABLEVISUALISATION"))
8991 {
8992 BUTTON(actionName, tr("None"));
8993 }
8994 else if (actionName == "DISABLEUPMIX")
8995 {
8997 {
8998 active = !m_audioState.m_isUpmixing;
8999 BUTTON(actionName, tr("Disable Audio Upmixer"));
9000 }
9001 }
9002 else if (actionName == "ENABLEUPMIX")
9003 {
9005 {
9006 active = m_audioState.m_isUpmixing;
9007 BUTTON(actionName, tr("Auto Detect"));
9008 }
9009 }
9010 else if (actionName == "AUTODETECT_FILL")
9011 {
9013 {
9014 active =
9017 BUTTON(actionName, tr("Auto Detect"));
9018 }
9019 }
9020 else if (actionName == "TOGGLEMANUALZOOM")
9021 {
9022 BUTTON(actionName, tr("Manual Zoom Mode"));
9023 }
9024 else if (actionName == "DISABLESUBS")
9025 {
9028 BUTTON(actionName, tr("Disable Subtitles"));
9029 }
9030 else if (actionName == "ENABLESUBS")
9031 {
9034 BUTTON(actionName, tr("Enable Subtitles"));
9035 }
9036 else if (actionName == "DISABLEFORCEDSUBS")
9037 {
9038 active = !m_tvmSubsForcedOn;
9039 if (!m_tvmTracks[kTrackTypeSubtitle].empty() ||
9041 {
9042 BUTTON(actionName, tr("Disable Forced Subtitles"));
9043 }
9044 }
9045 else if (actionName == "ENABLEFORCEDSUBS")
9046 {
9047 active = m_tvmSubsForcedOn;
9048 if (!m_tvmTracks[kTrackTypeSubtitle].empty() ||
9050 {
9051 BUTTON(actionName, tr("Enable Forced Subtitles"));
9052 }
9053 }
9054 else if (actionName == "DISABLEEXTTEXT")
9055 {
9058 BUTTON(actionName, tr("Disable External Subtitles"));
9059 }
9060 else if (actionName == "ENABLEEXTTEXT")
9061 {
9064 BUTTON(actionName, tr("Enable External Subtitles"));
9065 }
9066 else if (actionName == "TOGGLETTM")
9067 {
9068 if (!m_tvmTracks[kTrackTypeTeletextMenu].empty())
9069 BUTTON(actionName, tr("Toggle Teletext Menu"));
9070 }
9071 else if (actionName == "TOGGLESUBZOOM")
9072 {
9074 BUTTON(actionName, tr("Adjust Subtitle Zoom"));
9075 }
9076 else if (actionName == "TOGGLESUBDELAY")
9077 {
9081 {
9082 BUTTON(actionName, tr("Adjust Subtitle Delay"));
9083 }
9084 }
9085 else if (actionName == "PAUSE")
9086 {
9087 active = m_tvmIsPaused;
9088 BUTTON2(actionName, tr("Play"), tr("Pause"));
9089 }
9090 else if (actionName == "TOGGLESTRETCH")
9091 {
9092 BUTTON(actionName, tr("Toggle"));
9093 }
9094 else if (actionName == "TOGGLEBROWSE")
9095 {
9097 BUTTON(actionName, tr("Toggle Browse Mode"));
9098 }
9099 else if (actionName == "CANCELPLAYLIST")
9100 {
9101 if (m_inPlaylist)
9102 BUTTON(actionName, tr("Cancel Playlist"));
9103 }
9104 else if (actionName == "DEBUGOSD")
9105 {
9106 BUTTON(actionName, tr("Playback Data"));
9107 }
9108 else if (actionName == "JUMPFFWD")
9109 {
9110 if (m_tvmJump)
9111 BUTTON(actionName, tr("Jump Ahead"));
9112 }
9113 else if (actionName == "JUMPRWND")
9114 {
9115 if (m_tvmJump)
9116 BUTTON(actionName, tr("Jump Back"));
9117 }
9118 else if (actionName == "JUMPTODVDROOTMENU")
9119 {
9120 if (m_tvmIsBd || m_tvmIsDvd)
9121 {
9122 active = m_tvmIsDvd;
9123 BUTTON2(actionName, tr("DVD Root Menu"), tr("Top menu"));
9124 }
9125 }
9126 else if (actionName == "JUMPTOPOPUPMENU")
9127 {
9128 if (m_tvmIsBd)
9129 BUTTON(actionName, tr("Popup menu"));
9130 }
9131 else if (actionName == "JUMPTODVDTITLEMENU")
9132 {
9133 if (m_tvmIsDvd)
9134 BUTTON(actionName, tr("DVD Title Menu"));
9135 }
9136 else if (actionName == "JUMPTODVDCHAPTERMENU")
9137 {
9138 if (m_tvmIsDvd)
9139 BUTTON(actionName, tr("DVD Chapter Menu"));
9140 }
9141 else if (actionName == "PREVCHAN")
9142 {
9144 BUTTON(actionName, tr("Previous Channel"));
9145 }
9146 else if (actionName == "GUIDE")
9147 {
9148 BUTTON(actionName, tr("Program Guide"));
9149 }
9150 else if (actionName == "FINDER")
9151 {
9152 BUTTON(actionName, tr("Program Finder"));
9153 }
9154 else if (actionName == "VIEWSCHEDULED")
9155 {
9156 BUTTON(actionName, tr("Upcoming Recordings"));
9157 }
9158 else if (actionName == "SCHEDULE")
9159 {
9160 BUTTON(actionName, tr("Edit Recording Schedule"));
9161 }
9162 else if (actionName == "DIALOG_JUMPREC_X_0")
9163 {
9164 BUTTON3(actionName, tr("Recorded Program"), "", true);
9165 QVariant v;
9166 v.setValue(MythTVMenuNodeTuple(Context.m_menu.m_id,
9169 }
9170 else if (actionName == "JUMPPREV")
9171 {
9172 if (m_lastProgram != nullptr)
9173 {
9174 if (m_lastProgram->GetSubtitle().isEmpty())
9175 BUTTON(actionName, m_lastProgram->GetTitle());
9176 else
9177 {
9178 BUTTON(actionName,
9179 QString("%1: %2")
9180 .arg(m_lastProgram->GetTitle(),
9182 }
9183 }
9184 }
9185 else if (actionName == "EDIT")
9186 {
9189 {
9190 active = m_tvmIsLiveTv;
9191 BUTTON2(actionName, tr("Edit Channel"), tr("Edit Recording"));
9192 }
9193 }
9194 else if (actionName == "TOGGLEAUTOEXPIRE")
9195 {
9197 {
9198 active = m_tvmIsOn;
9199 BUTTON2(actionName,
9200 tr("Turn Auto-Expire OFF"), tr("Turn Auto-Expire ON"));
9201 }
9202 }
9203 else if (actionName == "QUEUETRANSCODE")
9204 {
9205 if (m_tvmIsRecorded)
9206 {
9207 active = m_tvmTranscoding;
9208 BUTTON2(actionName, tr("Stop Transcoding"), tr("Default"));
9209 }
9210 }
9211 else if (actionName == "QUEUETRANSCODE_AUTO")
9212 {
9213 if (m_tvmIsRecorded)
9214 {
9215 active = m_tvmTranscoding;
9216 BUTTON(actionName, tr("Autodetect"));
9217 }
9218 }
9219 else if (actionName == "QUEUETRANSCODE_HIGH")
9220 {
9221 if (m_tvmIsRecorded)
9222 {
9223 active = m_tvmTranscoding;
9224 BUTTON(actionName, tr("High Quality"));
9225 }
9226 }
9227 else if (actionName == "QUEUETRANSCODE_MEDIUM")
9228 {
9229 if (m_tvmIsRecorded)
9230 {
9231 active = m_tvmTranscoding;
9232 BUTTON(actionName, tr("Medium Quality"));
9233 }
9234 }
9235 else if (actionName == "QUEUETRANSCODE_LOW")
9236 {
9237 if (m_tvmIsRecorded)
9238 {
9239 active = m_tvmTranscoding;
9240 BUTTON(actionName, tr("Low Quality"));
9241 }
9242 }
9243 else if (actionName == ACTION_CAST)
9244 {
9245 if (!m_actors.isEmpty() || !m_guest_stars.isEmpty() ||
9246 !m_guests.isEmpty())
9247 BUTTON(actionName, tr("Cast"));
9248 }
9249 else
9250 {
9251 // Allow an arbitrary action if it has a translated
9252 // description available to be used as the button text.
9253 // Look in the specified keybinding context as well as the
9254 // Global context.
9255 QString text = m_mainWindow->GetActionText(Context.m_menu.GetKeyBindingContext(), actionName);
9256 if (text.isEmpty())
9257 text = m_mainWindow->GetActionText("Global", actionName);
9258 if (!text.isEmpty())
9259 BUTTON(actionName, text);
9260 }
9261 }
9262
9263 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
9264 return result;
9265}
9266
9267void TV::MenuLazyInit(void *Field)
9268{
9269 if (Field == &m_tvmFreeRecorderCount)
9270 if (m_tvmFreeRecorderCount < 0)
9272}
9273
9275{
9277 if (&Menu != &m_playbackMenu && &Menu != &m_playbackCompactMenu)
9278 return;
9279
9280 m_tvmAvsync = true;
9281
9282 m_tvmFillAutoDetect = false;
9283
9284 m_tvmSpeedX100 = std::lroundf(m_playerContext.m_tsNormal * 100);
9290 m_tvmIsPaused = false;
9295 m_tvmJump = ((m_tvmNumChapters == 0) && !m_tvmIsDvd &&
9299 m_tvmPreviousChan = false;
9300
9307 m_tvmChapterTimes.clear();
9309
9310 m_tvmSubsForcedOn = true;
9311 m_tvmSubsHaveSubs = false;
9312
9313 for (int i = kTrackTypeUnknown ; i < kTrackTypeCount ; ++i)
9314 m_tvmCurtrack[i] = -1;
9315
9316 if (m_tvmIsLiveTv)
9317 {
9318 QString prev_channum = m_playerContext.GetPreviousChannel();
9319 QString cur_channum = QString();
9321 cur_channum = m_playerContext.m_tvchain->GetChannelName(-1);
9322 if (!prev_channum.isEmpty() && prev_channum != cur_channum)
9323 m_tvmPreviousChan = true;
9324 }
9325
9326 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
9327
9329 {
9330 for (uint i = kTrackTypeUnknown ; i < kTrackTypeCount ; ++i)
9331 {
9333 if (!m_tvmTracks[i].empty())
9335 }
9337 !m_tvmTracks[kTrackTypeSubtitle].empty() ||
9339 !m_tvmTracks[kTrackTypeCC708].empty() ||
9340 !m_tvmTracks[kTrackTypeCC608].empty() ||
9344 !m_tvmTracks[kTrackTypeAudio].empty();
9349 if (vo)
9350 {
9352 }
9353 }
9354 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
9359 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
9360
9361 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
9362}
9363
9365{
9367}
9368
9369void TV::PlaybackMenuShow(const MythTVMenu &Menu, const QDomNode &Node, const QDomNode &Selected)
9370{
9371 PlaybackMenuInit(Menu);
9372 bool isPlayback = (&Menu == &m_playbackMenu || &Menu == &m_playbackCompactMenu);
9373 bool isCutlist = (&Menu == &m_cutlistMenu || &Menu == &m_cutlistCompactMenu);
9374 QString text = Menu.Translate(Node.toElement().attribute("text", Menu.GetName()));
9375 const char* windowtitle { "???" };
9376 if (isPlayback)
9377 windowtitle = OSD_DLG_MENU;
9378 else if (isCutlist)
9379 windowtitle = OSD_DLG_CUTPOINT;
9380 MythOSDDialogData menu {windowtitle, text };
9381 Menu.Show(Node, Selected, *this, &menu);
9382 QDomNode parent = Node.parentNode();
9383 if (!parent.parentNode().isNull())
9384 {
9385 QVariant v;
9386 v.setValue(MythTVMenuNodeTuple(Menu.m_id, MythTVMenu::GetPathFromNode(Node)));
9387 menu.m_back = { "", v };
9388 }
9389
9390 emit ChangeOSDDialog(menu);
9391
9392 if (isCutlist)
9393 {
9394 // hack to unhide the editbar
9395 InfoMap map;
9396 map.insert("title", tr("Edit"));
9398 }
9399 PlaybackMenuDeinit(Menu);
9400}
9401
9403{
9404 // Playback menu
9405 (void)tr("Playback Menu");
9406 (void)tr("Playback Compact Menu");
9407 (void)tr("Audio");
9408 (void)tr("Select Audio Track");
9409 (void)tr("Visualisation");
9410 (void)tr("Video");
9411 (void)tr("Change Aspect Ratio");
9412 (void)tr("Adjust Fill");
9413 (void)tr("Adjust Picture");
9414 (void)tr("3D");
9415 (void)tr("Advanced");
9416 (void)tr("Video Scan");
9417 (void)tr("Deinterlacer");
9418 (void)tr("Subtitles");
9419 (void)tr("Select Subtitle");
9420 (void)tr("Text Subtitles");
9421 (void)tr("Select ATSC CC");
9422 (void)tr("Select VBI CC");
9423 (void)tr("Select Teletext CC");
9424 (void)tr("Playback");
9425 (void)tr("Adjust Time Stretch");
9426 (void)tr("Picture-in-Picture");
9427 (void)tr("Sleep");
9428 (void)tr("Channel Groups");
9429 (void)tr("Navigate");
9430 (void)tr("Commercial Auto-Skip");
9431 (void)tr("Chapter");
9432 (void)tr("Angle");
9433 (void)tr("Title");
9434 (void)tr("Schedule");
9435 (void)tr("Source");
9436 (void)tr("Jump to Program");
9437 (void)tr("Switch Input");
9438 (void)tr("Switch Source");
9439 (void)tr("Jobs");
9440 (void)tr("Begin Transcoding");
9441 (void)tr("Cast");
9442 (void)tr("Recorded");
9443 (void)tr("Upcoming");
9444
9445 // Cutlist editor menu
9446 (void)tr("Edit Cut Points");
9447 (void)tr("Edit Cut Points (Compact)");
9448 (void)tr("Cut List Options");
9449}
9450
9451void TV::ShowOSDMenu(bool isCompact)
9452{
9453 if (!m_playbackMenu.IsLoaded())
9454 {
9456 "menu_playback.xml", tr("Playback Menu"),
9457 metaObject()->className(), "TV Playback");
9459 "menu_playback_compact.xml", tr("Playback Compact Menu"),
9460 metaObject()->className(), "TV Playback");
9461 }
9462
9463 if (isCompact && m_playbackCompactMenu.IsLoaded())
9465 else if (m_playbackMenu.IsLoaded())
9467}
9468
9469void TV::FillOSDMenuJumpRec(const QString &Category, int Level, const QString &Selected)
9470{
9471 // bool in_recgroup = !category.isEmpty() && level > 0;
9472 if (Level < 0 || Level > 1)
9473 {
9474 Level = 0;
9475 // in_recgroup = false;
9476 }
9477
9478 MythOSDDialogData dialog { "osd_jumprec", tr("Recorded Program") };
9479
9480 QMutexLocker locker(&m_progListsLock);
9481 m_progLists.clear();
9482 std::vector<ProgramInfo*> *infoList = RemoteGetRecordedList(0);
9483 bool LiveTVInAllPrograms = gCoreContext->GetBoolSetting("LiveTVInAllPrograms",false);
9484 if (infoList)
9485 {
9486 QList<QString> titles_seen;
9487
9488 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
9489 QString currecgroup = m_playerContext.m_playingInfo->GetRecordingGroup();
9490 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
9491
9492 for (auto *pi : *infoList)
9493 {
9494 if (pi->GetRecordingGroup() != "LiveTV" || LiveTVInAllPrograms ||
9495 pi->GetRecordingGroup() == currecgroup)
9496 {
9497 m_progLists[pi->GetRecordingGroup()].push_front(
9498 new ProgramInfo(*pi));
9499 }
9500 }
9501
9502 ProgramInfo *lastprog = GetLastProgram();
9503 QMap<QString,ProgramList>::const_iterator Iprog;
9504 for (Iprog = m_progLists.cbegin(); Iprog != m_progLists.cend(); ++Iprog)
9505 {
9506 const ProgramList &plist = *Iprog;
9507 auto progIndex = static_cast<uint>(plist.size());
9508 const QString& group = Iprog.key();
9509
9510 if (plist[0] && (plist[0]->GetRecordingGroup() != currecgroup))
9511 SetLastProgram(plist[0]);
9512
9513 if (progIndex == 1 && Level == 0)
9514 {
9515 dialog.m_buttons.push_back( {Iprog.key(), QString("JUMPPROG %1 0").arg(group) });
9516 }
9517 else if (progIndex > 1 && Level == 0)
9518 {
9519 QString act = QString("DIALOG_%1_%2_1")
9520 .arg(ACTION_JUMPREC, group);
9521 dialog.m_buttons.push_back( {group, act, true, Selected == group });
9522 }
9523 else if (Level == 1 && Iprog.key() == Category)
9524 {
9525 for (auto pit = plist.begin(); pit != plist.end(); ++pit)
9526 {
9527 const ProgramInfo *p = *pit;
9528
9529 if (titles_seen.contains(p->GetTitle()))
9530 continue;
9531
9532 titles_seen.push_back(p->GetTitle());
9533
9534 int j = -1;
9535 for (auto *q : plist)
9536 {
9537 j++;
9538
9539 if (q->GetTitle() != p->GetTitle())
9540 continue;
9541
9542 dialog.m_buttons.push_back( { q->GetSubtitle().isEmpty() ?
9543 q->GetTitle() : q->GetSubtitle(),
9544 QString("JUMPPROG %1 %2").arg(Iprog.key()).arg(j) });
9545 }
9546 }
9547 }
9548 }
9549 SetLastProgram(lastprog);
9550 delete lastprog;
9551
9552 while (!infoList->empty())
9553 {
9554 delete infoList->back();
9555 infoList->pop_back();
9556 }
9557 delete infoList;
9558 }
9559
9560 if (!Category.isEmpty())
9561 {
9562 if (Level == 1)
9563 dialog.m_back = { Category, "DIALOG_" + ACTION_JUMPREC + "_X_0" };
9564 else if (Level == 0)
9565 {
9566 if (m_tvmJumprecBackHack.isValid())
9567 dialog.m_back = { "", m_tvmJumprecBackHack };
9568 else
9569 dialog.m_back = { ACTION_JUMPREC, "DIALOG_MENU_" + ACTION_JUMPREC +"_0" };
9570 }
9571 }
9572
9573 emit ChangeOSDDialog(dialog);
9574}
9575
9577{
9578 bool recorded = (ProgInfo.GetFilesize() > 0);
9579 QString table = recorded ? "recordedcredits" : "credits";
9580
9581 m_actors.clear();
9582 m_guest_stars.clear();
9583 m_guests.clear();
9584
9586 query.prepare(QString("SELECT role, people.name,"
9587 " roles.name, people.person FROM %1"
9588 " AS credits"
9589 " LEFT JOIN people ON"
9590 " credits.person = people.person"
9591 " LEFT JOIN roles ON"
9592 " credits.roleid = roles.roleid"
9593 " WHERE credits.chanid = :CHANID"
9594 " AND credits.starttime = :STARTTIME"
9595 " AND role IN ('guest','actor','guest_star')"
9596 " ORDER BY role, priority;").arg(table));
9597
9598 query.bindValue(":CHANID", ProgInfo.GetChanID());
9599 query.bindValue(":STARTTIME", ProgInfo.GetScheduledStartTime());
9600
9601 if (query.exec() && query.size() > 0)
9602 {
9603 QStringList plist;
9604 QString role;
9605 QString pname;
9606 QString character;
9607
9608 while(query.next())
9609 {
9610 role = query.value(0).toString();
9611 /* The people.name, roles.name columns uses utf8_bin collation.
9612 * Qt-MySQL drivers use QVariant::ByteArray for string-type
9613 * MySQL fields marked with the BINARY attribute (those using a
9614 * *_bin collation) and QVariant::String for all others.
9615 * Since QVariant::toString() uses QString::fromAscii()
9616 * (through QVariant::convert()) when the QVariant's type is
9617 * QVariant::ByteArray, we have to use QString::fromUtf8()
9618 * explicitly to prevent corrupting characters.
9619 * The following code should be changed to use the simpler
9620 * toString() approach, as above, if we do a DB update to
9621 * coalesce the people.name values that differ only in case and
9622 * change the collation to utf8_general_ci, to match the
9623 * majority of other columns, or we'll have the same problem in
9624 * reverse.
9625 */
9626 int pid = query.value(3).toInt();
9627 pname = QString::fromUtf8(query.value(1)
9628 .toByteArray().constData()) +
9629 "|" + QString::number(pid);
9630 character = QString::fromUtf8(query.value(2)
9631 .toByteArray().constData());
9632
9633 if (role == "actor")
9634 m_actors.append(qMakePair(pname, character));
9635 else if (role == "guest_star")
9636 m_guest_stars.append(qMakePair(pname, character));
9637 else if (role == "guest")
9638 m_guests.append(qMakePair(pname, character));
9639 }
9640 }
9641
9642}
9643
9645 const QVector<string_pair> & people)
9646{
9647 for (const auto & [actor, role] : std::as_const(people))
9648 {
9649 if (role.isEmpty())
9650 {
9651 dialog.m_buttons.push_back( {actor.split('|')[0],
9652 QString("JUMPCAST|%1").arg(actor), true} );
9653 }
9654 else
9655 {
9656 dialog.m_buttons.push_back( {QString("%1 as %2")
9657 .arg(actor.split('|')[0], role),
9658 QString("JUMPCAST|%1").arg(actor), true} );
9659 }
9660 }
9661}
9662
9664{
9665 MythOSDDialogData dialog { "osd_cast", tr("Cast") };
9667
9671
9672 emit ChangeOSDDialog(dialog);
9673}
9674
9675void TV::FillOSDMenuActorShows(const QString & actor, int person_id,
9676 const QString & category)
9677{
9678 MythOSDDialogData dialog { actor, actor };
9679
9680 if (category.isEmpty())
9681 {
9682 dialog.m_buttons.push_back( {"Recorded",
9683 QString("JUMPCAST|%1|%2|Recorded").arg(actor).arg(person_id) } );
9684 dialog.m_buttons.push_back( {"Upcoming",
9685 QString("JUMPCAST|%1|%2|Upcoming").arg(actor).arg(person_id) } );
9686 emit ChangeOSDDialog(dialog);
9687 return;
9688 }
9689
9690 if (category == "Upcoming")
9691 {
9693 return;
9694 }
9695
9696 /*
9697 JUMPCAST|Amanda Burton|133897|Recorded
9698 JUMPCAST|Amanda Burton|133897|Upcoming
9699 */
9700 if (!m_progLists.contains(actor))
9701 {
9702 QString table = "recordedcredits";
9704 query.prepare(QString("SELECT chanid, starttime from %1"
9705 " where person = :PERSON"
9706 " ORDER BY starttime;").arg(table));
9707 query.bindValue(":PERSON", person_id);
9708
9709 QDateTime starttime;
9710 if (query.exec() && query.size() > 0)
9711 {
9712 while(query.next())
9713 {
9714 int chanid = query.value(0).toInt();
9715 starttime = MythDate::fromString(query.value(1).toString());
9716 auto *pi = new ProgramInfo(chanid, starttime.toUTC());
9717 if (!pi->GetTitle().isEmpty() &&
9718 pi->GetRecordingGroup() != "LiveTV" &&
9719 pi->GetRecordingGroup() != "Deleted")
9720 m_progLists[actor].push_back(pi);
9721 }
9722
9723 std::stable_sort(m_progLists[actor].begin(),
9724 m_progLists[actor].end(), comp_title);
9725 }
9726 }
9727
9728 QString show;
9729 int idx = -1;
9730 for (auto & pi : m_progLists[actor])
9731 {
9732 show = pi->GetTitle();
9733 if (show.isEmpty())
9734 continue;
9735 if (!pi->GetSubtitle().isEmpty())
9736 {
9737 show += QString(" %1x%2 %3").arg(pi->GetSeason())
9738 .arg(pi->GetEpisode())
9739 .arg(pi->GetSubtitle());
9740 }
9741
9742 dialog.m_buttons.push_back( {show,
9743 QString("JUMPPROG %1 %2").arg(actor).arg(++idx) });
9744 }
9745 emit ChangeOSDDialog(dialog);
9746}
9747
9749{
9750 QString message;
9751 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
9752 if (m_player)
9753 {
9755 message = ScanTypeToUserString(Scan == kScan_Detect ? kScan_Detect :
9757 }
9758 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
9759
9760 if (!message.isEmpty())
9761 emit ChangeOSDMessage(message);
9762}
9763
9765{
9766 QString desc;
9767
9768 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
9769
9771 {
9773 desc = tr("Auto-Expire OFF");
9774 }
9775 else
9776 {
9778 desc = tr("Auto-Expire ON");
9779 }
9780
9781 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
9782
9783 if (!desc.isEmpty())
9785}
9786
9788{
9789 QString desc;
9790
9791 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
9792 if (m_player)
9793 {
9796 }
9797 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
9798
9799 if (!desc.isEmpty())
9801}
9802
9803void TV::SetManualZoom(bool ZoomON, const QString& Desc)
9804{
9805 m_zoomMode = ZoomON;
9806 if (ZoomON)
9807 ClearOSD();
9808 if (!Desc.isEmpty())
9810}
9811
9812bool TV::HandleJumpToProgramAction(const QStringList &Actions)
9813{
9814 TVState state = GetState();
9815 if (IsActionable({ ACTION_JUMPPREV, "PREVCHAN" }, Actions) &&
9816 !StateIsLiveTV(state))
9817 {
9818 PrepareToExitPlayer(__LINE__);
9819 m_jumpToProgram = true;
9820 SetExitPlayer(true, true);
9821 return true;
9822 }
9823
9824 for (const auto& action : std::as_const(Actions))
9825 {
9826 if (!action.startsWith("JUMPPROG"))
9827 continue;
9828
9829 bool ok = false;
9830 QString key = action.section(" ",1,-2);
9831 uint index = action.section(" ",-1,-1).toUInt(&ok);
9832 ProgramInfo* proginfo = nullptr;
9833
9834 if (ok)
9835 {
9836 QMutexLocker locker(&m_progListsLock);
9837 auto pit = m_progLists.find(key);
9838 if (pit != m_progLists.end())
9839 {
9840 const ProgramInfo* tmp = (*pit)[index];
9841 if (tmp)
9842 proginfo = new ProgramInfo(*tmp);
9843 }
9844 }
9845
9846 if (!proginfo)
9847 {
9848 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to locate jump to program '%1' @ %2")
9849 .arg(key, action.section(" ",-1,-1)));
9850 return true;
9851 }
9852
9854
9855 delete proginfo;
9856 return true;
9857 }
9858
9859 if (!IsActionable(ACTION_JUMPREC, Actions))
9860 return false;
9861
9862 if (m_dbJumpPreferOsd && (StateIsPlaying(state) || StateIsLiveTV(state)))
9863 {
9864 // TODO I'm not sure this really needs to be asyncronous
9865 auto Jump = [&]()
9866 {
9870 };
9871 QTimer::singleShot(0, this, Jump);
9872 }
9873 else if (RunPlaybackBoxPtr)
9874 {
9876 }
9877 else
9878 {
9879 LOG(VB_GENERAL, LOG_ERR, "Failed to open jump to program GUI");
9880 }
9881
9882 return true;
9883}
9884
9885void TV::ToggleSleepTimer(const QString& Time)
9886{
9887 std::chrono::minutes mins { 0min };
9888
9889 if (Time == ACTION_TOGGLESLEEP + "ON")
9890 {
9891 if (m_sleepTimerId)
9892 {
9894 m_sleepTimerId = 0;
9895 }
9896 else
9897 {
9898 m_sleepTimerTimeout = mins = 60min;
9900 }
9901 }
9902 else
9903 {
9904 if (m_sleepTimerId)
9905 {
9907 m_sleepTimerId = 0;
9908 }
9909
9910 if (Time.length() > 11)
9911 {
9912 bool intRead = false;
9913 mins = std::chrono::minutes(Time.right(Time.length() - 11).toUInt(&intRead));
9914
9915 if (intRead)
9916 {
9917 // catch 120 -> 240 mins
9918 if (mins < 30min)
9919 {
9920 mins *= 10;
9921 }
9922 }
9923 else
9924 {
9925 mins = 0min;
9926 LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid time " + Time);
9927 }
9928 }
9929 else
9930 {
9931 LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid time string " + Time);
9932 }
9933
9934 if (mins > 0min)
9935 {
9936 m_sleepTimerTimeout = mins;
9938 }
9939 }
9940
9941 QString out;
9942 if (mins != 0min)
9943 out = tr("Sleep") + " " + QString::number(mins.count());
9944 else
9945 out = tr("Sleep") + " " + s_sleepTimes[0].dispString;
9946 emit ChangeOSDMessage(out);
9947}
9948
9950{
9951 QString errorText;
9952
9953 switch (MsgType)
9954 {
9955 case kNoRecorders:
9956 errorText = tr("MythTV is already using all available "
9957 "inputs for the channel you selected. "
9958 "If you want to watch an in-progress recording, "
9959 "select one from the playback menu. If you "
9960 "want to watch Live TV, cancel one of the "
9961 "in-progress recordings from the delete "
9962 "menu.");
9963 break;
9964 case kNoCurrRec:
9965 errorText = tr("Error: MythTV is using all inputs, "
9966 "but there are no active recordings?");
9967 break;
9968 case kNoTuners:
9969 errorText = tr("MythTV has no capture cards defined. "
9970 "Please run the mythtv-setup program.");
9971 break;
9972 }
9973
9974 emit ChangeOSDDialog({ OSD_DLG_INFO, errorText, 0ms, {{ tr("OK"), "DIALOG_INFO_X_X" }}});
9975}
9976
9981{
9982 m_lockTimerOn = false;
9983
9984 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
9986 {
9991 }
9992 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
9993
9994 // XXX: Get rid of this?
9996
9999
10000 m_lockTimerOn = false;
10001
10002 QString input = m_playerContext.m_recorder->GetInput();
10004
10005 if (timeout < 0xffffffff)
10006 {
10007 m_lockTimer.start();
10008 m_lockTimerOn = true;
10009 }
10010
10011 SetSpeedChangeTimer(0ms, __LINE__);
10012}
10013
10017void TV::UnpauseLiveTV(bool Quietly)
10018{
10020 {
10023 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
10024 if (m_player)
10025 m_player->Play(m_playerContext.m_tsNormal, true, false);
10026 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
10029 SetSpeedChangeTimer(0ms, __LINE__);
10030 }
10031
10032 ITVRestart(true);
10033
10034 if (m_playerContext.HasPlayer() && !Quietly)
10035 {
10037 UpdateLCD();
10039 }
10040}
10041
10045void TV::ITVRestart(bool IsLive)
10046{
10047 int chanid = -1;
10048 int sourceid = -1;
10049
10050 if (ContextIsPaused(__FILE__, __LINE__))
10051 return;
10052
10053 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
10055 {
10056 chanid = static_cast<int>(m_playerContext.m_playingInfo->GetChanID());
10057 sourceid = static_cast<int>(ChannelUtil::GetSourceIDForChannel(static_cast<uint>(chanid)));
10058 }
10059 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10060
10061 emit RestartITV(static_cast<uint>(chanid), static_cast<uint>(sourceid), IsLive);
10062}
10063
10065{
10068 else if (GetNumChapters() > 0)
10069 DoJumpChapter(9999);
10070 else
10071 DoSeek(m_playerContext.m_jumptime, tr("Jump Ahead"), /*timeIsOffset*/true, /*honorCutlist*/true);
10072}
10073
10075{
10076 DoSeek(m_playerContext.m_fftime, tr("Skip Ahead"), /*timeIsOffset*/true, /*honorCutlist*/true);
10077}
10078
10080{
10082 DVDJumpBack();
10083 else if (GetNumChapters() > 0)
10084 DoJumpChapter(-1);
10085 else
10086 DoSeek(-m_playerContext.m_jumptime, tr("Jump Back"), /*timeIsOffset*/true, /*honorCutlist*/true);
10087}
10088
10090{
10091 DoSeek(-m_playerContext.m_rewtime, tr("Jump Back"), /*timeIsOffset*/true, /*honorCutlist*/true);
10092}
10093
10094/* \fn TV::DVDJumpBack(PlayerContext*)
10095 \brief jump to the previous dvd title or chapter
10096*/
10098{
10099 auto *dvd = dynamic_cast<MythDVDBuffer*>(m_playerContext.m_buffer);
10100 if (!m_playerContext.HasPlayer() || !dvd)
10101 return;
10102
10104 {
10105 UpdateOSDSeekMessage(tr("Skip Back Not Allowed"), kOSDTimeout_Med);
10106 }
10107 else if (!dvd->StartOfTitle())
10108 {
10109 DoJumpChapter(-1);
10110 }
10111 else
10112 {
10113 std::chrono::seconds titleLength = dvd->GetTotalTimeOfTitle();
10114 std::chrono::seconds chapterLength = dvd->GetChapterLength();
10115 if ((titleLength == chapterLength) && chapterLength > 5min)
10116 {
10117 DoSeek(-m_playerContext.m_jumptime, tr("Jump Back"), /*timeIsOffset*/true, /*honorCutlist*/true);
10118 }
10119 else
10120 {
10121 emit GoToDVDProgram(false);
10122 UpdateOSDSeekMessage(tr("Previous Title"), kOSDTimeout_Med);
10123 }
10124 }
10125}
10126
10127/* \fn TV::DVDJumpForward(PlayerContext*)
10128 * \brief jump to the next dvd title or chapter
10129 */
10131{
10132 auto *dvd = dynamic_cast<MythDVDBuffer*>(m_playerContext.m_buffer);
10133 if (!m_playerContext.HasPlayer() || !dvd)
10134 return;
10135
10136 bool in_still = dvd->IsInStillFrame();
10137 bool in_menu = dvd->IsInMenu();
10138 if (in_still && !dvd->NumMenuButtons())
10139 {
10140 dvd->SkipStillFrame();
10141 UpdateOSDSeekMessage(tr("Skip Still Frame"), kOSDTimeout_Med);
10142 }
10143 else if (!dvd->EndOfTitle() && !in_still && !in_menu)
10144 {
10145 DoJumpChapter(9999);
10146 }
10147 else if (!in_still && !in_menu)
10148 {
10149 std::chrono::seconds titleLength = dvd->GetTotalTimeOfTitle();
10150 std::chrono::seconds chapterLength = dvd->GetChapterLength();
10151 std::chrono::seconds currentTime = dvd->GetCurrentTime();
10152 if ((titleLength == chapterLength) && (chapterLength > 5min) &&
10153 (currentTime < (chapterLength - (duration_cast<std::chrono::seconds>(m_playerContext.m_jumptime)))))
10154 {
10155 DoSeek(m_playerContext.m_jumptime, tr("Jump Ahead"), /*timeIsOffset*/true, /*honorCutlist*/true);
10156 }
10157 else
10158 {
10159 emit GoToDVDProgram(true);
10160 UpdateOSDSeekMessage(tr("Next Title"), kOSDTimeout_Med);
10161 }
10162 }
10163}
10164
10165/* \fn TV::IsBookmarkAllowed(const PlayerContext*) const
10166 * \brief Returns true if bookmarks are allowed for the current player.
10167 */
10169{
10170 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
10171
10172 // Allow bookmark of "Record current LiveTV program"
10175 {
10176 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10177 return false;
10178 }
10179
10181 {
10182 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10183 return false;
10184 }
10185
10186 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10187
10189}
10190
10191/* \fn TV::IsDeleteAllowed() const
10192 * \brief Returns true if the delete menu option should be offered.
10193 */
10195{
10196 bool allowed = false;
10197
10198 if (!StateIsLiveTV(GetState()))
10199 {
10200 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
10202 allowed = curProgram && curProgram->QueryIsDeleteCandidate(true);
10203 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10204 }
10205
10206 return allowed;
10207}
10208
10210{
10211 ClearOSD();
10212
10213 if (!ContextIsPaused(__FILE__, __LINE__))
10214 DoTogglePause(false);
10215
10216 QString videotype;
10217
10218 if (StateIsLiveTV(GetState()))
10219 videotype = tr("Live TV");
10221 videotype = tr("this DVD");
10222
10223 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
10224 if (videotype.isEmpty() && m_playerContext.m_playingInfo->IsVideo())
10225 videotype = tr("this Video");
10226 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10227
10228 if (videotype.isEmpty())
10229 videotype = tr("this recording");
10230
10231 MythOSDDialogData dialog { OSD_DLG_VIDEOEXIT, tr("You are exiting %1").arg(videotype) };
10232
10233 dialog.m_buttons.push_back({tr("Exit %1").arg(videotype), ACTION_STOP});
10234
10235 dialog.m_buttons.push_back({tr("Exit Without Saving"), "DIALOG_VIDEOEXIT_CLEARLASTPLAYEDPOSITION_0"});
10236
10237 if (IsDeleteAllowed())
10238 dialog.m_buttons.push_back({tr("Delete this recording"), "DIALOG_VIDEOEXIT_CONFIRMDELETE_0"});
10239
10240 dialog.m_buttons.push_back({tr("Keep watching"), "DIALOG_VIDEOEXIT_KEEPWATCHING_0"});
10241 dialog.m_back = { "", "DIALOG_VIDEOEXIT_KEEPWATCHING_0", true };
10242 emit ChangeOSDDialog(dialog);
10243
10244 if (m_videoExitDialogTimerId)
10245 KillTimer(m_videoExitDialogTimerId);
10246 m_videoExitDialogTimerId = StartTimer(kVideoExitDialogTimeout, __LINE__);
10247}
10248
10249void TV::ShowOSDPromptDeleteRecording(const QString& Title, bool Force)
10250{
10251 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
10252
10254 {
10255 // this should only occur when the cat walks on the keyboard.
10256 LOG(VB_GENERAL, LOG_ERR, "It is unsafe to delete at the moment");
10257 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10258 return;
10259 }
10260
10261 bool paused = ContextIsPaused(__FILE__, __LINE__);
10263 {
10264 LOG(VB_GENERAL, LOG_ERR, "This program cannot be deleted at this time.");
10266 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10267
10268 OSD *osd = GetOSDL();
10269 if (osd && !osd->DialogVisible())
10270 {
10271 QString message = tr("Cannot delete program ") + QString("%1 ").arg(pginfo.GetTitle());
10272
10273 if (!pginfo.GetSubtitle().isEmpty())
10274 message += QString("\"%1\" ").arg(pginfo.GetSubtitle());
10275
10276 if (!pginfo.IsRecording())
10277 {
10278 message += tr("because it is not a recording.");
10279 }
10280 else
10281 {
10282 message += tr("because it is in use by");
10283 QStringList byWho;
10284 pginfo.QueryIsInUse(byWho);
10285 for (int i = 0; (i + 2) < byWho.size(); i += 3)
10286 {
10287 if (byWho[i + 1] == gCoreContext->GetHostName() && byWho[i].contains(kPlayerInUseID))
10288 continue;
10289 if (byWho[i].contains(kRecorderInUseID))
10290 continue;
10291 message += " " + byWho[i+2];
10292 }
10293 }
10294 emit ChangeOSDDialog({OSD_DLG_DELETE, message, 0ms,
10295 {{ tr("OK"), "DIALOG_DELETE_OK_0" }},
10296 { "", "DIALOG_DELETE_OK_0", true }});
10297 }
10298 ReturnOSDLock();
10299 // If the delete prompt is to be displayed at the end of a
10300 // recording that ends in a final cut region, it will get into
10301 // a loop of popping up the OK button while the cut region
10302 // plays. Avoid this.
10303 if (m_player->IsNearEnd() && !paused)
10304 SetExitPlayer(true, true);
10305
10306 return;
10307 }
10308 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10309
10310 ClearOSD();
10311
10312 if (!paused)
10313 DoTogglePause(false);
10314
10315 InfoMap infoMap;
10317 if (m_player)
10318 m_player->GetCodecDescription(infoMap);
10319 QString message = QString("%1\n%2\n%3")
10320 .arg(Title, infoMap["title"], infoMap["timedate"]);
10321
10322 OSD *osd = GetOSDL();
10323 if (osd && (!osd->DialogVisible() || Force))
10324 {
10325 MythOSDDialogData dialog { OSD_DLG_VIDEOEXIT, message };
10326 if (Title == "End Of Recording")
10327 {
10328 dialog.m_buttons.push_back({tr("Delete it, but allow it to re-record"), "DIALOG_VIDEOEXIT_DELETEANDRERECORD_0"});
10329 dialog.m_buttons.push_back({tr("Delete it"), "DIALOG_VIDEOEXIT_JUSTDELETE_0"});
10330 dialog.m_buttons.push_back({tr("Save it so I can watch it again"), ACTION_STOP, false, true});
10331 }
10332 else
10333 {
10334 dialog.m_buttons.push_back({tr("Yes, and allow re-record"), "DIALOG_VIDEOEXIT_DELETEANDRERECORD_0"});
10335 dialog.m_buttons.push_back({tr("Yes, delete it"), "DIALOG_VIDEOEXIT_JUSTDELETE_0"});
10336 dialog.m_buttons.push_back({tr("No, keep it"), ACTION_STOP, false, true});
10337 if (!paused)
10338 dialog.m_back = { "", "DIALOG_PLAY_0_0", true };
10339 }
10340
10341 emit ChangeOSDDialog(dialog);
10342
10346 }
10347 ReturnOSDLock();
10348}
10349
10350bool TV::HandleOSDVideoExit(const QString& Action)
10351{
10353 return false;
10354
10355 bool hide = true;
10356 bool delete_ok = IsDeleteAllowed();
10357
10358 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
10359 bool near_end = m_player && m_player->IsNearEnd();
10360 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
10361
10362 if (Action == "DELETEANDRERECORD" && delete_ok)
10363 {
10364 m_allowRerecord = true;
10365 m_requestDelete = true;
10366 PrepareToExitPlayer(__LINE__);
10367 SetExitPlayer(true, true);
10368 }
10369 else if (Action == "JUSTDELETE" && delete_ok)
10370 {
10371 m_requestDelete = true;
10372 PrepareToExitPlayer(__LINE__);
10373 SetExitPlayer(true, true);
10374 }
10375 else if (Action == "CONFIRMDELETE")
10376 {
10377 hide = false;
10378 ShowOSDPromptDeleteRecording(tr("Are you sure you want to delete:"), true);
10379 }
10380 else if (Action == "KEEPWATCHING" && !near_end)
10381 {
10382 DoTogglePause(true);
10383 }
10384 else if (Action == "CLEARLASTPLAYEDPOSITION")
10385 {
10386 m_clearPosOnExit = true;
10387 PrepareToExitPlayer(__LINE__);
10388 SetExitPlayer(true, true);
10389 }
10390
10391 return hide;
10392}
10393
10395{
10397 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
10398 bool playing = m_player && !m_player->IsPaused();
10399 // Don't bother saving lastplaypos while paused
10400 if (playing)
10401 {
10402 uint64_t framesPlayed = m_player->GetFramesPlayed();
10403 auto *savPosThread = new SavePositionThread(m_playerContext.m_playingInfo,
10404 framesPlayed);
10405 GetPosThreadPool()->start(savPosThread, "PositionSaver");
10406 }
10407 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
10409
10410 m_savePosOnExit = true;
10411}
10412
10414{
10415 QMutexLocker locker(&m_lastProgramLock);
10416
10417 delete m_lastProgram;
10418
10419 if (ProgInfo)
10421 else
10422 m_lastProgram = nullptr;
10423}
10424
10426{
10427 QMutexLocker locker(&m_lastProgramLock);
10428 if (m_lastProgram)
10429 return new ProgramInfo(*m_lastProgram);
10430 return nullptr;
10431}
10432
10434{
10435 QString ret;
10436
10438 if (StateIsPlaying(GetState()))
10439 {
10440 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
10443 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10444 }
10446 return ret;
10447}
10448
10450{
10451 if (!ProgInfo)
10452 return false;
10453
10454 bool ret = false;
10458 return ret;
10459}
10460
10461bool TV::ContextIsPaused(const char *File, int Location)
10462{
10463 bool paused = false;
10464 m_playerContext.LockDeletePlayer(File, Location);
10465 if (m_player)
10466 paused = m_player->IsPaused();
10467 m_playerContext.UnlockDeletePlayer(File, Location);
10468 return paused;
10469}
10470
10472{
10473 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
10474 if (m_player)
10475 {
10476 m_player->LockOSD();
10477 OSD *osd = m_player->GetOSD();
10478 if (!osd)
10479 {
10481 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
10482 }
10483 return osd;
10484 }
10485 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
10486 return nullptr;
10487}
10488
10490{
10491 if (m_player)
10493 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
10494}
10495
10497{
10498 m_playerLock.lockForWrite();
10499}
10500
10502{
10503 m_playerLock.lockForRead();
10504}
10505
10507{
10508 m_playerLock.unlock();
10509}
10510
10511void TV::onApplicationStateChange(Qt::ApplicationState State)
10512{
10513 switch (State)
10514 {
10515 case Qt::ApplicationState::ApplicationSuspended:
10516 {
10517 LOG(VB_GENERAL, LOG_NOTICE, "Exiting playback on app suspecnd");
10518 StopPlayback();
10519 break;
10520 }
10521 default:
10522 break;
10523 }
10524}
10525
10527{
10528 return m_posThreadPool;
10529}
10530
10532{
10533 if (m_progInfo)
10534 {
10535 try
10536 {
10538 }
10539 catch (...)
10540 {
10541 LOG(VB_GENERAL, LOG_ERR, "An exception occurred");
10542 }
10543 }
10544}
#define Clear(a)
std::vector< ChannelInfo > ChannelInfoList
Definition: channelinfo.h:131
An action (for this plugin) consists of a description, and a set of key sequences.
Definition: action.h:41
iterator begin(void)
iterator end(void)
size_t size(void) const
QString m_chanNum
QString m_startTime
static bool IsTunerShared(uint inputidA, uint inputidB)
Definition: cardutil.cpp:242
static QString GetStartChannel(uint inputid)
Definition: cardutil.cpp:1792
static std::vector< uint > GetInputGroups(uint inputid)
Definition: cardutil.cpp:2188
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1948
static bool SetStartChannel(uint inputid, const QString &channum)
Definition: cardutil.cpp:1680
static QString GetDisplayName(uint inputid)
Definition: cardutil.cpp:1876
static ChannelGroupList GetChannelGroups(bool includeEmpty=true)
static QStringList GetValidRecorderList(uint chanid, const QString &channum)
Returns list of the recorders that have chanid or channum in their sources.
static uint GetMplexID(uint sourceid, const QString &channum)
static int GetChanID(int db_mplexid, int service_transport_id, int major_channel, int minor_channel, int program_number)
static QString GetIcon(uint chanid)
static void SortChannels(ChannelInfoList &list, const QString &order, bool eliminate_duplicates=false)
static uint GetNextChannel(const ChannelInfoList &sorted, uint old_chanid, uint mplexid_restriction, uint chanid_restriction, ChannelChangeDirection direction, bool skip_non_visible=true, bool skip_same_channum_and_callsign=false, bool skip_other_sources=false)
static QString GetChanNum(int chan_id)
Returns the channel-number string of the given channel.
static ChannelInfoList GetChannels(uint sourceid, bool visible_only, const QString &group_by=QString(), uint channel_groupid=0)
Definition: channelutil.h:251
static uint GetSourceIDForChannel(uint chanid)
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:41
static const Type kEventType
Definition: mythdialogbox.h:56
Event details.
Definition: zmdefines.h:28
uint m_chanId
chanid restriction if applicable
Definition: inputinfo.h:51
uint m_inputId
unique key in DB for this input
Definition: inputinfo.h:49
uint m_mplexId
mplexid restriction if applicable
Definition: inputinfo.h:50
static bool ChangeJobCmds(int jobID, int newCmds)
Definition: jobqueue.cpp:919
static bool QueueJob(int jobType, uint chanid, const QDateTime &recstartts, const QString &args="", const QString &comment="", QString host="", int flags=0, int status=JOB_QUEUED, QDateTime schedruntime=QDateTime())
Definition: jobqueue.cpp:508
static bool IsJobQueuedOrRunning(int jobType, uint chanid, const QDateTime &recstartts)
Definition: jobqueue.cpp:1098
Definition: lcddevice.h:170
static LCD * Get(void)
Definition: lcddevice.cpp:69
int getLCDWidth(void) const
Definition: lcddevice.h:293
void switchToChannel(const QString &channum="", const QString &title="", const QString &subtitle="")
Definition: lcddevice.cpp:577
void setChannelProgress(const QString &time, float value)
Definition: lcddevice.cpp:463
bool HasNext(void) const
QString GetID(void) const
Definition: livetvchain.h:54
void JumpTo(int num, std::chrono::seconds pos)
QString GetChannelName(int pos=-1) const
void SetProgram(const ProgramInfo &pginfo)
QString GetInputType(int pos=-1) const
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
QVariant value(int i) const
Definition: mythdbcon.h:204
int size(void) const
Definition: mythdbcon.h:214
bool isActive(void) const
Definition: mythdbcon.h:215
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
void start(QRunnable *runnable, const QString &debugName, int priority=0)
void waitForDone(void)
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
void exit(int retcode=0)
Use this to exit from the thread if you are using a Qt event loop.
Definition: mthread.cpp:278
std::chrono::milliseconds m_audioOffset
MuteState m_muteState
bool IsHDMVNavigation(void) const
Dialog asking for user confirmation.
void emitTVPlaybackStarted(void)
void UnregisterForPlayback(QObject *sender)
Unregister sender from being called when TVPlaybackAboutToStart signal is emitted.
void emitTVPlaybackSought(qint64 position)
void emitTVPlaybackStopped(void)
QString GetHostName(void)
void TVInWantingPlayback(bool b)
Let the TV class tell us if we was interrupted following a call to WantingPlayback().
void SaveSetting(const QString &key, int newValue)
void RegisterForPlayback(QObject *sender, PlaybackStartCb method)
Register sender for TVPlaybackAboutToStart signal.
void emitTVPlaybackUnpaused(void)
void SendSystemEvent(const QString &msg)
void emitTVPlaybackPaused(void)
void emitTVPlaybackAborted(void)
void WantingPlayback(QObject *sender)
All the objects that have registered using MythCoreContext::RegisterForPlayback but sender will be ca...
void dispatch(const MythEvent &event)
void SendMessage(const QString &message)
void emitTVPlaybackPlaying(void)
int GetNumSetting(const QString &key, int defaultval=0)
bool GetBoolSetting(const QString &key, bool defaultval=false)
void GetResolutionSetting(const QString &type, int &width, int &height, double &forced_aspect, double &refresh_rate, int index=-1)
static MythDB * getMythDB()
Definition: mythdb.cpp:30
int NumPartsInTitle(void) const
bool GoBack(void)
Attempts to back-up by trying to jump to the 'Go up' PGC, the root menu or the title menu in turn.
std::chrono::seconds GetTotalTimeOfTitle(void) const
get the total time of the title in seconds 90000 ticks = 1 sec
bool IsInStillFrame(void) const override
bool GetNameAndSerialNum(QString &Name, QString &SerialNumber) override
Get the dvd title and serial num.
void GetPartAndTitle(int &Part, int &Title) const
T dequeue()
Removes item from front of list and returns a copy. O(1).
Definition: mythdeque.h:31
void enqueue(const T &d)
Adds item to the back of the list. O(1).
Definition: mythdeque.h:41
QSize GetGUIResolution()
bool SwitchToGUI(bool Wait=false)
Switches to the GUI resolution.
virtual bool UsingVideoModes()
Definition: mythdisplay.h:30
QRect GetScreenBounds()
bool NextModeIsLarger(QSize Size)
Check whether the next mode is larger in size than the current mode.
uint64_t m_totalFrames
uint64_t m_previousCut
This class is used as a container for messages.
Definition: mythevent.h:17
const QString & Message() const
Definition: mythevent.h:65
static const Type kUpdateTvProgressEventType
Definition: mythevent.h:81
static const Type kUpdateBrowseInfoEventType
Definition: mythevent.h:88
static const Type kMythEventMessage
Definition: mythevent.h:79
static const Type kMythUserMessage
Definition: mythevent.h:80
A custom event that represents a mouse gesture.
Definition: mythgesture.h:40
static const Type kEventType
Definition: mythgesture.h:91
QWidget * GetPaintWindow()
void ClearKeyContext(const QString &Context)
static void DisableScreensaver()
QString GetActionText(const QString &Context, const QString &Action) const
void PauseIdleTimer(bool Pause)
Pause the idle timeout timer.
MythDisplay * GetDisplay()
MythScreenStack * GetMainStack()
static bool ScreenShot(int Width=0, int Height=0, QString Filename="")
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
bool IsExitingToMain() const
void MoveResize(QRect &Geometry)
MythScreenStack * GetStack(const QString &Stackname)
static QString GetKey(const QString &Context, const QString &Action)
bool KeyLongPressFilter(QEvent **Event, QScopedPointer< QEvent > &NewEvent)
static void RestoreScreensaver()
void SetLiveMode(LiveTVChain *Chain)
Assigns a LiveTVChain to this RingBuffer.
void IgnoreLiveEOF(bool Ignore)
Tells RingBuffer whether to ignore the end-of-file.
virtual void IgnoreWaitStates(bool)
QString GetLastError(void) const
virtual bool HandleAction(const QStringList &, mpeg::chrono::pts)
virtual bool IsBookmarkAllowed(void)
void Pause(void)
Pauses the read-ahead thread.
void WaitForPause(void)
Waits for Pause(void) to take effect.
bool IsDVD(void) const
virtual bool IsOpen(void) const =0
static MythMediaBuffer * Create(const QString &Filename, bool Write, bool UseReadAhead=true, std::chrono::milliseconds Timeout=kDefaultOpenTimeout, bool StreamOnly=false)
Creates a RingBuffer instance.
virtual bool IsSeekingAllowed(void)
const MythDVDBuffer * DVD(void) const
virtual bool IsInDiscMenuOrStillFrame(void) const
const MythBDBuffer * BD(void) const
bool IsBD(void) const
static constexpr std::chrono::milliseconds kLiveTVOpenTimeout
QString GetFilename(void) const
MythMediaStatus getStatus() const
Definition: mythmedia.h:70
const QString & getDevicePath() const
Definition: mythmedia.h:61
static const Type kEventType
Definition: mythmedia.h:193
bool HasWindow(const QString &Window)
static MythNotificationCenter * GetInstance(void)
returns the MythNotificationCenter singleton
bool Queue(const MythNotification &notification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
std::vector< MythOSDDialogButton > m_buttons
Definition: osd.h:87
void addListener(QObject *listener)
Add a listener to the observable.
void removeListener(QObject *listener)
Remove a listener to the observable.
bool IsInMenu(void) const override
QStringList GetTracks(uint Type)
bool HandleProgramEditorActions(const QStringList &Actions)
virtual void UpdateSliderInfo(osdInfo &Info, bool PaddedFields=false)
bool StartPlaying()
virtual bool VideoLoop()
void SetWatched(bool ForceWatched=false)
Determines if the recording should be considered watched.
void GetCodecDescription(InfoMap &Map)
virtual void EventLoop()
virtual bool Rewind(float seconds)
Definition: mythplayer.cpp:869
virtual int GetNumAngles(void) const
Definition: mythplayer.h:224
uint64_t TranslatePositionAbsToRel(uint64_t position) const
Definition: mythplayer.h:266
CommSkipMode GetAutoCommercialSkip(void)
Definition: mythplayer.h:283
bool PauseDecoder(void)
Definition: mythplayer.cpp:968
uint64_t TranslatePositionMsToFrame(std::chrono::milliseconds position, bool use_cutlist) const
Definition: mythplayer.h:257
virtual QString GetTitleName(int) const
Definition: mythplayer.h:221
virtual int GetNumTitles(void) const
Definition: mythplayer.h:218
virtual QString GetAngleName(int) const
Definition: mythplayer.h:226
virtual std::chrono::seconds GetTitleDuration(int) const
Definition: mythplayer.h:220
void SetWatchingRecording(bool mode)
Definition: mythplayer.cpp:120
uint64_t TranslatePositionRelToAbs(uint64_t position) const
Definition: mythplayer.h:269
virtual bool JumpToFrame(uint64_t frame)
Definition: mythplayer.cpp:892
virtual bool FastForward(float seconds)
Definition: mythplayer.cpp:839
virtual int GetCurrentAngle(void) const
Definition: mythplayer.h:225
virtual int GetNumChapters(void)
uint64_t GetCurrentFrameCount(void) const
QString GetError(void) const
virtual int GetCurrentTitle(void) const
Definition: mythplayer.h:219
void SkipCommercials(int direction)
Definition: mythplayer.h:280
MythVideoOutput * GetVideoOutput(void)
Definition: mythplayer.h:164
bool IsPaused(void) const
Definition: mythplayer.h:151
bool IsPlaying(std::chrono::milliseconds wait_in_msec=0ms, bool wait_for=true) const
Definition: mythplayer.cpp:251
uint64_t GetFramesPlayed(void) const
Definition: mythplayer.h:144
void SetLength(std::chrono::seconds len)
Definition: mythplayer.h:116
virtual int GetCurrentChapter(void)
bool IsNearEnd(void)
Returns true iff near end of recording.
bool IsErrored(void) const
virtual uint64_t GetBookmark(void)
bool GetLimitKeyRepeat(void) const
Definition: mythplayer.h:152
bool Pause(void)
Definition: mythplayer.cpp:153
float GetPlaySpeed(void) const
Definition: mythplayer.h:138
float ComputeSecs(uint64_t position, bool use_cutlist) const
Definition: mythplayer.h:272
bool GetEditMode(void) const
Definition: mythplayer.h:315
QString GetXDS(const QString &key) const
virtual void GetChapterTimes(QList< std::chrono::seconds > &times)
virtual bool SwitchAngle(int)
Definition: mythplayer.h:172
bool GetAllowForcedSubtitles(void) const
Definition: mythplayer.h:204
void ResetErrored(void)
virtual bool SwitchTitle(int)
Definition: mythplayer.h:167
bool Play(float speed=1.0, bool normal=true, bool unpauseaudio=true)
Definition: mythplayer.cpp:186
void JumpChapter(int chapter)
Definition: mythplayer.cpp:913
void SetCommBreakMap(const frm_dir_map_t &NewMap)
void SetAutoCommercialSkip(CommSkipMode autoskip)
Definition: mythplayer.h:278
float GetFrameRate(void) const
Definition: mythplayer.h:133
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
virtual MythScreenType * GetTopScreen(void) const
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
virtual void Close()
MenuCurrentContext m_currentContext
Definition: mythtvmenu.h:70
MenuCategory m_category
Definition: mythtvmenu.h:67
bool AddButton(MythOSDDialogData *Menu, bool Active, const QString &Action, const QString &DefaultTextActive, const QString &DefaultTextInactive, bool IsMenu, const QString &TextArg) const
Definition: mythtvmenu.cpp:14
const QDomNode & m_node
Definition: mythtvmenu.h:66
const QString m_menuName
Definition: mythtvmenu.h:68
const QString m_action
Definition: mythtvmenu.h:71
const MythTVMenu & m_menu
Definition: mythtvmenu.h:65
MenuTypeId m_id
Definition: mythtvmenu.h:112
const QString & GetKeyBindingContext() const
Definition: mythtvmenu.cpp:113
static bool MatchesGroup(const QString &Name, const QString &Prefix, MenuCategory Category, QString &OutPrefix)
Definition: mythtvmenu.cpp:123
bool Show(const QDomNode &Node, const QDomNode &Selected, MythTVMenuItemDisplayer &Displayer, MythOSDDialogData *Menu, bool Visible=true) const
Definition: mythtvmenu.cpp:294
static QString GetPathFromNode(QDomNode Node)
Definition: mythtvmenu.cpp:131
QString GetName() const
Definition: mythtvmenu.cpp:93
bool LoadFromFile(MenuTypeId id, const QString &Filename, const QString &Menuname, const char *TranslationContext, const QString &KeyBindingContext, int IncludeLevel=0)
Definition: mythtvmenu.cpp:189
bool IsLoaded() const
Definition: mythtvmenu.cpp:98
QString Translate(const QString &Text) const
Definition: mythtvmenu.cpp:118
QDomElement GetRoot() const
Definition: mythtvmenu.cpp:103
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:14
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:91
bool isRunning(void) const
Returns true if start() or restart() has been called at least once since construction and since any c...
Definition: mythtimer.cpp:135
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
static void SetState(const QVariantMap &NewState)
void DeleteAllChildren(void)
Delete all child widgets.
Definition: mythuitype.cpp:222
StereoscopicMode m_stereoOverride
AspectOverrideMode m_aspectOverrideMode
AdjustFillMode m_adjustFillMode
PictureAttributeSupported m_supportedAttributes
int GetValue(PictureAttribute Attribute)
std::chrono::milliseconds m_timecode
Definition: mythframe.h:130
bool HasSoftwareFrames() const
Definition: mythvideoout.h:86
void SetScanOverride(FrameScanType Scan)
FrameScanType NextScanOverride()
FrameScanType GetScanTypeWithOverride() const
QStringList m_visualiserList
Definition: osd.h:94
void SetValues(const QString &Window, const QHash< QString, int > &Map, OSDTimeout Timeout)
Definition: osd.cpp:152
bool DialogVisible(const QString &Window=QString())
Definition: osd.cpp:699
void ResetWindow(const QString &Window)
Definition: osd.cpp:636
bool DialogHandleGesture(MythGestureEvent *Event)
Definition: osd.cpp:713
void DialogGetText(InfoMap &Map)
Definition: osd.cpp:836
bool DialogHandleKeypress(QKeyEvent *Event)
Definition: osd.cpp:706
void SetFunctionalWindow(const QString &Window, enum OSDFunctionalType Type)
Definition: osd.cpp:667
bool IsWindowVisible(const QString &Window)
Definition: osd.cpp:628
void HideWindow(const QString &Window) override
Definition: osd.cpp:675
static int GetCount(void)
Definition: playgroup.cpp:220
void SetRingBuffer(MythMediaBuffer *Buffer)
void SetPlayingInfo(const ProgramInfo *info)
assign programinfo to the context
bool IsErrored(void) const
This is set if the player encountered some irrecoverable error.
StringDeque m_prevChan
Previous channels.
QStringList m_lastSignalMsg
QString GetPlayMessage(void) const
MythPlayer * m_player
void SetPseudoLiveTV(const ProgramInfo *pi, PseudoState new_state)
PseudoState m_pseudoLiveTVState
bool ReloadTVChain(void)
MythTimer m_lastSignalMsgTime
void LockState(void) const
void LockPlayingInfo(const char *file, int line) const
TVState GetState(void) const
static constexpr std::chrono::milliseconds kSMExitTimeout
Timeout after last Signal Monitor message for ignoring OSD when exiting.
bool HasPlayer(void) const
bool IsPlayerErrored(void) const
void PushPreviousChannel(void)
most recently selected channel to the previous channel list
bool IsPlayerPlaying(void) const
std::chrono::seconds m_rewtime
int m_ffRewState
0 == normal, +1 == fast forward, -1 == rewind
TVState DequeueNextState(void)
void ForceNextStateNone(void)
Removes any pending state changes, and puts kState_None on the queue.
void UnlockDeletePlayer(const char *file, int line) const
allow player to be deleted.
void UnlockState(void) const
int m_ffRewIndex
Index into m_ffRewSpeeds for FF and Rewind speeds.
RemoteEncoder * m_recorder
void UpdateTVChain(const QStringList &data=QStringList())
bool HandlePlayerSpeedChangeFFRew(void)
bool InStateChange(void) const
TVState m_playingState
InfoMap m_lastSignalUIInfo
void SetPlayer(MythPlayer *newplayer)
int m_ffRewSpeed
Caches value of m_ffRewSpeeds[m_ffRewIndex].
bool IsPlayerChangingBuffers(void) const
void LockDeletePlayer(const char *file, int line) const
prevent MythPlayer from being deleted used to ensure player can only be deleted after osd in TV() is ...
void SetRecorder(RemoteEncoder *rec)
bool IsRecorderErrored(void) const
bool HandlePlayerSpeedChangeEOF(void)
float m_tsNormal
Time stretch speed, 1.0F for normal playback.
std::chrono::seconds m_fftime
QString GetPreviousChannel(void) const
MythMediaBuffer * m_buffer
bool GetPlayingInfoMap(InfoMap &infoMap) const
void SetInitialTVState(bool islivetv)
determine initial tv state and playgroup for the recording
void ChangeState(TVState newState)
Puts a state change on the nextState queue.
LiveTVChain * m_tvchain
std::chrono::minutes m_jumptime
QString PopPreviousChannel(void)
MythDeque< TVState > m_nextState
uint GetCardID(void) const
Definition: playercontext.h:93
bool IsSameProgram(const ProgramInfo &p) const
std::chrono::seconds m_playingLen
Initial CalculateLength()
MythTimer m_lastSignalUIInfoTime
void TeardownPlayer(void)
void UnlockPlayingInfo(const char *file, int line) const
ProgramInfo * m_pseudoLiveTVRec
void StopPlaying(void) const
ProgramInfo * m_playingInfo
Currently playing info.
Holds information on recordings and videos.
Definition: programinfo.h:68
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:373
QString GetBasename(void) const
Definition: programinfo.h:345
bool HasPathname(void) const
Definition: programinfo.h:359
static bool ExtractKey(const QString &uniquekey, uint &chanid, QDateTime &recstartts)
Extracts chanid and recstartts from a unique key generated by MakeUniqueKey().
bool QueryIsInUse(QStringList &byWho) const
Returns true if Program is in use.
bool IsVideoFile(void) const
Definition: programinfo.h:346
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
bool IsVideoDVD(void) const
Definition: programinfo.h:348
void SetIgnoreProgStart(bool ignore)
If "ignore" is true QueryProgStart() will return 0, otherwise QueryProgStart() will return the progst...
Definition: programinfo.h:570
AutoExpireType QueryAutoExpire(void) const
Returns "autoexpire" field from "recorded" table.
void SetIgnoreBookmark(bool ignore)
If "ignore" is true GetBookmark() will return 0, otherwise GetBookmark() will return the bookmark pos...
Definition: programinfo.h:563
uint GetEpisode(void) const
Definition: programinfo.h:368
bool IsVideo(void) const
Definition: programinfo.h:490
QString GetProgramID(void) const
Definition: programinfo.h:440
QString GetRecordingGroup(void) const
Definition: programinfo.h:420
void SaveAutoExpire(AutoExpireType autoExpire, bool updateDelete=false)
Set "autoexpire" field in "recorded" table to "autoExpire".
uint GetRecordingID(void) const
Definition: programinfo.h:450
bool QueryIsDeleteCandidate(bool one_playback_allowed=false) const
Returns true iff this is a recording, it is not in use (except by the recorder), and at most one play...
int64_t QueryTotalFrames(void) const
If present in recording this loads total frames of the main video stream from database's stream marku...
bool IsFileReadable(void)
Attempts to ascertain if the main file for this ProgramInfo is readable.
void SetIgnoreLastPlayPos(bool ignore)
If "ignore" is true QueryLastPlayPos() will return 0, otherwise QueryLastPlayPos() will return the la...
Definition: programinfo.h:578
QString GetHostname(void) const
Definition: programinfo.h:422
bool IsRecording(void) const
Definition: programinfo.h:491
uint GetSourceID(void) const
Definition: programinfo.h:466
QString GetPlaybackGroup(void) const
Definition: programinfo.h:421
QString GetTitle(void) const
Definition: programinfo.h:362
bool QueryIsEditing(void) const
Queries "recorded" table for its "editing" field and returns true if it is set to true.
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:405
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:391
void SaveLastPlayPos(uint64_t frame)
TODO Move to RecordingInfo.
QString GetChanNum(void) const
This is the channel "number", in the form 1, 1_2, 1-2, 1#1, etc.
Definition: programinfo.h:377
void SaveTotalFrames(int64_t frames)
Store the Total Frames at frame 0 in the recordedmarkup table.
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:340
QString GetSortTitle(void) const
Definition: programinfo.h:363
bool IsVideoBD(void) const
Definition: programinfo.h:350
virtual void ToMap(InfoMap &progMap, bool showrerecord=false, uint star_range=10, uint date_format=0) const
Converts ProgramInfo into QString QHash containing each field in ProgramInfo converted into localized...
QString GetPathname(void) const
Definition: programinfo.h:344
QDate GetOriginalAirDate(void) const
Definition: programinfo.h:432
void SaveEditing(bool edit)
Sets "editing" field in "recorded" table to "edit".
void ToStringList(QStringList &list) const
Serializes ProgramInfo into a QStringList which can be passed over a socket.
QString GetPlaybackURL(bool checkMaster=false, bool forceCheckLocal=false)
Returns filename or URL to be used to play back this recording.
QString GetSubtitle(void) const
Definition: programinfo.h:364
uint GetSeason(void) const
Definition: programinfo.h:367
QString GetChannelSchedulingID(void) const
This is the unique programming identifier of a channel.
Definition: programinfo.h:384
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:36
void QuickRecord(void)
Create a kSingleRecord if not already scheduled.
void ApplyRecordRecGroupChange(const QString &newrecgroup)
Sets the recording group, both in this RecordingInfo and in the database.
void ApplyTranscoderProfileChange(const QString &profile) const
Sets the transcoder profile for a recording.
Internal representation of a recording rule, mirrors the record table.
Definition: recordingrule.h:30
bool LoadTemplate(const QString &title, const QString &category="Default", const QString &categoryType="Default")
General purpose reference counter.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
QAtomicInt m_referenceCount
virtual int IncrRef(void)
Increments reference count.
std::chrono::milliseconds SetSignalMonitoringRate(std::chrono::milliseconds rate, int notifyFrontend=1)
Sets the signal monitoring rate.
void StopLiveTV(void)
Tells TVRec to stop a "Live TV" recorder.
bool ShouldSwitchToAnotherCard(const QString &channelid)
Checks if named channel exists on current tuner, or another tuner.
void SpawnLiveTV(const QString &chainid, bool pip, const QString &startchan)
Tells TVRec to Spawn a "Live TV" recorder.
void ChangeChannel(int channeldirection)
void SetLiveRecording(bool recording)
int ChangePictureAttribute(PictureAdjustType type, PictureAttribute attr, bool up)
Changes brightness/contrast/colour/hue of a recording.
int GetPictureAttribute(PictureAttribute attr)
bool IsValidRecorder(void) const
void GetChannelInfo(InfoMap &infoMap, uint chanid=0)
bool CheckChannelPrefix(const QString &prefix, uint &complete_valid_channel_on_rec, bool &is_extra_char_useful, QString &needed_spacer)
Checks a prefix against the channels in the DB.
void PauseRecorder(void)
Tells TVRec to pause a recorder, used for channel and input changes.
void SetChannel(const QString &channel)
uint GetSignalLockTimeout(const QString &input)
void FrontendReady(void)
bool SetChannelInfo(const InfoMap &infoMap)
QString GetInput(void)
bool Setup(void)
bool CheckChannel(const QString &channel)
Checks if named channel exists on current tuner.
void CancelNextRecording(bool cancel)
int GetRecorderNumber(void) const
void ToggleChannelFavorite(const QString &changroupname)
bool IsRecording(bool *ok=nullptr)
uint64_t m_framesPlayed
Definition: tv_play.h:789
ProgramInfo * m_progInfo
Definition: tv_play.h:788
void run() override
Definition: tv_play.cpp:10531
static bool IsExiting(void)
static bool AllGood(const SignalMonitorList &slist)
Returns true if all the values in the list return true on IsGood().
static SignalMonitorList Parse(const QStringList &slist)
Converts a list of strings to SignalMonitorValue classes.
static QString GetSourceName(uint sourceid)
Definition: sourceutil.cpp:47
void BrowseEnd(bool ChangeChannel)
Ends channel browsing.
BrowseInfo GetBrowsedInfo() const
void BrowseDispInfo(const BrowseInfo &Browseinfo)
void BrowseChannel(const QString &Channum)
void BrowseInit(std::chrono::seconds BrowseMaxForward, bool BrowseAllTuners, bool UseChannelGroups, const QString &DBChannelOrdering)
uint GetBrowseChanId(const QString &Channum, uint PrefCardid, uint PrefSourceid) const
Returns a chanid for the channum, or 0 if none is available.
friend class TV
bool BrowseStart(bool SkipBrowse=false)
Begins channel browsing.
void ChangeAspectOverride(AspectOverrideMode AspectMode=kAspect_Toggle)
void GoToDVDProgram(bool Direction)
void ChangeOSDDebug()
MythAudioState m_audioState
void SetCaptionsEnabled(bool Enable, bool UpdateOSD=true)
void ChangeZoom(ZoomDirection Zoom)
void GoToMenu(const QString &Menu)
void DisableEdit(int HowToSave)
void EnableCaptions(uint Mode, bool UpdateOSD=true)
void ChangeCaptionTrack(int Direction)
void UpdateLastPlayPosition(uint64_t frame)
void ChangeMuteState(bool CycleChannels=false)
void HideAll(bool KeepSubs=true, MythScreenType *Except=nullptr, bool DropNotification=false)
void ResizeScreenForVideo(QSize Size={})
void DisableCaptions(uint Mode, bool UpdateOSD=true)
void ResetTeletext()
void EmbedPlayback(bool Embed, const QRect &Rect={})
void UpdateBookmark(bool Clear=false)
void ChangeOSDMessage(const QString &Message)
void ChangeStereoOverride(StereoscopicMode Mode)
void ChangeOSDDialog(const MythOSDDialogData &Data)
void AdjustSubtitleDelay(std::chrono::milliseconds Delta)
void InitialisePlayerState()
void RefreshEditorState(bool CheckSaved=false)
void AdjustSubtitleZoom(int Delta)
void SaveBottomLine()
MythOverlayState m_overlayState
void ChangeAdjustFill(AdjustFillMode FillMode=kAdjustFill_Toggle)
void WindowResized(const QSize &Size)
MythCaptionsState m_captionsState
void ChangeOSDPositionUpdates(bool Enable)
void ToggleDetectLetterBox()
void ChangeTrack(uint Type, int Direction)
void SetTeletextPage(uint Page)
void RequestEmbedding(bool Embed, const QRect &Rect={}, const QStringList &Data={})
MythVisualiserState m_visualiserState
void HandleTeletextAction(const QString &Action, bool &Handled)
void RestartITV(uint Chanid, uint Cardid, bool IsLiveTV)
void ResetCaptions()
void PauseAudioUntilReady()
void ToggleCaptions()
void ToggleMoveBottomLine()
void HandleITVAction(const QString &Action, bool &Handled)
void EnableTeletext(int Page=0x100)
MythVideoColourState m_videoColourState
void ChangeVolume(bool Direction, int Volume)
void ToggleCaptionsByType(uint Type)
MythVideoBoundsState m_videoBoundsState
void IsOSDVisible(bool &Visible)
void ChangeAudioOffset(std::chrono::milliseconds Delta, std::chrono::milliseconds Value=-9999ms)
void ChangeAllowForcedSubtitles(bool Allow)
void ChangePictureAttribute(PictureAttribute Attribute, bool Direction, int Value)
void EnableVisualiser(bool Enable, bool Toggle=false, const QString &Name=QString())
void ChangeUpmix(bool Enable, bool Toggle=false)
MythEditorState m_editorState
void ChangeOSDText(const QString &Window, const InfoMap &Map, OSDTimeout Timeout)
void SetTrack(uint Type, uint TrackNo)
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:143
QString dispString
Definition: tv_play.cpp:984
std::chrono::milliseconds milliseconds
Definition: tv_play.cpp:985
SleepTimerInfo(QString String, std::chrono::milliseconds MilliSeconds)
Definition: tv_play.cpp:981
Control TV playback.
Definition: tv_play.h:156
QList< std::chrono::seconds > m_tvmChapterTimes
Definition: tv_play.h:729
void ChannelEditXDSFill(InfoMap &Info)
Definition: tv_play.cpp:8058
void DoQueueTranscode(const QString &Profile)
Definition: tv_play.cpp:5340
QString m_lcdTitle
Definition: tv_play.h:653
void HandleOSDIdle(const QString &Action)
Definition: tv_play.cpp:7217
MythTVMenu m_playbackCompactMenu
Definition: tv_play.h:745
bool SubtitleZoomHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3739
void VolumeChange(bool Up, int NewVolume=-1)
Definition: tv_play.cpp:7022
bool ManualZoomHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3585
PictureAttribute m_adjustingPictureAttribute
Picture attribute to modify (on arrow left or right)
Definition: tv_play.h:567
void DVDJumpForward()
Definition: tv_play.cpp:10130
static bool IsTVRunning()
Check whether media is currently playing.
Definition: tv_play.cpp:173
OSD * GetOSDL()
Definition: tv_play.cpp:10471
volatile int m_endOfRecPromptTimerId
Definition: tv_play.h:689
void ShowLCDChannelInfo()
Definition: tv_play.cpp:6696
bool HandleLCDTimerEvent()
Definition: tv_play.cpp:2638
void QuickRecord()
Definition: tv_play.cpp:7673
void GetPlayerWriteLock() const
Definition: tv_play.cpp:10496
void ProcessNetworkControlCommand(const QString &Command)
Definition: tv_play.cpp:4301
InfoMap m_chanEditMap
Channel Editing initial map.
Definition: tv_play.h:581
bool m_requestDelete
User wants last video deleted.
Definition: tv_play.h:558
QList< QKeyEvent * > m_screenPressKeyMapLiveTV
Definition: tv_play.h:621
QString GetTitleName(int Title)
Definition: tv_play.cpp:5493
int Playback(const ProgramInfo &ProgInfo)
Definition: tv_play.cpp:1910
bool GetJumpToProgram() const
This is set if the user asked MythTV to jump to the previous recording in the playlist.
Definition: tv_play.h:320
volatile int m_endOfPlaybackTimerId
Definition: tv_play.h:688
void IdleDialogTimeout()
Definition: tv_play.cpp:7240
static const int kInitFFRWSpeed
Definition: tv_play.h:750
bool TranslateKeyPressOrGesture(const QString &Context, QEvent *Event, QStringList &Actions, bool IsLiveTV, bool AllowJumps=true)
Definition: tv_play.cpp:3290
volatile int m_networkControlTimerId
Definition: tv_play.h:684
QList< QKeyEvent * > m_screenPressKeyMapPlayback
Definition: tv_play.h:620
bool HandleOSDCutpoint(const QString &Action)
Definition: tv_play.cpp:7879
bool DoPlayerSeekToFrame(uint64_t FrameNum)
Definition: tv_play.cpp:4973
void ShowOSDAlreadyEditing()
Definition: tv_play.cpp:7917
static QList< QKeyEvent * > ConvertScreenPressKeyMap(const QString &KeyList)
Definition: tv_play.cpp:3225
void PopPreviousChannel(bool ImmediateChange)
Definition: tv_play.cpp:6272
PlayerContext m_playerContext
Definition: tv_play.h:639
void DoPlay()
Definition: tv_play.cpp:4794
static const std::chrono::milliseconds kSpeedChangeCheckFrequency
Definition: tv_play.h:765
static const std::chrono::milliseconds kEndOfRecPromptCheckFrequency
Definition: tv_play.h:767
void HandleStateChange()
Changes the state to the state on the front of the state change queue.
Definition: tv_play.cpp:1984
static void ToggleChannelFavorite()
Definition: tv_play.cpp:5773
static const std::chrono::milliseconds kErrorRecoveryCheckFrequency
Definition: tv_play.h:766
bool MenuItemDisplayPlayback(const MythTVMenuItemContext &Context, MythOSDDialogData *Menu)
Definition: tv_play.cpp:8701
int StartTimer(std::chrono::milliseconds Interval, int Line)
Definition: tv_play.cpp:2694
static const std::vector< SleepTimerInfo > s_sleepTimes
Definition: tv_play.h:584
uint GetQueuedChanID() const
Definition: tv_play.h:346
bool m_underNetworkControl
initial show started via by the network control interface
Definition: tv_play.h:633
void SleepDialogTimeout()
Definition: tv_play.cpp:7183
std::chrono::milliseconds m_dbIdleTimeout
Definition: tv_play.h:524
bool m_doSmartForward
Definition: tv_play.h:560
QString m_dbChannelFormat
Definition: tv_play.h:523
MythPlayerUI * m_player
Definition: tv_play.h:642
bool m_zoomMode
Definition: tv_play.h:555
QMap< QString, ProgramList > m_progLists
Definition: tv_play.h:574
bool AudioSyncHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3716
void DoEditSchedule(int EditType=kScheduleProgramGuide, const QString &EditArg="")
Definition: tv_play.cpp:6887
bool MenuItemDisplay(const MythTVMenuItemContext &Context, MythOSDDialogData *Menu) override
Definition: tv_play.cpp:8557
int m_tvmCurrentChapter
Definition: tv_play.h:728
bool m_ignoreKeyPresses
should we ignore keypresses
Definition: tv_play.h:667
volatile int m_lcdVolumeTimerId
Definition: tv_play.h:683
void ShowOSDStopWatchingRecording()
Definition: tv_play.cpp:10209
static const std::chrono::milliseconds kSleepTimerDialogTimeout
Definition: tv_play.h:760
void UpdateOSDStatus(const QString &Title, const QString &Desc, const QString &Value, int Type, const QString &Units, int Position=0, enum OSDTimeout Timeout=kOSDTimeout_Med)
Definition: tv_play.cpp:6411
uint m_queuedChanID
Queued ChanID (from EPG channel selector)
Definition: tv_play.h:610
int m_sleepDialogTimerId
Timer for sleep dialog.
Definition: tv_play.h:588
bool m_subtitleDelayAdjustment
True if subtitle delay is turned on.
Definition: tv_play.h:554
MythDeque< QString > m_networkControlCommands
Definition: tv_play.h:679
void RetrieveCast(const ProgramInfo &ProgInfo)
Definition: tv_play.cpp:9576
void HandleSaveLastPlayPosEvent()
Definition: tv_play.cpp:10394
MythTVMenu m_cutlistCompactMenu
Definition: tv_play.h:747
uint m_initialChanID
Initial chanid override for Live TV.
Definition: tv_play.h:612
int GetNumTitles()
Definition: tv_play.cpp:5432
void PlaybackLoop()
The main playback loop.
Definition: tv_play.cpp:1307
static EMBEDRETURNVOIDSCHEDIT RunScheduleEditorPtr
Definition: tv_play.h:202
bool m_sigMonMode
Are we in signal monitoring mode?
Definition: tv_play.h:556
int m_tvmNumTitles
Definition: tv_play.h:732
bool HasQueuedChannel() const
Definition: tv_play.h:340
void ClearInputQueues(bool Hideosd)
Clear channel key buffer of input keys.
Definition: tv_play.cpp:5820
static bool IsPaused()
Check whether playback is paused.
Definition: tv_play.cpp:4892
bool m_tvmIsRecorded
Definition: tv_play.h:715
bool m_savedPause
saved pause state before embedding
Definition: tv_play.h:668
bool GetAllowRerecord() const
Returns true if the user told Mythtv to allow re-recording of the show.
Definition: tv_play.h:314
QVector< string_pair > m_guests
Definition: tv_play.h:578
bool m_allowRerecord
User wants to rerecord the last video if deleted.
Definition: tv_play.h:559
ChannelGroupList m_dbChannelGroups
Definition: tv_play.h:538
QString GetRecordingGroup() const
Definition: tv_play.cpp:10433
void setUnderNetworkControl(bool setting)
Definition: tv_play.h:294
void HandleEndOfRecordingExitPromptTimerEvent()
Definition: tv_play.cpp:2857
static EMBEDRETURNVOIDEPG RunProgramGuidePtr
Definition: tv_play.h:200
static void SetFuncPtr(const char *Name, void *Pointer)
Import pointers to functions used to embed the TV window into other containers e.g.
Definition: tv_play.cpp:477
bool GetEndOfRecording() const
This is set to true if the player reaches the end of the recording without the user explicitly exitin...
Definition: tv_play.h:317
QString GetAngleName(int Angle)
Definition: tv_play.cpp:5472
bool ProcessKeypressOrGesture(QEvent *Event)
Definition: tv_play.cpp:3303
void ShowOSDSleep()
Definition: tv_play.cpp:7146
uint m_vbimode
Definition: tv_play.h:545
float DoTogglePauseStart()
Definition: tv_play.cpp:4828
MThreadPool * GetPosThreadPool()
Definition: tv_play.cpp:10526
void DoSwitchAngle(int Angle)
Definition: tv_play.cpp:5519
QVariant m_tvmJumprecBackHack
Definition: tv_play.h:741
bool RequestNextRecorder(bool ShowDialogs, const ChannelInfoList &Selection=ChannelInfoList())
Definition: tv_play.cpp:1559
bool HandleJumpToProgramAction(const QStringList &Actions)
Definition: tv_play.cpp:9812
bool CommitQueuedInput()
Definition: tv_play.cpp:5952
void SetFFRew(int Index)
Definition: tv_play.cpp:5295
void FillOSDMenuJumpRec(const QString &Category="", int Level=0, const QString &Selected="")
Definition: tv_play.cpp:9469
static const std::chrono::milliseconds kVideoExitDialogTimeout
Definition: tv_play.h:762
bool m_tvmIsVideo
Definition: tv_play.h:716
MThreadPool * m_posThreadPool
Definition: tv_play.h:520
void SwitchInputs(uint ChanID=0, QString ChanNum="", uint InputID=0)
Definition: tv_play.cpp:5614
QMap< QString, AskProgramInfo > m_askAllowPrograms
Definition: tv_play.h:570
QString m_queuedChanNum
Input key presses queued up so far to form a valid ChanNum.
Definition: tv_play.h:608
bool DoPlayerSeek(float Time)
Definition: tv_play.cpp:4937
bool m_stretchAdjustment
True if time stretch is turned on.
Definition: tv_play.h:551
void FillOSDMenuActorShows(const QString &actor, int person_id, const QString &category="")
Definition: tv_play.cpp:9675
bool m_asInputMode
Are we in Arbitrary seek input mode?
Definition: tv_play.h:602
void ReloadKeys()
Definition: tv_play.cpp:968
bool StartPlayer(TVState desiredState)
Definition: tv_play.cpp:4771
void ToggleTimeStretch()
Definition: tv_play.cpp:7054
bool m_ffRewReverse
Definition: tv_play.h:542
int GetNumAngles()
Definition: tv_play.cpp:5452
PictureAdjustType m_adjustingPicture
Picture attribute type to modify.
Definition: tv_play.h:565
QString GetQueuedInput() const
Definition: tv_play.cpp:5786
static const uint kNextSource
Definition: tv_play.h:752
void PlaybackMenuDeinit(const MythTVMenu &Menu)
Definition: tv_play.cpp:9364
bool LiveTV(bool ShowDialogs, const ChannelInfoList &Selection)
Starts LiveTV.
Definition: tv_play.cpp:1531
PlayerContext * GetPlayerContext()
Return a pointer to TV::m_playerContext.
Definition: tv_play.cpp:193
bool m_subtitleZoomAdjustment
True if subtitle zoom is turned on.
Definition: tv_play.h:553
volatile int m_videoExitDialogTimerId
Definition: tv_play.h:690
std::chrono::milliseconds m_sleepTimerTimeout
Current sleep timeout in msec.
Definition: tv_play.h:586
void ShowOSDPromptDeleteRecording(const QString &Title, bool Force=false)
Definition: tv_play.cpp:10249
bool m_tvmIsLiveTv
Definition: tv_play.h:723
void DoTogglePauseFinish(float Time, bool ShowOSD)
Definition: tv_play.cpp:4857
void DoJumpChapter(int Chapter)
Definition: tv_play.cpp:5417
void HandleEndOfPlaybackTimerEvent()
Definition: tv_play.cpp:2826
void UpdateOSDSignal(const QStringList &List)
Updates Signal portion of OSD...
Definition: tv_play.cpp:6450
bool MenuItemDisplayCutlist(const MythTVMenuItemContext &Context, MythOSDDialogData *Menu)
Definition: tv_play.cpp:8566
bool m_tvmFillAutoDetect
Definition: tv_play.h:709
bool m_dbUseGuiSizeForTv
Definition: tv_play.h:530
bool HandleOSDVideoExit(const QString &Action)
Definition: tv_play.cpp:10350
bool m_dbRememberLastChannelGroup
Definition: tv_play.h:537
void HandleOSDAskAllow(const QString &Action)
Definition: tv_play.cpp:1872
volatile int m_speedChangeTimerId
Definition: tv_play.h:692
bool m_ccInputMode
Are we in CC/Teletext page/stream selection mode?
Definition: tv_play.h:598
void UpdateOSDTimeoutMessage()
Definition: tv_play.cpp:6608
void ForceNextStateNone()
Definition: tv_play.cpp:2707
void PauseLiveTV()
Used in ChangeChannel() to temporarily stop video output.
Definition: tv_play.cpp:9980
static EMBEDRETURNVOID RunPlaybackBoxPtr
Definition: tv_play.h:198
float m_ffRewRepos
Definition: tv_play.h:541
void ShowOSDIdle()
After idleTimer has expired, display a dialogue warning the user that we will exit LiveTV unless they...
Definition: tv_play.cpp:7201
void GetPlayerReadLock() const
Definition: tv_play.cpp:10501
int m_tvmCurtrack[kTrackTypeCount]
Definition: tv_play.h:703
void ToggleSleepTimer()
Definition: tv_play.cpp:7120
MythTimer m_keyRepeatTimer
Queue of unprocessed key presses.
Definition: tv_play.h:594
void StartOsdNavigation()
Definition: tv_play.cpp:7997
static const std::chrono::milliseconds kEndOfPlaybackFirstCheckTimer
Definition: tv_play.h:772
static const uint kPreviousSource
Definition: tv_play.h:753
void HandleOSDAlreadyEditing(const QString &Action, bool WasPaused)
Definition: tv_play.cpp:7931
static EMBEDRETURNVOIDFINDER RunProgramFinderPtr
Definition: tv_play.h:201
void GetStatus()
Definition: tv_play.cpp:1375
volatile int m_lcdTimerId
Definition: tv_play.h:682
volatile int m_channelGroupId
Definition: tv_play.h:675
int GetCurrentAngle()
Definition: tv_play.cpp:5462
bool m_tvmIsDvd
Definition: tv_play.h:720
~TV() override
Definition: tv_play.cpp:1227
QRect m_playerBounds
Prior GUI window bounds, for DoEditSchedule() and player exit().
Definition: tv_play.h:660
QReadWriteLock m_playerLock
lock on player and playerActive changes
Definition: tv_play.h:644
bool m_tvmIsPaused
Definition: tv_play.h:718
bool m_wantsToQuit
True if the user told MythTV to stop playback.
Definition: tv_play.h:550
QRecursiveMutex m_askAllowLock
Definition: tv_play.h:571
void PlaybackExiting(TV *Player)
void onApplicationStateChange(Qt::ApplicationState State)
Definition: tv_play.cpp:10511
bool DialogIsVisible(const QString &Dialog)
Definition: tv_play.cpp:8529
void UpdateOSDProgInfo(const char *WhichInfo)
Update and display the passed OSD set with programinfo.
Definition: tv_play.cpp:6385
static const uint kInputKeysMax
Definition: tv_play.h:751
static bool StateIsLiveTV(TVState State)
Definition: tv_play.cpp:1956
bool StartRecorder(std::chrono::milliseconds MaxWait=-1ms)
Starts recorder, must be called before StartPlayer().
Definition: tv_play.cpp:2334
bool CreatePlayer(TVState State, bool Muted=false)
Definition: tv_play.cpp:198
void ToggleAutoExpire()
Definition: tv_play.cpp:9764
static bool StartTV(ProgramInfo *TVRec, uint Flags, const ChannelInfoList &Selection=ChannelInfoList())
Start playback of media.
Definition: tv_play.cpp:287
QString m_lcdCallsign
Definition: tv_play.h:655
void MenuLazyInit(void *Field)
Definition: tv_play.cpp:9267
volatile int m_asInputTimerId
Definition: tv_play.h:686
bool event(QEvent *Event) override
This handles all standard events.
Definition: tv_play.cpp:3037
bool DoSetPauseState(bool Pause)
Definition: tv_play.cpp:6876
bool HandleTrackAction(const QString &Action)
Definition: tv_play.cpp:3104
static const int kScreenPressRegionCount
screen area to keypress translation region is now 0..11 0 1 2 3 4 5 6 7 8 9 10 11
Definition: tv_play.h:619
void HandleLCDVolumeTimerEvent()
Definition: tv_play.cpp:2679
bool m_lockTimerOn
Definition: tv_play.h:625
ArbSeekWhence
Definition: tv_play.h:372
@ ARBSEEK_FORWARD
Definition: tv_play.h:372
@ ARBSEEK_END
Definition: tv_play.h:372
@ ARBSEEK_SET
Definition: tv_play.h:372
@ ARBSEEK_REWIND
Definition: tv_play.h:372
static bool IsTunable(uint ChanId)
Definition: tv_play.cpp:6766
int GetCurrentTitle()
Definition: tv_play.cpp:5442
void HandleOSDSleep(const QString &Action)
Definition: tv_play.cpp:7162
int m_tvmSpeedX100
Definition: tv_play.h:712
void HandlePseudoLiveTVTimerEvent()
Definition: tv_play.cpp:2912
bool m_audiosyncAdjustment
True if audiosync is turned on.
Definition: tv_play.h:552
volatile int m_exitPlayerTimerId
Definition: tv_play.h:694
bool m_tvmIsBd
Definition: tv_play.h:721
bool m_tvmSubsHaveSubs
Definition: tv_play.h:736
bool m_savePosOnExit
False until first timer event.
Definition: tv_play.h:562
void UpdateOSDInput()
Definition: tv_play.cpp:6439
int GetNumChapters()
Definition: tv_play.cpp:5389
QString GetQueuedChanNum() const
Definition: tv_play.cpp:5796
bool m_dbBrowseAllTuners
Definition: tv_play.h:535
bool IsDeleteAllowed()
Definition: tv_play.cpp:10194
bool ProcessSmartChannel(QString &InputStr)
Definition: tv_play.cpp:5882
void StartChannelEditMode()
Starts channel editing mode.
Definition: tv_play.cpp:7968
int GetCurrentChapter()
Definition: tv_play.cpp:5407
void ClearOSD()
Definition: tv_play.cpp:6303
static QStringList lastProgramStringList
Definition: tv_play.h:197
int m_tvmNumAngles
Definition: tv_play.h:730
void GetChapterTimes(QList< std::chrono::seconds > &Times)
Definition: tv_play.cpp:5399
bool TranslateGesture(const QString &Context, MythGestureEvent *Event, QStringList &Actions, bool IsLiveTV)
Definition: tv_play.cpp:3259
static bool StateIsPlaying(TVState State)
Definition: tv_play.cpp:1947
float StopFFRew()
Definition: tv_play.cpp:5237
void StartProgramEditMode()
Starts Program Cut Map Editing mode.
Definition: tv_play.cpp:7902
bool SubtitleDelayHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3762
int GetQueuedInputAsInt(bool *OK=nullptr, int Base=10) const
Definition: tv_play.cpp:5791
int m_dbPlaybackExitPrompt
Definition: tv_play.h:525
void InitFromDB()
Definition: tv_play.cpp:1037
static void MenuStrings()
Definition: tv_play.cpp:9402
bool BrowseHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3540
void SetErrored()
Definition: tv_play.cpp:2753
void StopPlayback()
Definition: tv_play.cpp:268
void DoSkipCommercials(int Direction)
Definition: tv_play.cpp:5535
static TV * AcquireRelease(int &RefCount, bool Acquire, bool Create=false)
Statically create, destroy or check the existence of the TV instance.
Definition: tv_play.cpp:140
void DoTogglePictureAttribute(PictureAdjustType Type)
Definition: tv_play.cpp:7799
bool m_dbUseVideoModes
Definition: tv_play.h:531
bool ContextIsPaused(const char *File, int Location)
Definition: tv_play.cpp:10461
void ToggleOSD(bool IncludeStatusOSD)
Cycle through the available Info OSDs.
Definition: tv_play.cpp:6319
volatile int m_saveLastPlayPosTimerId
Definition: tv_play.h:695
volatile int m_ccInputTimerId
Definition: tv_play.h:685
bool HandleOSDChannelEdit(const QString &Action)
Processes channel editing key.
Definition: tv_play.cpp:8012
QMutex m_channelGroupLock
Lock necessary when modifying channel group variables.
Definition: tv_play.h:674
void UnpauseLiveTV(bool Quietly=false)
Used in ChangeChannel() to restart video output.
Definition: tv_play.cpp:10017
void ReturnPlayerLock() const
Definition: tv_play.cpp:10506
std::vector< int > m_ffRewSpeeds
Definition: tv_play.h:543
QMutex m_lastProgramLock
Definition: tv_play.h:630
TvPlayWindow * m_myWindow
Our screen, if it exists.
Definition: tv_play.h:658
void HandleOSDClosed(int OSDType)
Definition: tv_play.cpp:7748
bool eventFilter(QObject *Object, QEvent *Event) override
Prevent events from being sent to another object.
Definition: tv_play.cpp:2990
static bool StateIsRecording(TVState State)
Definition: tv_play.cpp:1942
void ShowOSDCutpoint(const QString &Type)
Definition: tv_play.cpp:7835
void DoArbSeek(ArbSeekWhence Whence, bool HonorCutlist)
Definition: tv_play.cpp:5137
void ReturnOSDLock() const
Definition: tv_play.cpp:10489
void SetBookmark(bool Clear=false)
Definition: tv_play.cpp:4175
QMutex m_progListsLock
Definition: tv_play.h:573
PictureAttribute NextPictureAdjustType(PictureAdjustType Type, PictureAttribute Attr)
Definition: tv_play.cpp:7777
bool m_weDisabledGUI
true if this instance disabled MythUI drawing.
Definition: tv_play.h:664
void UpdateOSDSeekMessage(const QString &Msg, enum OSDTimeout Timeout)
Definition: tv_play.cpp:6425
bool SeekHandleAction(const QStringList &Actions, bool IsDVD)
Definition: tv_play.cpp:5002
bool m_tvmSubsForcedOn
Definition: tv_play.h:735
bool m_tvmTranscoding
Definition: tv_play.h:739
volatile int m_errorRecoveryTimerId
Definition: tv_play.h:693
void PlaybackMenuShow(const MythTVMenu &Menu, const QDomNode &Node, const QDomNode &Selected)
Definition: tv_play.cpp:9369
void OSDDialogEvent(int Result, const QString &Text, QString Action)
Definition: tv_play.cpp:8095
void HandleVideoExitDialogTimerEvent()
Definition: tv_play.cpp:2887
void SetAutoCommercialSkip(CommSkipMode SkipMode=kCommSkipOff)
Definition: tv_play.cpp:9787
bool m_queuedTranscode
Definition: tv_play.h:561
static const std::chrono::milliseconds kIdleTimerDialogTimeout
Definition: tv_play.h:761
void HideOSDWindow(const char *window)
Definition: tv_play.cpp:6680
TVState GetState() const
Definition: tv_play.cpp:1366
void KillTimer(int Id)
Definition: tv_play.cpp:2702
static const std::chrono::milliseconds kSaveLastPlayPosTimeout
Definition: tv_play.h:768
bool m_dbEndOfRecExitPrompt
Definition: tv_play.h:528
void AskAllowRecording(const QStringList &Msg, int Timeuntil, bool HasRec, bool HasLater)
Definition: tv_play.cpp:1612
void ShowPreviousChannel()
Definition: tv_play.cpp:6263
void UpdateChannelList(int GroupID)
update the channel list with channels from the selected channel group
Definition: tv_play.cpp:1343
static EMBEDRETURNVOIDPROGLIST RunProgramListPtr
Definition: tv_play.h:203
void SetExitPlayer(bool SetIt, bool WantsTo)
Definition: tv_play.cpp:2809
bool m_tvmPreviousChan
Definition: tv_play.h:724
int m_tvmNumChapters
Definition: tv_play.h:727
bool m_dbContinueEmbedded
Definition: tv_play.h:533
void ChangeTimeStretch(int Dir, bool AllowEdit=true)
Definition: tv_play.cpp:7068
bool TimeStretchHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3691
static const std::chrono::milliseconds kEndOfPlaybackCheckFrequency
Definition: tv_play.h:763
static EMBEDRETURNVOID RunViewScheduledPtr
Definition: tv_play.h:199
MythTVMenu m_cutlistMenu
Definition: tv_play.h:746
bool m_dbJumpPreferOsd
Definition: tv_play.h:529
bool m_jumpToProgram
Definition: tv_play.h:636
QStringList m_tvmTracks[kTrackTypeCount]
Definition: tv_play.h:702
bool FFRewHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:4069
bool m_dbRunJobsOnRemote
Definition: tv_play.h:532
QString m_lcdSubtitle
Definition: tv_play.h:654
static int ConfiguredTunerCards()
If any cards are configured, return the number.
Definition: tv_play.cpp:118
void ChannelEditAutoFill(InfoMap &Info)
Automatically fills in as much information as possible.
Definition: tv_play.cpp:8048
void SwitchSource(uint Direction)
Definition: tv_play.cpp:5558
int m_tvmCurrentTitle
Definition: tv_play.h:733
void SetSpeedChangeTimer(std::chrono::milliseconds When, int Line)
Definition: tv_play.cpp:2946
void ShowLCDDVDInfo()
Definition: tv_play.cpp:6722
void HandleOSDInfo(const QString &Action)
Definition: tv_play.cpp:8539
void AddKeyToInputQueue(char Key)
Definition: tv_play.cpp:5835
bool ActivePostQHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:4193
int m_tvmFreeRecorderCount
Definition: tv_play.h:719
QDateTime m_lastLockSeenTime
Definition: tv_play.h:626
void DoSeekAbsolute(long long Seconds, bool HonorCutlist)
Definition: tv_play.cpp:5123
bool ActiveHandleAction(const QStringList &Actions, bool IsDVD, bool IsDVDStillFrame)
Definition: tv_play.cpp:3801
static const std::chrono::milliseconds kInputModeTimeout
Definition: tv_play.h:755
void timerEvent(QTimerEvent *Event) override
Definition: tv_play.cpp:2419
MythMainWindow * m_mainWindow
Definition: tv_play.h:519
QString m_queuedInput
Input key presses queued up so far...
Definition: tv_play.h:606
ProgramInfo * m_lastProgram
last program played with this player
Definition: tv_play.h:631
static QVector< uint > IsTunableOn(PlayerContext *Context, uint ChanId)
Definition: tv_play.cpp:6799
void DoTogglePause(bool ShowOSD)
Definition: tv_play.cpp:4914
void NormalSpeed()
Definition: tv_play.cpp:5174
bool m_smartForward
Definition: tv_play.h:540
bool IsSameProgram(const ProgramInfo *ProgInfo) const
Definition: tv_play.cpp:10449
MythTVMenu m_playbackMenu
Definition: tv_play.h:744
void ScheduleStateChange()
Definition: tv_play.cpp:2713
volatile int m_signalMonitorTimerId
Definition: tv_play.h:696
void OverrideScan(FrameScanType Scan)
Definition: tv_play.cpp:9748
QVector< string_pair > m_guest_stars
Definition: tv_play.h:577
void DoSeekRWND()
Definition: tv_play.cpp:10089
void DoSwitchTitle(int Title)
Definition: tv_play.cpp:5503
void EditSchedule(int EditType=kScheduleProgramGuide, const QString &arg="")
Definition: tv_play.cpp:7014
int m_sleepTimerId
Timer for turning off playback.
Definition: tv_play.h:587
void ChangeChannel(const ChannelInfoList &Options)
Definition: tv_play.cpp:6242
bool DiscMenuHandleAction(const QStringList &Actions) const
Definition: tv_play.cpp:3785
bool IsTunablePriv(uint ChanId)
Definition: tv_play.cpp:6782
uint m_dbAutoexpireDefault
Definition: tv_play.h:526
uint m_sleepIndex
Index into sleep_times.
Definition: tv_play.h:585
RemoteEncoder * m_switchToRec
Main recorder to use after a successful SwitchCards() call.
Definition: tv_play.h:650
CommSkipMode m_tvmCurSkip
Definition: tv_play.h:717
void DoJumpFFWD()
Definition: tv_play.cpp:10064
void ChangeFFRew(int Direction)
Definition: tv_play.cpp:5262
void ITVRestart(bool IsLive)
Restart the MHEG/MHP engine.
Definition: tv_play.cpp:10045
bool m_tvmIsOn
Definition: tv_play.h:738
void SetManualZoom(bool ZoomON, const QString &Desc)
Definition: tv_play.cpp:9803
QElapsedTimer m_lockTimer
Definition: tv_play.h:624
bool ToggleHandleAction(const QStringList &Actions, bool IsDVD)
Definition: tv_play.cpp:4106
bool m_endOfRecording
!player->IsPlaying() && StateIsPlaying()
Definition: tv_play.h:557
QRect m_savedGuiBounds
Definition: tv_play.h:662
void SetLastProgram(const ProgramInfo *ProgInfo)
Definition: tv_play.cpp:10413
QRecursiveMutex m_chanEditMapLock
Lock for chanEditMap and ddMap.
Definition: tv_play.h:580
bool m_dbAutoSetWatched
Definition: tv_play.h:527
static const std::chrono::milliseconds kKeyRepeatTimeout
Definition: tv_play.h:758
void DoJumpRWND()
Definition: tv_play.cpp:10079
std::chrono::seconds GetTitleDuration(int Title)
Definition: tv_play.cpp:5482
void PrepareToExitPlayer(int Line)
Definition: tv_play.cpp:2770
void ShowNoRecorderDialog(NoRecorderMsg MsgType=kNoRecorders)
Definition: tv_play.cpp:9949
uint m_switchToInputId
Definition: tv_play.h:546
static void FillOSDMenuCastButton(MythOSDDialogData &dialog, const QVector< string_pair > &people)
Definition: tv_play.cpp:9644
bool HasQueuedInput() const
Definition: tv_play.h:339
bool PictureAttributeHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3662
bool m_dbBrowseAlways
Definition: tv_play.h:534
bool m_tvmAvsync
Definition: tv_play.h:706
bool m_tvmJump
Definition: tv_play.h:722
static void InitKeys()
Definition: tv_play.cpp:494
ProgramInfo * GetLastProgram() const
Definition: tv_play.cpp:10425
QVector< string_pair > m_actors
Definition: tv_play.h:576
int m_idleDialogTimerId
Timer for idle dialog.
Definition: tv_play.h:591
void Embed(bool Embed, QRect Rect={}, const QStringList &Data={})
Definition: tv_play.cpp:6843
void PrepToSwitchToRecordedProgram(const ProgramInfo &ProgInfo)
Definition: tv_play.cpp:2760
bool m_inPlaylist
show is part of a playlist
Definition: tv_play.h:632
bool m_tvmIsRecording
Definition: tv_play.h:714
bool StartPlaying(std::chrono::milliseconds MaxWait=-1ms)
Definition: tv_play.cpp:234
void HandleSpeedChangeTimerEvent()
Definition: tv_play.cpp:2953
void DVDJumpBack()
Definition: tv_play.cpp:10097
void ShowOSDAskAllow()
Definition: tv_play.cpp:1653
void SetInPlayList(bool InPlayList)
Definition: tv_play.h:293
int m_tvmCurrentAngle
Definition: tv_play.h:731
bool Init()
Performs instance initialization, returns true on success.
Definition: tv_play.cpp:1145
void StopStuff(bool StopRingBuffer, bool StopPlayer, bool StopRecorder)
Can shut down the ringbuffers, the players, and in LiveTV it can shut down the recorders.
Definition: tv_play.cpp:2384
volatile int m_pseudoChangeChanTimerId
Definition: tv_play.h:691
void FillOSDMenuCast(void)
Definition: tv_play.cpp:9663
bool m_dbUseChannelGroups
Definition: tv_play.h:536
void customEvent(QEvent *Event) override
This handles all custom events.
Definition: tv_play.cpp:7275
volatile int m_queueInputTimerId
Definition: tv_play.h:687
void ScheduleInputChange()
Definition: tv_play.cpp:2736
int m_idleTimerId
Timer for turning off playback after idle period.
Definition: tv_play.h:590
void DoSeek(float Time, const QString &Msg, bool TimeIsOffset, bool HonorCutlist)
Definition: tv_play.cpp:5086
static const std::chrono::milliseconds kLCDTimeout
Definition: tv_play.h:756
bool m_clearPosOnExit
False unless requested by user on playback exit.
Definition: tv_play.h:563
const MythTVMenu & getMenuFromId(MenuTypeId id)
Definition: tv_play.cpp:7258
void DoSeekFFWD()
Definition: tv_play.cpp:10074
TVState m_tvmState
Definition: tv_play.h:713
bool IsBookmarkAllowed()
Definition: tv_play.cpp:10168
void UpdateLCD()
Definition: tv_play.cpp:6688
void ChangeSpeed(int Direction)
Definition: tv_play.cpp:5189
void ShowOSDMenu(bool isCompact=false)
Definition: tv_play.cpp:9451
ChannelInfoList m_channelGroupChannelList
Definition: tv_play.h:676
void PlaybackMenuInit(const MythTVMenu &Menu)
Definition: tv_play.cpp:9274
bool CalcPlayerSliderPosition(osdInfo &info, bool paddedFields=false) const
Definition: tv_play.cpp:6667
Simple screen shown while the video player is starting up.
Definition: tv_play_win.h:13
void UpdateProgress(void)
Definition: tv_play_win.cpp:44
bool Create(void) override
Definition: tv_play_win.cpp:21
static uint Parse(const QString &vbiformat)
Definition: tv.h:17
@ NTSC_CC
Definition: tv.h:14
@ PAL_TT
Definition: tv.h:13
static QString GetArtPath(const QString &pathname, const QString &type)
int to_track_type(const QString &str)
TrackType
Track types.
Definition: decoderbase.h:27
@ kTrackTypeCC608
Definition: decoderbase.h:32
@ kTrackTypeRawText
Definition: decoderbase.h:36
@ kTrackTypeSubtitle
Definition: decoderbase.h:31
@ kTrackTypeTextSubtitle
Definition: decoderbase.h:42
@ kTrackTypeCount
Definition: decoderbase.h:38
@ kTrackTypeTeletextMenu
Definition: decoderbase.h:35
@ kTrackTypeCC708
Definition: decoderbase.h:33
@ kTrackTypeTeletextCaptions
Definition: decoderbase.h:34
@ kTrackTypeAudio
Definition: decoderbase.h:29
@ kTrackTypeUnknown
Definition: decoderbase.h:28
@ kTrackTypeVideo
Definition: decoderbase.h:30
static constexpr int OK
Definition: dvbci.cpp:69
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
unsigned int uint
Definition: freesurround.h:24
static guint32 * tmp
Definition: goom_core.cpp:26
@ JOB_TRANSCODE
Definition: jobqueue.h:78
@ JOB_STOP
Definition: jobqueue.h:54
@ JOB_USE_CUTLIST
Definition: jobqueue.h:60
@ FUNC_TV
Definition: lcddevice.h:164
@ FUNC_MOVIE
Definition: lcddevice.h:161
std::enable_if_t< std::is_floating_point_v< T >, std::chrono::milliseconds > millisecondsFromFloat(T value)
Helper function for convert a floating point number to a duration.
Definition: mythchrono.h:91
bool progress
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static void showStatus(void)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythNotificationCenter * GetNotificationCenter(void)
MythMainWindow * GetMythMainWindow(void)
static QString GET_KEY(const QString &Context, const QString &Action)
static void REG_KEY(const QString &Context, const QString &Action, const QString &Description, const QString &Key)
@ MEDIASTAT_OPEN
CD/DVD tray open (meaningless for non-CDs?)
Definition: mythmedia.h:16
void ShowNotificationError(const QString &msg, const QString &from, const QString &detail, const VNMask visibility, const MythNotification::Priority priority)
convenience utility to display error message as notification
PlayerFlags
Definition: mythplayer.h:65
@ kAudioMuted
Definition: mythplayer.h:74
@ kNoFlags
Definition: mythplayer.h:66
@ kDecodeAllowGPU
Definition: mythplayer.h:72
void SendMythSystemPlayEvent(const QString &msg, const ProgramInfo *pginfo)
static MythThemedMenu * menu
StereoscopicMode ActionToStereoscopic(const QString &Action)
bool IsActionable(const QString &Action, const QStringList &Actions)
static const MythTVMenu dummy_menubase
Definition: mythtvmenu.h:119
MenuCategory
Definition: mythtvmenu.h:12
@ kMenuCategoryMenu
Definition: mythtvmenu.h:15
@ kMenuCategoryItem
Definition: mythtvmenu.h:13
@ kMenuCurrentDefault
Definition: mythtvmenu.h:27
MenuTypeId
Definition: mythtvmenu.h:37
@ kMenuIdCutlist
Definition: mythtvmenu.h:41
@ kMenuIdPlayback
Definition: mythtvmenu.h:39
@ kMenuIdPlaybackCompact
Definition: mythtvmenu.h:40
@ kMenuIdCutlistCompact
Definition: mythtvmenu.h:42
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
static constexpr const char * ACTION_SCREENSHOT
Definition: mythuiactions.h:22
static constexpr const char * ACTION_LEFT
Definition: mythuiactions.h:18
static constexpr const char * ACTION_DOWN
Definition: mythuiactions.h:17
static constexpr const char * ACTION_TVPOWERON
Definition: mythuiactions.h:25
static constexpr const char * ACTION_TVPOWEROFF
Definition: mythuiactions.h:24
static constexpr const char * ACTION_RIGHT
Definition: mythuiactions.h:19
static constexpr const char * ACTION_SELECT
Definition: mythuiactions.h:15
static constexpr const char * ACTION_UP
Definition: mythuiactions.h:16
static constexpr const char * ACTION_GETSTATUS
Definition: mythuiactions.h:27
QString formatTime(std::chrono::milliseconds msecs, QString fmt)
Format a milliseconds time value.
Definition: mythdate.cpp:242
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ ISODate
Default UTC.
Definition: mythdate.h:17
std::chrono::seconds secsInFuture(const QDateTime &future)
Definition: mythdate.cpp:217
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
MBASE_PUBLIC int naturalCompare(const QString &_a, const QString &_b, Qt::CaseSensitivity caseSensitivity=Qt::CaseSensitive)
This method chops the input a and b into pieces of digits and non-digits (a1.05 becomes a | 1 | .
Definition: stringutil.cpp:160
dictionary info
Definition: azlyrics.py:7
def scan(profile, smoonURL, gate)
Definition: scan.py:54
std::chrono::duration< CHRONO_TYPE, std::ratio< 1, 90000 > > pts
Definition: mythchrono.h:55
int chapterLength
Definition: mythburn.py:191
STL namespace.
static constexpr const char * OSD_WIN_BROWSE
Definition: osd.h:34
static constexpr const char * OSD_DLG_EDITOR
Definition: osd.h:23
static constexpr const char * OSD_DLG_INFO
Definition: osd.h:20
static constexpr const char * OSD_WIN_PROGINFO
Definition: osd.h:31
static constexpr const char * OSD_WIN_STATUS
Definition: osd.h:32
OSDFunctionalType
Definition: osd.h:45
@ kOSDFunctionalType_SubtitleZoomAdjust
Definition: osd.h:51
@ kOSDFunctionalType_AudioSyncAdjust
Definition: osd.h:50
@ kOSDFunctionalType_SubtitleDelayAdjust
Definition: osd.h:52
@ kOSDFunctionalType_SmartForward
Definition: osd.h:48
@ kOSDFunctionalType_Default
Definition: osd.h:46
@ kOSDFunctionalType_TimeStretchAdjust
Definition: osd.h:49
@ kOSDFunctionalType_PictureAdjust
Definition: osd.h:47
static constexpr const char * OSD_DLG_SLEEP
Definition: osd.h:18
static constexpr const char * OSD_DLG_CONFIRM
Definition: osd.h:27
static constexpr const char * OSD_DLG_VIDEOEXIT
Definition: osd.h:16
static constexpr const char * OSD_WIN_INPUT
Definition: osd.h:30
static constexpr const char * OSD_DLG_CUTPOINT
Definition: osd.h:24
static constexpr const char * OSD_DLG_EDITING
Definition: osd.h:21
static constexpr const char * OSD_WIN_PROGEDIT
Definition: osd.h:35
static constexpr const char * OSD_DLG_MENU
Definition: osd.h:17
static constexpr const char * OSD_DLG_IDLE
Definition: osd.h:19
OSDTimeout
Definition: osd.h:56
@ kOSDTimeout_Short
Definition: osd.h:59
@ kOSDTimeout_Long
Definition: osd.h:61
@ kOSDTimeout_None
Definition: osd.h:58
@ kOSDTimeout_Med
Definition: osd.h:60
static constexpr const char * OSD_DLG_ASKALLOW
Definition: osd.h:22
static constexpr const char * OSD_DLG_NAVIGATE
Definition: osd.h:26
static constexpr const char * OSD_WIN_MESSAGE
Definition: osd.h:29
static constexpr const char * OSD_DLG_DELETE
Definition: osd.h:25
@ kPseudoRecording
Definition: playercontext.h:44
@ kPseudoNormalLiveTV
Definition: playercontext.h:42
@ kPseudoChangeChannel
Definition: playercontext.h:43
std::deque< QString > StringDeque
Definition: playercontext.h:47
const QString kPlayerInUseID
const QString kRecorderInUseID
MarkTypes
Definition: programtypes.h:46
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
Definition: programtypes.h:117
AutoExpireType
Definition: programtypes.h:192
@ kLiveTVAutoExpire
Definition: programtypes.h:196
@ kDisableAutoExpire
Definition: programtypes.h:193
@ kNormalAutoExpire
Definition: programtypes.h:194
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:206
std::vector< ProgramInfo * > * RemoteGetRecordedList(int sort)
Definition: remoteutil.cpp:19
static void show(uint8_t *buf, int length)
Definition: ringbuffer.cpp:341
std::vector< SignalMonitorValue > SignalMonitorList
QHash< QString, int > values
Definition: playercontext.h:37
InfoMap text
Definition: playercontext.h:36
#define output
QString toTypeString(PictureAdjustType type)
Definition: tv.cpp:49
QString StateToString(TVState state)
Returns a human readable QString representing a TVState.
Definition: tv.cpp:11
QString toTitleString(PictureAdjustType type)
Definition: tv.cpp:62
CommSkipMode
Definition: tv.h:134
@ kCommSkipIncr
Definition: tv.h:139
@ kCommSkipOff
Definition: tv.h:135
@ BROWSE_RIGHT
Fetch information on current channel in the future.
Definition: tv.h:47
@ BROWSE_LEFT
Fetch information on current channel in the past.
Definition: tv.h:46
@ BROWSE_UP
Fetch information on previous channel.
Definition: tv.h:44
@ BROWSE_FAVORITE
Fetch information on the next favorite channel.
Definition: tv.h:48
@ BROWSE_DOWN
Fetch information on next channel.
Definition: tv.h:45
PictureAdjustType
Definition: tv.h:124
@ kAdjustingPicture_None
Definition: tv.h:125
@ kAdjustingPicture_Playback
Definition: tv.h:126
@ kAdjustingPicture_Recording
Definition: tv.h:128
@ kAdjustingPicture_Channel
Definition: tv.h:127
ChannelChangeDirection
ChannelChangeDirection is an enumeration of possible channel changing directions.
Definition: tv.h:32
@ CHANNEL_DIRECTION_DOWN
Definition: tv.h:34
@ CHANNEL_DIRECTION_FAVORITE
Definition: tv.h:35
@ CHANNEL_DIRECTION_UP
Definition: tv.h:33
TVState
TVState is an enumeration of the states used by TV and TVRec.
Definition: tv.h:54
@ kState_WatchingDVD
Watching DVD is the state when we are watching a DVD.
Definition: tv.h:76
@ kState_None
None State, this is the initial state in both TV and TVRec, it indicates that we are ready to change ...
Definition: tv.h:61
@ kState_RecordingOnly
Recording Only is a TVRec only state for when we are recording a program, but there is no one current...
Definition: tv.h:87
@ kState_WatchingLiveTV
Watching LiveTV is the state for when we are watching a recording and the user has control over the c...
Definition: tv.h:66
@ kState_Error
Error State, if we ever try to enter this state errored is set.
Definition: tv.h:57
@ kState_WatchingPreRecorded
Watching Pre-recorded is a TV only state for when we are watching a pre-existing recording.
Definition: tv.h:70
@ kState_WatchingRecording
Watching Recording is the state for when we are watching an in progress recording,...
Definition: tv.h:83
@ kState_WatchingBD
Watching BD is the state when we are watching a BD.
Definition: tv.h:78
@ kState_ChangingState
This is a placeholder state which we never actually enter, but is returned by GetState() when we are ...
Definition: tv.h:92
@ kState_WatchingVideo
Watching Video is the state when we are watching a video and is not a dvd or BD.
Definition: tv.h:74
#define ACTION_TOGGLESUBS
Definition: tv_actions.h:67
#define ACTION_BOTTOMLINEMOVE
Definition: tv_actions.h:145
#define ACTION_TOGGLEEXTTEXT
Definition: tv_actions.h:72
#define ACTION_MENUBLUE
Definition: tv_actions.h:80
#define ACTION_TOGGLEOSDDEBUG
Definition: tv_actions.h:122
#define ACTION_TOGGLEPGORDER
Definition: tv_actions.h:13
#define ACTION_ENABLEFORCEDSUBS
Definition: tv_actions.h:68
#define ACTION_TOGGLESLEEP
Definition: tv_actions.h:29
#define ACTION_NEXTSUBPAGE
Definition: tv_actions.h:98
#define ACTION_SWITCHANGLE
Definition: tv_actions.h:54
#define ACTION_DISABLEVISUALISATION
Definition: tv_actions.h:119
#define ACTION_JUMPTODVDCHAPTERMENU
Definition: tv_actions.h:50
#define ACTION_PLAY
Definition: tv_actions.h:30
#define ACTION_TOGGLESUBTITLEDELAY
Definition: tv_actions.h:74
#define ACTION_VIEWSCHEDULED
Definition: tv_actions.h:31
#define ACTION_FINDER
Definition: tv_actions.h:27
#define ACTION_ZOOMHORIZONTALIN
Definition: tv_actions.h:141
#define ACTION_TOGGELAUDIOSYNC
Definition: tv_actions.h:113
#define ACTION_ZOOMVERTICALIN
Definition: tv_actions.h:139
#define ACTION_MENUTEXT
Definition: tv_actions.h:82
#define ACTION_MENURED
Definition: tv_actions.h:77
#define ACTION_TOGGLEVISUALISATION
Definition: tv_actions.h:117
#define ACTION_CHANNELUP
Definition: tv_actions.h:16
#define ACTION_DISABLEEXTTEXT
Definition: tv_actions.h:70
#define ACTION_3DIGNORE
Definition: tv_actions.h:126
#define ACTION_TOGGLEBACKGROUND
Definition: tv_actions.h:102
#define ACTION_CAST
Definition: tv_actions.h:149
#define ACTION_ZOOMHORIZONTALOUT
Definition: tv_actions.h:142
#define ACTION_OSDNAVIGATION
Definition: tv_actions.h:55
#define ACTION_PAGERIGHT
Definition: tv_actions.h:12
#define ACTION_CHANNELSEARCH
Definition: tv_actions.h:28
#define ACTION_PREVCUT
Definition: tv_actions.h:91
#define ACTION_SEEKFFWD
Definition: tv_actions.h:43
#define ACTION_JUMPTODVDROOTMENU
Definition: tv_actions.h:48
#define ACTION_ENABLEEXTTEXT
Definition: tv_actions.h:71
#define ACTION_3DNONE
Definition: tv_actions.h:125
#define ACTION_TOGGLERECORD
Definition: tv_actions.h:19
#define ACTION_JUMPSTART
Definition: tv_actions.h:47
#define ACTION_TOGGLEFAV
Definition: tv_actions.h:20
#define ACTION_JUMPFFWD
Definition: tv_actions.h:44
#define ACTION_SETCONTRAST
Definition: tv_actions.h:60
#define ACTION_LISTRECORDEDEPISODES
Definition: tv_actions.h:24
#define ACTION_BIGJUMPREW
Definition: tv_actions.h:92
#define ACTION_TOGGLECHANCONTROLS
Definition: tv_actions.h:21
#define ACTION_DAYLEFT
Definition: tv_actions.h:9
#define ACTION_SETCOLOUR
Definition: tv_actions.h:61
#define ACTION_PAGELEFT
Definition: tv_actions.h:11
#define ACTION_3DTOPANDBOTTOMDISCARD
Definition: tv_actions.h:128
#define ACTION_LOADCOMMSKIP
Definition: tv_actions.h:89
#define ACTION_SETBRIGHTNESS
Definition: tv_actions.h:59
#define ACTION_SETHUE
Definition: tv_actions.h:62
#define ACTION_PREVSUBPAGE
Definition: tv_actions.h:99
#define ACTION_TOGGLESUBTITLEZOOM
Definition: tv_actions.h:73
#define ACTION_PAUSE
Definition: tv_actions.h:15
#define ACTION_ZOOMLEFT
Definition: tv_actions.h:133
#define ACTION_MUTEAUDIO
Definition: tv_actions.h:106
#define ACTION_DISABLEUPMIX
Definition: tv_actions.h:109
#define ACTION_TOGGLEBOOKMARK
Definition: tv_actions.h:35
#define ACTION_ZOOMQUIT
Definition: tv_actions.h:143
#define ACTION_ZOOMASPECTDOWN
Definition: tv_actions.h:136
#define ACTION_JUMPRWND
Definition: tv_actions.h:45
#define ACTION_SWITCHTITLE
Definition: tv_actions.h:53
#define ACTION_MENUEPG
Definition: tv_actions.h:83
#define ACTION_3DSIDEBYSIDEDISCARD
Definition: tv_actions.h:127
#define ACTION_DAYRIGHT
Definition: tv_actions.h:10
#define ACTION_TOGGLETT
Definition: tv_actions.h:100
#define ACTION_DISABLESUBS
Definition: tv_actions.h:66
#define ACTION_SEEKABSOLUTE
Definition: tv_actions.h:40
#define ACTION_JUMPTOPOPUPMENU
Definition: tv_actions.h:49
#define ACTION_ENABLESUBS
Definition: tv_actions.h:65
#define ACTION_SEEKRWND
Definition: tv_actions.h:42
#define ACTION_PREVRECORDED
Definition: tv_actions.h:32
#define ACTION_SAVEMAP
Definition: tv_actions.h:88
#define ACTION_BIGJUMPFWD
Definition: tv_actions.h:93
#define ACTION_ZOOMOUT
Definition: tv_actions.h:138
#define ACTION_REVEAL
Definition: tv_actions.h:103
#define ACTION_JUMPCHAPTER
Definition: tv_actions.h:52
#define ACTION_EXITSHOWNOPROMPTS
Definition: tv_actions.h:4
#define ACTION_ZOOMRIGHT
Definition: tv_actions.h:134
#define ACTION_DISABLEFORCEDSUBS
Definition: tv_actions.h:69
#define ACTION_ZOOMASPECTUP
Definition: tv_actions.h:135
#define ACTION_JUMPBKMRK
Definition: tv_actions.h:46
#define ACTION_ZOOMUP
Definition: tv_actions.h:131
#define ACTION_MENUCOMPACT
Definition: tv_actions.h:6
#define ACTION_SETAUDIOSYNC
Definition: tv_actions.h:114
#define ACTION_ENABLEVISUALISATION
Definition: tv_actions.h:118
#define ACTION_ENABLEUPMIX
Definition: tv_actions.h:108
#define ACTION_PREVPAGE
Definition: tv_actions.h:97
#define ACTION_MENUWHITE
Definition: tv_actions.h:101
#define ACTION_TOGGLEUPMIX
Definition: tv_actions.h:107
#define ACTION_MENUYELLOW
Definition: tv_actions.h:79
#define ACTION_SEEKARB
Definition: tv_actions.h:41
#define ACTION_BOTTOMLINESAVE
Definition: tv_actions.h:146
#define ACTION_STOP
Definition: tv_actions.h:8
#define ACTION_VOLUMEDOWN
Definition: tv_actions.h:111
#define ACTION_JUMPTODVDTITLEMENU
Definition: tv_actions.h:51
#define ACTION_NEXTPAGE
Definition: tv_actions.h:96
#define ACTION_TOGGLERECCONTROLS
Definition: tv_actions.h:22
#define ACTION_ZOOMVERTICALOUT
Definition: tv_actions.h:140
#define ACTION_CLEARMAP
Definition: tv_actions.h:86
#define ACTION_MENUGREEN
Definition: tv_actions.h:78
#define ACTION_SETVOLUME
Definition: tv_actions.h:112
#define ACTION_SIGNALMON
Definition: tv_actions.h:33
#define ACTION_SETBOOKMARK
Definition: tv_actions.h:34
#define ACTION_CHANNELDOWN
Definition: tv_actions.h:17
#define ACTION_ZOOMCOMMIT
Definition: tv_actions.h:144
#define ACTION_PLAYBACK
Definition: tv_actions.h:7
#define ACTION_ZOOMIN
Definition: tv_actions.h:137
#define ACTION_JUMPREC
Definition: tv_actions.h:39
#define ACTION_INVERTMAP
Definition: tv_actions.h:87
#define ACTION_TEXTEXIT
Definition: tv_actions.h:81
#define ACTION_GUIDE
Definition: tv_actions.h:26
#define ACTION_JUMPPREV
Definition: tv_actions.h:38
#define ACTION_VOLUMEUP
Definition: tv_actions.h:110
#define ACTION_ZOOMDOWN
Definition: tv_actions.h:132
#define ACTION_CLEAROSD
Definition: tv_actions.h:14
#define ACTION_NEXTCUT
Definition: tv_actions.h:90
#define LOC
Definition: tv_play.cpp:81
static int comp_season_rev(const ProgramInfo *a, const ProgramInfo *b)
Definition: tv_play.cpp:96
static QString add_spacer(const QString &chan, const QString &spacer)
Definition: tv_play.cpp:5875
#define BUTTON3(action, textActive, textInactive, isMenu)
Definition: tv_play.cpp:8553
#define BUTTON2(action, textActive, textInactive)
Definition: tv_play.cpp:8551
static void insert_map(InfoMap &infoMap, const InfoMap &newMap)
Definition: tv_play.cpp:7959
#define SET_LAST()
Definition: tv_play.cpp:1965
#define SET_NEXT()
Definition: tv_play.cpp:1964
static bool comp_title(const ProgramInfo *a, const ProgramInfo *b)
Definition: tv_play.cpp:107
static QString tv_i18n(const QString &msg)
Definition: tv_play.cpp:1968
static QString toCommaList(const QVector< uint > &list)
Definition: tv_play.cpp:6787
static uint get_chanid(const PlayerContext *ctx, uint cardid, const QString &channum)
Definition: tv_play.cpp:6078
#define BUTTON(action, text)
Definition: tv_play.cpp:8549
static int comp_originalAirDate_rev(const ProgramInfo *a, const ProgramInfo *b)
Definition: tv_play.cpp:83
static bool SysEventHandleAction(MythMainWindow *MainWindow, QKeyEvent *e, const QStringList &actions)
Definition: tv_play.cpp:3209
#define TRANSITION(ASTATE, BSTATE)
Definition: tv_play.cpp:1962
void(*)(const ProgramInfo *, void *) EMBEDRETURNVOIDSCHEDIT
Definition: tv_play.h:63
void(*)(void *, bool) EMBEDRETURNVOID
Definition: tv_play.h:59
@ kViewSchedule
Definition: tv_play.h:98
@ kScheduleProgramList
Definition: tv_play.h:100
@ kScheduleProgramGuide
Definition: tv_play.h:95
@ kScheduleProgramFinder
Definition: tv_play.h:96
@ kPlaybackBox
Definition: tv_play.h:99
@ kScheduledRecording
Definition: tv_play.h:97
void(*)(uint, const QString &, const QDateTime, TV *, bool, bool, int) EMBEDRETURNVOIDEPG
Definition: tv_play.h:60
void(*)(TV *, int, const QString &) EMBEDRETURNVOIDPROGLIST
Definition: tv_play.h:62
void(*)(TV *, bool, bool) EMBEDRETURNVOIDFINDER
Definition: tv_play.h:61
@ kStartTVIgnoreLastPlayPos
Definition: tv_play.h:119
@ kStartTVIgnoreProgStart
Definition: tv_play.h:118
@ kStartTVByNetworkCommand
Definition: tv_play.h:116
@ kStartTVInPlayList
Definition: tv_play.h:115
@ kStartTVIgnoreBookmark
Definition: tv_play.h:117
NoRecorderMsg
Type of message displayed in ShowNoRecorderDialog()
Definition: tv_play.h:107
@ kNoRecorders
No free recorders.
Definition: tv_play.h:108
@ kNoCurrRec
No current recordings.
Definition: tv_play.h:109
@ kNoTuners
No capture cards configured.
Definition: tv_play.h:110
bool RemoteIsBusy(uint inputid, InputInfo &busy_input)
RemoteEncoder * RemoteGetExistingRecorder(const ProgramInfo *pginfo)
RemoteEncoder * RemoteRequestNextFreeRecorder(int inputid)
std::vector< InputInfo > RemoteRequestFreeInputInfo(uint excluded_input)
int RemoteGetFreeRecorderCount(void)
RemoteEncoder * RemoteRequestFreeRecorderFromList(const QStringList &qualifiedRecorders, uint excluded_input)
void RemoteCancelNextRecording(uint inputid, bool cancel)
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:89
PictureAttribute next_picattr(PictureAttributeSupported Supported, PictureAttribute Attribute)
QString GetZoomString(float HorizScale, float VertScale, QPoint Move)
AspectOverrideMode
Definition: videoouttypes.h:61
@ kAspect_16_9
Definition: videoouttypes.h:65
@ kAspect_Off
Definition: videoouttypes.h:63
@ kAspect_14_9
Definition: videoouttypes.h:66
@ kAspect_END
Definition: videoouttypes.h:68
@ kDisplayCC608
Definition: videoouttypes.h:16
@ kDisplayNone
Definition: videoouttypes.h:12
@ kDisplayAVSubtitle
Definition: videoouttypes.h:15
@ kDisplayRawTextSubtitle
Definition: videoouttypes.h:20
@ kDisplayTeletextMenu
Definition: videoouttypes.h:22
@ kDisplayNUVTeletextCaptions
Definition: videoouttypes.h:13
@ kDisplayTextSubtitle
Definition: videoouttypes.h:18
@ kDisplayTeletextCaptions
Definition: videoouttypes.h:14
@ kDisplayCC708
Definition: videoouttypes.h:17
AdjustFillMode
Definition: videoouttypes.h:72
@ kAdjustFill_Off
Definition: videoouttypes.h:74
@ kAdjustFill_AutoDetect_DefaultHalf
Definition: videoouttypes.h:83
@ kAdjustFill_AutoDetect_DefaultOff
Definition: videoouttypes.h:82
@ kAdjustFill_END
Definition: videoouttypes.h:81
PictureAttributeSupported
@ kPictureAttributeSupported_Colour
@ kPictureAttributeSupported_Volume
@ kPictureAttributeSupported_Brightness
@ kPictureAttributeSupported_Hue
@ kPictureAttributeSupported_Contrast
@ kPictureAttributeSupported_None
FrameScanType
Definition: videoouttypes.h:95
@ kScan_Intr2ndField
Definition: videoouttypes.h:99
@ kScan_Interlaced
Definition: videoouttypes.h:98
@ kScan_Detect
Definition: videoouttypes.h:97
@ kScan_Progressive
PictureAttribute
@ kPictureAttribute_MIN
@ kPictureAttribute_Range
@ kPictureAttribute_Contrast
@ kPictureAttribute_Brightness
@ kPictureAttribute_Volume
@ kPictureAttribute_None
@ kPictureAttribute_Colour
@ kPictureAttribute_MAX
@ kPictureAttribute_Hue
QString ScanTypeToUserString(FrameScanType Scan, bool Forced=false)
ZoomDirection
Definition: videoouttypes.h:43
@ kZoomVerticalOut
Definition: videoouttypes.h:48
@ kZoomRight
Definition: videoouttypes.h:54
@ kZoomAspectUp
Definition: videoouttypes.h:55
@ kZoom_END
Definition: videoouttypes.h:57
@ kZoomVerticalIn
Definition: videoouttypes.h:47
@ kZoomHome
Definition: videoouttypes.h:44
@ kZoomUp
Definition: videoouttypes.h:51
@ kZoomDown
Definition: videoouttypes.h:52
@ kZoomLeft
Definition: videoouttypes.h:53
@ kZoomOut
Definition: videoouttypes.h:46
@ kZoomIn
Definition: videoouttypes.h:45
@ kZoomAspectDown
Definition: videoouttypes.h:56
@ kZoomHorizontalIn
Definition: videoouttypes.h:49
@ kZoomHorizontalOut
Definition: videoouttypes.h:50
@ kStereoscopicModeAuto
@ kStereoscopicModeTopAndBottomDiscard
@ kStereoscopicModeSideBySideDiscard
@ kStereoscopicModeIgnore3D
PictureAttributeSupported toMask(PictureAttribute PictureAttribute)
bool OptionalCaptionEnabled(uint Captions)
Return whether any optional captions are enabled.
Definition: videoouttypes.h:30
@ kMuteAll
Definition: volumebase.h:12
State
Definition: zmserver.h:69