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