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 for (std::chrono::seconds chapter : std::as_const(chapters))
1411 var << QVariant((long long)chapter.count());
1412 status.insert("chaptertimes", var);
1413 }
1414
1416 QVariantMap tracks;
1417
1418 QStringList list = m_player->GetTracks(kTrackTypeSubtitle);
1419 int currenttrack = -1;
1420 if (!list.isEmpty() && (kDisplayAVSubtitle == capmode))
1421 currenttrack = m_player->GetTrack(kTrackTypeSubtitle);
1422 for (int i = 0; i < list.size(); i++)
1423 {
1424 if (i == currenttrack)
1425 status.insert("currentsubtitletrack", list[i]);
1426 tracks.insert("SELECTSUBTITLE_" + QString::number(i), list[i]);
1427 }
1428
1430 currenttrack = -1;
1431 if (!list.isEmpty() && (kDisplayTeletextCaptions == capmode))
1433 for (int i = 0; i < list.size(); i++)
1434 {
1435 if (i == currenttrack)
1436 status.insert("currentsubtitletrack", list[i]);
1437 tracks.insert("SELECTTTC_" + QString::number(i), list[i]);
1438 }
1439
1441 currenttrack = -1;
1442 if (!list.isEmpty() && (kDisplayCC708 == capmode))
1443 currenttrack = m_player->GetTrack(kTrackTypeCC708);
1444 for (int i = 0; i < list.size(); i++)
1445 {
1446 if (i == currenttrack)
1447 status.insert("currentsubtitletrack", list[i]);
1448 tracks.insert("SELECTCC708_" + QString::number(i), list[i]);
1449 }
1450
1452 currenttrack = -1;
1453 if (!list.isEmpty() && (kDisplayCC608 == capmode))
1454 currenttrack = m_player->GetTrack(kTrackTypeCC608);
1455 for (int i = 0; i < list.size(); i++)
1456 {
1457 if (i == currenttrack)
1458 status.insert("currentsubtitletrack", list[i]);
1459 tracks.insert("SELECTCC608_" + QString::number(i), list[i]);
1460 }
1461
1463 currenttrack = -1;
1464 if (!list.isEmpty() && (kDisplayRawTextSubtitle == capmode))
1465 currenttrack = m_player->GetTrack(kTrackTypeRawText);
1466 for (int i = 0; i < list.size(); i++)
1467 {
1468 if (i == currenttrack)
1469 status.insert("currentsubtitletrack", list[i]);
1470 tracks.insert("SELECTRAWTEXT_" + QString::number(i), list[i]);
1471 }
1472
1474 {
1475 if (kDisplayTextSubtitle == capmode)
1476 status.insert("currentsubtitletrack", tr("External Subtitles"));
1477 tracks.insert(ACTION_ENABLEEXTTEXT, tr("External Subtitles"));
1478 }
1479
1480 status.insert("totalsubtitletracks", tracks.size());
1481 if (!tracks.isEmpty())
1482 status.insert("subtitletracks", tracks);
1483
1484 tracks.clear();
1486 currenttrack = m_player->GetTrack(kTrackTypeAudio);
1487 for (int i = 0; i < list.size(); i++)
1488 {
1489 if (i == currenttrack)
1490 status.insert("currentaudiotrack", list[i]);
1491 tracks.insert("SELECTAUDIO_" + QString::number(i), list[i]);
1492 }
1493
1494 status.insert("totalaudiotracks", tracks.size());
1495 if (!tracks.isEmpty())
1496 status.insert("audiotracks", tracks);
1497
1498 status.insert("playspeed", m_player->GetPlaySpeed());
1499 status.insert("audiosyncoffset", static_cast<long long>(m_audioState.m_audioOffset.count()));
1500
1502 {
1503 status.insert("volume", m_audioState.m_volume);
1504 status.insert("mute", m_audioState.m_muteState);
1505 }
1506
1509 status.insert("brightness", m_videoColourState.GetValue(kPictureAttribute_Brightness));
1511 status.insert("contrast", m_videoColourState.GetValue(kPictureAttribute_Contrast));
1513 status.insert("colour", m_videoColourState.GetValue(kPictureAttribute_Colour));
1515 status.insert("hue", m_videoColourState.GetValue(kPictureAttribute_Hue));
1516 }
1517 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
1519
1520 for (auto tit =info.text.cbegin(); tit != info.text.cend(); ++tit)
1521 status.insert(tit.key(), tit.value());
1522
1523 for (auto vit = info.values.cbegin(); vit != info.values.cend(); ++vit)
1524 status.insert(vit.key(), vit.value());
1525
1527}
1528
1534bool TV::LiveTV(bool ShowDialogs, const ChannelInfoList &Selection)
1535{
1536 m_requestDelete = false;
1537 m_allowRerecord = false;
1538 m_jumpToProgram = false;
1539
1541 if (m_playerContext.GetState() == kState_None && RequestNextRecorder(ShowDialogs, Selection))
1542 {
1545 m_switchToRec = nullptr;
1546
1547 // Start Idle Timer
1548 if (m_dbIdleTimeout > 0ms)
1549 {
1551 LOG(VB_GENERAL, LOG_INFO, QString("Using Idle Timer. %1 minutes")
1552 .arg(duration_cast<std::chrono::minutes>(m_dbIdleTimeout).count()));
1553 }
1554
1556 return true;
1557 }
1559 return false;
1560}
1561
1562bool TV::RequestNextRecorder(bool ShowDialogs, const ChannelInfoList &Selection)
1563{
1565
1566 RemoteEncoder *testrec = nullptr;
1567 if (m_switchToRec)
1568 {
1569 // If this is set we, already got a new recorder in SwitchCards()
1570 testrec = m_switchToRec;
1571 m_switchToRec = nullptr;
1572 }
1573 else if (!Selection.empty())
1574 {
1575 for (const auto & ci : Selection)
1576 {
1577 uint chanid = ci.m_chanId;
1578 QString channum = ci.m_chanNum;
1579 if (!chanid || channum.isEmpty())
1580 continue;
1581 QVector<uint> cards = IsTunableOn(&m_playerContext, chanid);
1582
1583 if (chanid && !channum.isEmpty() && !cards.isEmpty())
1584 {
1585 testrec = RemoteGetExistingRecorder(static_cast<int>(*(cards.begin())));
1586 m_initialChanID = chanid;
1587 break;
1588 }
1589 }
1590 }
1591 else
1592 {
1593 // When starting LiveTV we just get the next free recorder
1594 testrec = RemoteRequestNextFreeRecorder(-1);
1595 }
1596
1597 if (!testrec)
1598 return false;
1599
1600 if (!testrec->IsValidRecorder())
1601 {
1602 if (ShowDialogs)
1604
1605 delete testrec;
1606
1607 return false;
1608 }
1609
1611
1612 return true;
1613}
1614
1615void TV::AskAllowRecording(const QStringList &Msg, int Timeuntil, bool HasRec, bool HasLater)
1616{
1617 if (!StateIsLiveTV(GetState()))
1618 return;
1619
1620 auto *info = new ProgramInfo(Msg);
1621 if (!info->GetChanID())
1622 {
1623 delete info;
1624 return;
1625 }
1626
1627 QMutexLocker locker(&m_askAllowLock);
1628 QString key = info->MakeUniqueKey();
1629 if (Timeuntil > 0)
1630 {
1631 // add program to list
1632#if 0
1633 LOG(VB_GENERAL, LOG_DEBUG, LOC + "AskAllowRecording -- " +
1634 QString("adding '%1'").arg(info->m_title));
1635#endif
1636 QDateTime expiry = MythDate::current().addSecs(Timeuntil);
1637 m_askAllowPrograms[key] = AskProgramInfo(expiry, HasRec, HasLater, info);
1638 }
1639 else
1640 {
1641 // remove program from list
1642 LOG(VB_GENERAL, LOG_INFO, LOC + "-- " +
1643 QString("removing '%1'").arg(info->GetTitle()));
1644 QMap<QString,AskProgramInfo>::iterator it = m_askAllowPrograms.find(key);
1645 if (it != m_askAllowPrograms.end())
1646 {
1647 delete (*it).m_info;
1648 m_askAllowPrograms.erase(it);
1649 }
1650 delete info;
1651 }
1652
1654}
1655
1657{
1658 QMutexLocker locker(&m_askAllowLock);
1660 return;
1661
1662 uint cardid = m_playerContext.GetCardID();
1663
1664 QString single_rec = tr("MythTV wants to record \"%1\" on %2 in %d seconds. Do you want to:");
1665
1666 QString record_watch = tr("Record and watch while it records");
1667 QString let_record1 = tr("Let it record and go back to the Main Menu");
1668 QString let_recordm = tr("Let them record and go back to the Main Menu");
1669 QString record_later1 = tr("Record it later, I want to watch TV");
1670 QString record_laterm = tr("Record them later, I want to watch TV");
1671 QString do_not_record1= tr("Don't let it record, I want to watch TV");
1672 QString do_not_recordm= tr("Don't let them record, I want to watch TV");
1673
1674 // eliminate timed out programs
1675 QDateTime timeNow = MythDate::current();
1676 QMap<QString,AskProgramInfo>::iterator it = m_askAllowPrograms.begin();
1677 while (it != m_askAllowPrograms.end())
1678 {
1679 if ((*it).m_expiry <= timeNow)
1680 {
1681#if 0
1682 LOG(VB_GENERAL, LOG_DEBUG, LOC + "-- " +
1683 QString("removing '%1'").arg((*it).m_info->m_title));
1684#endif
1685 delete (*it).m_info;
1686 it = m_askAllowPrograms.erase(it);
1687 }
1688 else
1689 {
1690 it++;
1691 }
1692 }
1693 std::chrono::milliseconds timeuntil = 0ms;
1694 QString message;
1695 uint conflict_count = static_cast<uint>(m_askAllowPrograms.size());
1696
1697 it = m_askAllowPrograms.begin();
1698 if ((1 == m_askAllowPrograms.size()) && ((*it).m_info->GetInputID() == cardid))
1699 {
1700 (*it).m_isInSameInputGroup = (*it).m_isConflicting = true;
1701 }
1702 else if (!m_askAllowPrograms.empty())
1703 {
1704 // get the currently used input on our card
1705 bool busy_input_grps_loaded = false;
1706 std::vector<uint> busy_input_grps;
1707 InputInfo busy_input;
1708 RemoteIsBusy(cardid, busy_input);
1709
1710 // check if current input can conflict
1711 for (it = m_askAllowPrograms.begin(); it != m_askAllowPrograms.end(); ++it)
1712 {
1713 (*it).m_isInSameInputGroup =
1714 (cardid == (*it).m_info->GetInputID());
1715
1716 if ((*it).m_isInSameInputGroup)
1717 continue;
1718
1719 // is busy_input in same input group as recording
1720 if (!busy_input_grps_loaded)
1721 {
1722 busy_input_grps = CardUtil::GetInputGroups(busy_input.m_inputId);
1723 busy_input_grps_loaded = true;
1724 }
1725
1726 std::vector<uint> input_grps =
1727 CardUtil::GetInputGroups((*it).m_info->GetInputID());
1728
1729 for (uint grp : input_grps)
1730 {
1731#ifdef __cpp_lib_ranges_contains
1732 if (std::ranges::contains(busy_input_grps, grp))
1733#else
1734 if (std::ranges::find(busy_input_grps,
1735 grp) != busy_input_grps.end())
1736#endif
1737 {
1738 (*it).m_isInSameInputGroup = true;
1739 break;
1740 }
1741 }
1742 }
1743
1744 // check if inputs that can conflict are ok
1745 conflict_count = 0;
1746 for (it = m_askAllowPrograms.begin(); it != m_askAllowPrograms.end(); ++it)
1747 {
1748 if (!(*it).m_isInSameInputGroup)
1749 (*it).m_isConflicting = false; // NOLINT(bugprone-branch-clone)
1750 else if (cardid == (*it).m_info->GetInputID())
1751 (*it).m_isConflicting = true; // NOLINT(bugprone-branch-clone)
1752 else if (!CardUtil::IsTunerShared(cardid, (*it).m_info->GetInputID()))
1753 (*it).m_isConflicting = true;
1754 else if ((busy_input.m_mplexId &&
1755 (busy_input.m_mplexId == (*it).m_info->QueryMplexID())) ||
1756 (!busy_input.m_mplexId &&
1757 (busy_input.m_chanId == (*it).m_info->GetChanID())))
1758 (*it).m_isConflicting = false;
1759 else
1760 (*it).m_isConflicting = true;
1761
1762 conflict_count += (*it).m_isConflicting ? 1 : 0;
1763 }
1764 }
1765
1766 it = m_askAllowPrograms.begin();
1767 for (; it != m_askAllowPrograms.end() && !(*it).m_isConflicting; ++it);
1768
1769 if (conflict_count == 0)
1770 {
1771 LOG(VB_GENERAL, LOG_INFO, LOC + "The scheduler wants to make "
1772 "a non-conflicting recording.");
1773 // TODO take down mplexid and inform user of problem
1774 // on channel changes.
1775 }
1776 else if (conflict_count == 1 && ((*it).m_info->GetInputID() == cardid))
1777 {
1778#if 0
1779 LOG(VB_GENERAL, LOG_DEBUG, LOC + "UpdateOSDAskAllowDialog -- " +
1780 "kAskAllowOneRec");
1781#endif
1782
1783 it = m_askAllowPrograms.begin();
1784
1785 QString channel = m_dbChannelFormat;
1786 channel
1787 .replace("<num>", (*it).m_info->GetChanNum())
1788 .replace("<sign>", (*it).m_info->GetChannelSchedulingID())
1789 .replace("<name>", (*it).m_info->GetChannelName());
1790
1791 message = single_rec.arg((*it).m_info->GetTitle(), channel);
1792
1793 BrowseEnd(false);
1794 timeuntil = MythDate::secsInFuture((*it).m_expiry);
1796 .m_message=message,
1797 .m_timeout=timeuntil };
1798 dialog.m_buttons.push_back({ record_watch, "DIALOG_ASKALLOW_WATCH_0", false, !((*it).m_hasRec)} );
1799 dialog.m_buttons.push_back({ let_record1, "DIALOG_ASKALLOW_EXIT_0" });
1800 dialog.m_buttons.push_back({ ((*it).m_hasLater) ? record_later1 : do_not_record1,
1801 "DIALOG_ASKALLOW_CANCELRECORDING_0", false, ((*it).m_hasRec) });
1802 emit ChangeOSDDialog(dialog);
1803 }
1804 else
1805 {
1806 if (conflict_count > 1)
1807 {
1808 message = tr(
1809 "MythTV wants to record these programs in %d seconds:");
1810 message += "\n";
1811 }
1812
1813 bool has_rec = false;
1814 for (it = m_askAllowPrograms.begin(); it != m_askAllowPrograms.end(); ++it)
1815 {
1816 if (!(*it).m_isConflicting)
1817 continue;
1818
1819 QString title = (*it).m_info->GetTitle();
1820 if ((title.length() < 10) && !(*it).m_info->GetSubtitle().isEmpty())
1821 title += ": " + (*it).m_info->GetSubtitle();
1822 if (title.length() > 20)
1823 title = title.left(17) + "...";
1824
1825 QString channel = m_dbChannelFormat;
1826 channel
1827 .replace("<num>", (*it).m_info->GetChanNum())
1828 .replace("<sign>", (*it).m_info->GetChannelSchedulingID())
1829 .replace("<name>", (*it).m_info->GetChannelName());
1830
1831 if (conflict_count > 1)
1832 {
1833 message += tr("\"%1\" on %2").arg(title, channel);
1834 message += "\n";
1835 }
1836 else
1837 {
1838 message = single_rec.arg((*it).m_info->GetTitle(), channel);
1839 has_rec = (*it).m_hasRec;
1840 }
1841 }
1842
1843 if (conflict_count > 1)
1844 {
1845 message += "\n";
1846 message += tr("Do you want to:");
1847 }
1848
1849 bool all_have_later = true;
1850 timeuntil = 9999999ms;
1851 for (it = m_askAllowPrograms.begin(); it != m_askAllowPrograms.end(); ++it)
1852 {
1853 if ((*it).m_isConflicting)
1854 {
1855 all_have_later &= (*it).m_hasLater;
1856 auto tmp = std::chrono::milliseconds(MythDate::secsInFuture((*it).m_expiry));
1857 timeuntil = std::clamp(tmp, 0ms, timeuntil);
1858 }
1859 }
1860 timeuntil = (9999999ms == timeuntil) ? 0ms : timeuntil;
1861
1862 if (conflict_count > 1)
1863 {
1864 BrowseEnd(false);
1865 emit ChangeOSDDialog(
1866 { .m_dialogName=OSD_DLG_ASKALLOW,
1867 .m_message=message,
1868 .m_timeout=timeuntil,
1869 .m_buttons={
1870 { let_recordm, "DIALOG_ASKALLOW_EXIT_0", false, true },
1871 { all_have_later ? record_laterm : do_not_recordm, "DIALOG_ASKALLOW_CANCELCONFLICTING_0" }
1872 }});
1873 }
1874 else
1875 {
1876 BrowseEnd(false);
1877 emit ChangeOSDDialog(
1878 {.m_dialogName=OSD_DLG_ASKALLOW,
1879 .m_message=message,
1880 .m_timeout=timeuntil,
1881 .m_buttons={
1882 { let_record1, "DIALOG_ASKALLOW_EXIT_0", false, !has_rec},
1883 { all_have_later ? record_later1 : do_not_record1, "DIALOG_ASKALLOW_CANCELRECORDING_0", false, has_rec}
1884 }});
1885 }
1886 }
1887}
1888
1889void TV::HandleOSDAskAllow(const QString& Action)
1890{
1892 return;
1893
1894 if (!m_askAllowLock.tryLock())
1895 {
1896 LOG(VB_GENERAL, LOG_ERR, "allowrecordingbox : askAllowLock is locked");
1897 return;
1898 }
1899
1900 if (Action == "CANCELRECORDING")
1901 {
1904 }
1905 else if (Action == "CANCELCONFLICTING")
1906 {
1907 for (const auto& pgm : std::as_const(m_askAllowPrograms))
1908 {
1909 if (pgm.m_isConflicting)
1910 RemoteCancelNextRecording(pgm.m_info->GetInputID(), true);
1911 }
1912 }
1913 else if (Action == "WATCH")
1914 {
1917 }
1918 else // if (action == "EXIT")
1919 {
1920 PrepareToExitPlayer(__LINE__);
1921 SetExitPlayer(true, true);
1922 }
1923
1924 m_askAllowLock.unlock();
1925}
1926
1928{
1929 m_wantsToQuit = false;
1930 m_jumpToProgram = false;
1931 m_allowRerecord = false;
1932 m_requestDelete = false;
1934
1937 {
1939 return 0;
1940 }
1941
1943
1947
1949
1950 if (LCD *lcd = LCD::Get())
1951 {
1952 lcd->switchToChannel(ProgInfo.GetChannelSchedulingID(), ProgInfo.GetTitle(), ProgInfo.GetSubtitle());
1953 lcd->setFunctionLEDs((ProgInfo.IsRecording())?FUNC_TV:FUNC_MOVIE, true);
1954 }
1955
1956 return 1;
1957}
1958
1960{
1962}
1963
1965{
1966 return (State == kState_WatchingPreRecorded ||
1971}
1972
1974{
1975 return (State == kState_WatchingLiveTV);
1976}
1977
1978// NOLINTBEGIN(cppcoreguidelines-macro-usage)
1979#define TRANSITION(ASTATE,BSTATE) ((ctxState == (ASTATE)) && (desiredNextState == (BSTATE)))
1980
1981#define SET_NEXT() do { nextState = desiredNextState; changed = true; } while(false)
1982#define SET_LAST() do { nextState = ctxState; changed = true; } while(false)
1983// NOLINTEND(cppcoreguidelines-macro-usage)
1984
1985static QString tv_i18n(const QString &msg)
1986{
1987 QByteArray msg_arr = msg.toLatin1();
1988 QString msg_i18n = TV::tr(msg_arr.constData());
1989 QByteArray msg_i18n_arr = msg_i18n.toLatin1();
1990 return (msg_arr == msg_i18n_arr) ? msg_i18n : msg;
1991}
1992
2002{
2004 {
2005 LOG(VB_GENERAL, LOG_ERR, LOC + "Called after fatal error detected.");
2006 return;
2007 }
2008
2009 bool changed = false;
2010
2012 TVState nextState = m_playerContext.GetState();
2013 if (m_playerContext.m_nextState.empty())
2014 {
2015 LOG(VB_GENERAL, LOG_WARNING, LOC + "Warning, called with no state to change to.");
2017 return;
2018 }
2019
2020 TVState ctxState = m_playerContext.GetState();
2021 TVState desiredNextState = m_playerContext.DequeueNextState();
2022
2023 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Attempting to change from %1 to %2")
2024 .arg(StateToString(nextState), StateToString(desiredNextState)));
2025
2026 if (desiredNextState == kState_Error)
2027 {
2028 LOG(VB_GENERAL, LOG_ERR, LOC + "Attempting to set to an error state!");
2029 SetErrored();
2031 return;
2032 }
2033
2034 bool ok = false;
2036 {
2038
2040
2041 QDateTime timerOffTime = MythDate::current();
2042 m_lockTimerOn = false;
2043
2044 SET_NEXT();
2045
2046 uint chanid = m_initialChanID;
2047 if (!chanid)
2048 chanid = static_cast<uint>(gCoreContext->GetNumSetting("DefaultChanid", 0));
2049
2050 if (chanid && !IsTunablePriv(chanid))
2051 chanid = 0;
2052
2053 QString channum = "";
2054
2055 if (chanid)
2056 {
2057 QStringList reclist;
2058
2060 query.prepare("SELECT channum FROM channel "
2061 "WHERE chanid = :CHANID");
2062 query.bindValue(":CHANID", chanid);
2063 if (query.exec() && query.isActive() && query.size() > 0 && query.next())
2064 channum = query.value(0).toString();
2065 else
2066 channum = QString::number(chanid);
2067
2069 QString::number(chanid));
2070
2071 if (getit)
2072 reclist = ChannelUtil::GetValidRecorderList(chanid, channum);
2073
2074 if (!reclist.empty())
2075 {
2076 RemoteEncoder *testrec = RemoteRequestFreeRecorderFromList(reclist, 0);
2077 if (testrec && testrec->IsValidRecorder())
2078 {
2081 }
2082 else
2083 {
2084 delete testrec; // If testrec isn't a valid recorder ...
2085 }
2086 }
2087 else if (getit)
2088 {
2089 chanid = 0;
2090 }
2091 }
2092
2093 LOG(VB_GENERAL, LOG_DEBUG, LOC + "Spawning LiveTV Recorder -- begin");
2094
2095 if (chanid && !channum.isEmpty())
2097 else
2099
2100 LOG(VB_GENERAL, LOG_DEBUG, LOC + "Spawning LiveTV Recorder -- end");
2101
2103 {
2104 LOG(VB_GENERAL, LOG_ERR, LOC + "LiveTV not successfully started");
2107 SetErrored();
2108 SET_LAST();
2109 }
2110 else
2111 {
2112 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
2113 QString playbackURL = m_playerContext.m_playingInfo->GetPlaybackURL(true);
2114 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
2115
2116 bool opennow = (m_playerContext.m_tvchain->GetInputType(-1) != "DUMMY");
2117
2118 LOG(VB_GENERAL, LOG_INFO, LOC +
2119 QString("playbackURL(%1) inputtype(%2)")
2120 .arg(playbackURL, m_playerContext.m_tvchain->GetInputType(-1)));
2121
2124 playbackURL, false, true,
2125 opennow ? MythMediaBuffer::kLiveTVOpenTimeout : -1ms));
2126
2129 }
2130
2131
2133 {
2134 ok = StartPlayer(desiredNextState);
2135 }
2136 if (!ok)
2137 {
2138 LOG(VB_GENERAL, LOG_ERR, LOC + "LiveTV not successfully started");
2141 SetErrored();
2142 SET_LAST();
2143 }
2144 else
2145 {
2146 if (!m_lastLockSeenTime.isValid() ||
2147 (m_lastLockSeenTime < timerOffTime))
2148 {
2149 m_lockTimer.start();
2150 m_lockTimerOn = true;
2151 }
2152 }
2153 }
2155 {
2156 SET_NEXT();
2158 StopStuff(true, true, true);
2159 }
2165 {
2166 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
2167 QString playbackURL = m_playerContext.m_playingInfo->GetPlaybackURL(true);
2168 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
2169
2170 MythMediaBuffer *buffer = MythMediaBuffer::Create(playbackURL, false);
2171 if (buffer && !buffer->GetLastError().isEmpty())
2172 {
2173 ShowNotificationError(tr("Can't start playback"),
2174 TV::tr( "TV Player" ), buffer->GetLastError());
2175 delete buffer;
2176 buffer = nullptr;
2177 }
2179
2181 {
2182 if (desiredNextState == kState_WatchingRecording)
2183 {
2184 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
2186 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
2187
2189
2192 {
2193 LOG(VB_GENERAL, LOG_ERR, LOC +
2194 "Couldn't find recorder for in-progress recording");
2195 desiredNextState = kState_WatchingPreRecorded;
2197 }
2198 else
2199 {
2201 }
2202 }
2203
2204 ok = StartPlayer(desiredNextState);
2205
2206 if (ok)
2207 {
2208 SET_NEXT();
2209
2210 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
2212 {
2213 QString message = "COMMFLAG_REQUEST ";
2215 gCoreContext->SendMessage(message);
2216 }
2217 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
2218 }
2219 }
2220
2221 if (!ok)
2222 {
2223 SET_LAST();
2224 SetErrored();
2226 {
2228 TV::tr( "TV Player" ),
2229 playbackURL);
2230 // We're going to display this error as notification
2231 // no need to display it later as popup
2233 }
2234 }
2235 }
2241 {
2242 SET_NEXT();
2244 StopStuff(true, true, false);
2245 }
2248 {
2249 SET_NEXT();
2250 }
2251
2252 // Print state changed message...
2253 if (!changed)
2254 {
2255 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unknown state transition: %1 to %2")
2256 .arg(StateToString(m_playerContext.GetState()), StateToString(desiredNextState)));
2257 }
2258 else if (m_playerContext.GetState() != nextState)
2259 {
2260 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Changing from %1 to %2")
2262 }
2263
2264 // update internal state variable
2265 TVState lastState = m_playerContext.GetState();
2266 m_playerContext.m_playingState = nextState;
2268
2270 {
2271 LOG(VB_GENERAL, LOG_INFO, LOC + "State is LiveTV");
2273 LOG(VB_GENERAL, LOG_INFO, LOC + "UpdateOSDInput done");
2274 UpdateLCD();
2275 LOG(VB_GENERAL, LOG_INFO, LOC + "UpdateLCD done");
2276 ITVRestart(true);
2277 LOG(VB_GENERAL, LOG_INFO, LOC + "ITVRestart done");
2278 }
2279 else if (StateIsPlaying(m_playerContext.GetState()) && lastState == kState_None)
2280 {
2281 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
2282 int count = PlayGroup::GetCount();
2283 QString msg = tr("%1 Settings")
2285 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
2286 if (count > 0)
2287 emit ChangeOSDMessage(msg);
2288 ITVRestart(false);
2289 }
2290
2292 {
2293 UpdateLCD();
2294 }
2295
2298
2304
2308
2310 {
2312 }
2313
2320 {
2322 // m_playerBounds is not applicable when switching modes so
2323 // skip this logic in that case.
2324 if (!m_dbUseVideoModes)
2326
2327 if (!m_weDisabledGUI)
2328 {
2329 m_weDisabledGUI = true;
2331 }
2332 // we no longer need the contents of myWindow
2333 if (m_myWindow)
2335
2336 LOG(VB_GENERAL, LOG_INFO, LOC + "Main UI disabled.");
2337 }
2338
2339 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + " -- end");
2340}
2341
2342#undef TRANSITION
2343#undef SET_NEXT
2344#undef SET_LAST
2345
2351bool TV::StartRecorder(std::chrono::milliseconds MaxWait)
2352{
2354 MaxWait = (MaxWait <= 0ms) ? 40s : MaxWait;
2355 MythTimer t;
2356 t.start();
2357 bool recording = false;
2358 bool ok = true;
2359 if (!rec)
2360 {
2361 LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid Remote Encoder");
2362 SetErrored();
2363 return false;
2364 }
2365 while (!(recording = rec->IsRecording(&ok)) && !m_exitPlayerTimerId && t.elapsed() < MaxWait)
2366 {
2367 if (!ok)
2368 {
2369 LOG(VB_GENERAL, LOG_ERR, LOC + "Lost contact with backend");
2370 SetErrored();
2371 return false;
2372 }
2373 std::this_thread::sleep_for(5us);
2374 }
2375
2376 if (!recording || m_exitPlayerTimerId)
2377 {
2379 LOG(VB_GENERAL, LOG_ERR, LOC + "Timed out waiting for recorder to start");
2380 return false;
2381 }
2382
2383 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Took %1 ms to start recorder.")
2384 .arg(t.elapsed().count()));
2385 return true;
2386}
2387
2401void TV::StopStuff(bool StopRingBuffer, bool StopPlayer, bool StopRecorder)
2402{
2403 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "-- begin");
2404
2405 emit PlaybackExiting(this);
2406
2409
2410 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
2411 if (StopPlayer)
2413 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
2414
2415 if (StopRingBuffer)
2416 {
2417 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Stopping ring buffer");
2419 {
2423 }
2424 }
2425
2426 if (StopRecorder)
2427 {
2428 LOG(VB_PLAYBACK, LOG_INFO, LOC + "stopping recorder");
2431 }
2432
2433 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "-- end");
2434}
2435
2436void TV::timerEvent(QTimerEvent *Event)
2437{
2438 const int timer_id = Event->timerId();
2439
2441 bool errored = m_playerContext.IsErrored();
2443 if (errored)
2444 return;
2445
2446 bool handled = true;
2447 if (timer_id == m_lcdTimerId)
2449 else if (timer_id == m_lcdVolumeTimerId)
2451 else if (timer_id == m_sleepTimerId)
2452 ShowOSDSleep();
2453 else if (timer_id == m_sleepDialogTimerId)
2455 else if (timer_id == m_idleTimerId)
2456 ShowOSDIdle();
2457 else if (timer_id == m_idleDialogTimerId)
2459 else if (timer_id == m_endOfPlaybackTimerId)
2461 else if (timer_id == m_endOfRecPromptTimerId)
2463 else if (timer_id == m_videoExitDialogTimerId)
2465 else if (timer_id == m_pseudoChangeChanTimerId)
2467 else if (timer_id == m_speedChangeTimerId)
2469 else if (timer_id == m_saveLastPlayPosTimerId)
2471 else
2472 handled = false;
2473
2474 if (handled)
2475 return;
2476
2477 // Check if it matches a signalMonitorTimerId
2478 if (timer_id == m_signalMonitorTimerId)
2479 {
2483 if (!m_playerContext.m_lastSignalMsg.empty())
2484 {
2485 // set last signal msg, so we get some feedback...
2488 }
2491 return;
2492 }
2493
2494 // Check if it matches networkControlTimerId
2495 QString netCmd;
2496 if (timer_id == m_networkControlTimerId)
2497 {
2498 if (!m_networkControlCommands.empty())
2500 if (m_networkControlCommands.empty())
2501 {
2504 }
2505 }
2506
2507 if (!netCmd.isEmpty())
2508 {
2512 handled = true;
2513 }
2514
2515 if (handled)
2516 return;
2517
2518 // Check if it matches exitPlayerTimerId
2519 if (timer_id == m_exitPlayerTimerId)
2520 {
2522 emit DialogQuit();
2523 emit HideAll();
2524
2526 {
2528 {
2529 emit ChangeOSDMessage(tr("Last Program: \"%1\" Doesn't Exist")
2530 .arg(m_lastProgram->GetTitle()));
2531 lastProgramStringList.clear();
2532 SetLastProgram(nullptr);
2533 LOG(VB_PLAYBACK, LOG_ERR, LOC + "Last Program File does not exist");
2534 m_jumpToProgram = false;
2535 }
2536 else
2537 {
2539 }
2540 }
2541 else
2542 {
2544 }
2545
2547
2550 handled = true;
2551 }
2552
2553 if (handled)
2554 return;
2555
2556 if (timer_id == m_ccInputTimerId)
2557 {
2559 // Clear closed caption input mode when timer expires
2560 if (m_ccInputMode)
2561 {
2562 m_ccInputMode = false;
2563 ClearInputQueues(true);
2564 }
2566
2568 m_ccInputTimerId = 0;
2569 handled = true;
2570 }
2571
2572 if (handled)
2573 return;
2574
2575 if (timer_id == m_asInputTimerId)
2576 {
2578 // Clear closed caption input mode when timer expires
2579 if (m_asInputMode)
2580 {
2581 m_asInputMode = false;
2582 ClearInputQueues(true);
2583 }
2585
2587 m_asInputTimerId = 0;
2588 handled = true;
2589 }
2590
2591 if (handled)
2592 return;
2593
2594 if (timer_id == m_queueInputTimerId)
2595 {
2597 // Commit input when the OSD fades away
2598 if (HasQueuedChannel())
2599 {
2600 OSD *osd = GetOSDL();
2601 if (osd && !osd->IsWindowVisible(OSD_WIN_INPUT))
2602 {
2603 ReturnOSDLock();
2605 }
2606 else
2607 {
2608 ReturnOSDLock();
2609 }
2610 }
2612
2614 {
2617 }
2618 handled = true;
2619 }
2620
2621 if (handled)
2622 return;
2623
2624 if (timer_id == m_browseTimerId)
2625 {
2627 BrowseEnd(false);
2629 handled = true;
2630 }
2631
2632 if (handled)
2633 return;
2634
2635 if (timer_id == m_errorRecoveryTimerId)
2636 {
2640 {
2641 SetExitPlayer(true, false);
2643 }
2645
2649 return;
2650 }
2651
2652 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Unknown timer: %1").arg(timer_id));
2653}
2654
2656{
2658 LCD *lcd = LCD::Get();
2659 if (lcd)
2660 {
2661 float progress = 0.0F;
2662 QString lcd_time_string;
2663 bool showProgress = true;
2664
2665 if (StateIsLiveTV(GetState()))
2667
2669 {
2672 }
2673
2674 if (showProgress)
2675 {
2676 osdInfo info;
2678 progress = info.values["position"] * 0.001F;
2679
2680 lcd_time_string = info.text["playedtime"] + " / " + info.text["totaltime"];
2681 // if the string is longer than the LCD width, remove all spaces
2682 if (lcd_time_string.length() > lcd->getLCDWidth())
2683 lcd_time_string.remove(' ');
2684 }
2685 }
2686 lcd->setChannelProgress(lcd_time_string, progress);
2687 }
2689
2691 m_lcdTimerId = StartTimer(kLCDTimeout, __LINE__);
2692
2693 return true;
2694}
2695
2697{
2699 LCD *lcd = LCD::Get();
2700 if (lcd)
2701 {
2704 }
2706
2709}
2710
2711int TV::StartTimer(std::chrono::milliseconds Interval, int Line)
2712{
2713 int timer = startTimer(Interval);
2714 if (!timer)
2715 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to start timer on line %1 of %2").arg(Line).arg(__FILE__));
2716 return timer;
2717}
2718
2719void TV::KillTimer(int Id)
2720{
2721 killTimer(Id);
2722}
2723
2725{
2728}
2729
2731{
2732 auto StateChange = [&]()
2733 {
2735 if (!m_playerContext.m_nextState.empty())
2736 {
2740 {
2744 m_player = nullptr;
2745 }
2746 }
2748 };
2749
2750 QTimer::singleShot(0, this, StateChange);
2751}
2752
2754{
2755 auto InputChange = [&]()
2756 {
2759 {
2760 uint tmp = m_switchToInputId;
2762 SwitchInputs(0, QString(), tmp);
2763 }
2765 };
2766
2767 QTimer::singleShot(0, this, InputChange);
2768}
2769
2771{
2774 m_errorRecoveryTimerId = StartTimer(1ms, __LINE__);
2775}
2776
2778{
2779 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Switching to program: %1")
2780 .arg(ProgInfo.toString(ProgramInfo::kTitleSubtitle)));
2782 PrepareToExitPlayer(__LINE__);
2783 m_jumpToProgram = true;
2784 SetExitPlayer(true, true);
2785}
2786
2788{
2789 m_playerContext.LockDeletePlayer(__FILE__, Line);
2791 {
2792 // Clear last play position when we're at the end of a recording.
2793 // unless the recording is in-progress.
2794 bool at_end = !StateIsRecording(m_playerContext.GetState()) &&
2796
2797 // Clear last play position on exit when the user requested this
2798 if (m_clearPosOnExit)
2799 {
2800 at_end = true;
2801 }
2802
2803 // Save total frames for video file if not already present
2805 {
2806 auto totalFrames = m_playerContext.m_playingInfo->QueryTotalFrames();
2807 if (!totalFrames)
2808 {
2811 }
2812 }
2813
2814 // Clear/Save play position without notification
2815 // The change must be broadcast when file is no longer in use
2816 // to update previews, ie. with the MarkNotInUse notification
2817 uint64_t frame = at_end ? 0 : m_playerContext.m_player->GetFramesPlayed();
2819 emit UpdateLastPlayPosition(frame);
2822 }
2823 m_playerContext.UnlockDeletePlayer(__FILE__, Line);
2824}
2825
2826void TV::SetExitPlayer(bool SetIt, bool WantsTo)
2827{
2828 if (SetIt)
2829 {
2830 m_wantsToQuit = WantsTo;
2832 m_exitPlayerTimerId = StartTimer(1ms, __LINE__);
2833 }
2834 else
2835 {
2839 m_wantsToQuit = WantsTo;
2840 }
2841}
2842
2844{
2848
2849 bool is_playing = false;
2851 if (StateIsPlaying(GetState()))
2852 {
2854 {
2855 is_playing = true;
2856 }
2857 // If the end of playback is destined to pop up the end of
2858 // recording delete prompt, then don't exit the player here.
2859 else if (GetState() != kState_WatchingPreRecorded ||
2861 {
2863 m_endOfRecording = true;
2864 PrepareToExitPlayer(__LINE__);
2865 SetExitPlayer(true, true);
2866 }
2867 }
2869
2870 if (is_playing)
2872}
2873
2875{
2878 {
2879 return;
2880 }
2881
2883 OSD *osd = GetOSDL();
2884 if (osd && osd->DialogVisible())
2885 {
2886 ReturnOSDLock();
2888 return;
2889 }
2890 ReturnOSDLock();
2891
2892 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
2893 bool do_prompt = (m_playerContext.GetState() == kState_WatchingPreRecorded &&
2895 !m_player->IsPlaying());
2896 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
2897
2898 if (do_prompt)
2899 ShowOSDPromptDeleteRecording(tr("End Of Recording"));
2900
2902}
2903
2905{
2909
2910 // disable dialog and exit playback after timeout
2912 OSD *osd = GetOSDL();
2913 if (!osd || !osd->DialogVisible(OSD_DLG_VIDEOEXIT))
2914 {
2915 ReturnOSDLock();
2917 return;
2918 }
2919 ReturnOSDLock();
2920 DoTogglePause(true);
2921 ClearOSD();
2922 PrepareToExitPlayer(__LINE__);
2924
2925 m_requestDelete = false;
2926 SetExitPlayer(true, true);
2927}
2928
2930{
2933
2934 bool restartTimer = false;
2937 {
2939 {
2940 restartTimer = true;
2941 }
2942 else
2943 {
2944 LOG(VB_CHANNEL, LOG_INFO, "REC_PROGRAM -- channel change");
2945
2947 QString channum = m_playerContext.m_pseudoLiveTVRec->GetChanNum();
2949
2951 ChangeChannel(chanid, channum);
2954 }
2955 }
2957
2958 if (restartTimer)
2960 m_pseudoChangeChanTimerId = StartTimer(25ms, __LINE__);
2961}
2962
2963void TV::SetSpeedChangeTimer(std::chrono::milliseconds When, int Line)
2964{
2967 m_speedChangeTimerId = StartTimer(When, Line);
2968}
2969
2971{
2975
2979 if (update_msg)
2982}
2983
3007bool TV::eventFilter(QObject* Object, QEvent* Event)
3008{
3009 // We want to intercept all resize events sent to the main window
3010 if ((Event->type() == QEvent::Resize))
3011 return (m_mainWindow != Object) ? false : event(Event);
3012
3013 // Intercept keypress events unless they need to be handled by a main UI
3014 // screen (e.g. GuideGrid, ProgramFinder)
3015
3016 if ( (QEvent::KeyPress == Event->type() || QEvent::KeyRelease == Event->type())
3018 return TVPlaybackState::eventFilter(Object, Event);
3019
3020 QScopedPointer<QEvent> sNewEvent(nullptr);
3021 if (m_mainWindow->KeyLongPressFilter(&Event, sNewEvent))
3022 return true;
3023
3024 if (QEvent::KeyPress == Event->type())
3025 return event(Event);
3026
3027 if (MythGestureEvent::kEventType == Event->type())
3028 return m_ignoreKeyPresses ? false : event(Event);
3029
3030 if (Event->type() == MythEvent::kMythEventMessage ||
3034 {
3036 return true;
3037 }
3038
3039 switch (Event->type())
3040 {
3041 case QEvent::Paint:
3042 case QEvent::UpdateRequest:
3043 case QEvent::Enter:
3044 {
3045 event(Event);
3046 return TVPlaybackState::eventFilter(Object, Event);
3047 }
3048 default:
3049 return TVPlaybackState::eventFilter(Object, Event);
3050 }
3051}
3052
3054bool TV::event(QEvent* Event)
3055{
3056 if (Event == nullptr)
3057 return TVPlaybackState::event(Event);
3058
3059 if (QEvent::Resize == Event->type())
3060 {
3061 // These events probably aren't received by a direct call from
3062 // the Qt event dispacther, but are received by way of the event
3063 // dispatcher calling TV::eventFilter(MainWindow, Event).
3064 const auto *qre = dynamic_cast<const QResizeEvent*>(Event);
3065 if (qre)
3066 emit WindowResized(qre->size());
3067 return TVPlaybackState::event(Event);
3068 }
3069
3070 if (QEvent::KeyPress == Event->type() || MythGestureEvent::kEventType == Event->type())
3071 {
3072 // These events aren't received by a direct call from the Qt
3073 // event dispacther, but are received by way of the event
3074 // dispatcher calling TV::eventFilter(MainWindow, Event).
3075#if DEBUG_ACTIONS
3076 if (QEvent::KeyPress == Event->type())
3077 {
3078 const auto * ke = dynamic_cast<QKeyEvent*>(Event);
3079 if (ke)
3080 {
3081 LOG(VB_GENERAL, LOG_INFO, LOC + QString("keypress: %1 '%2'")
3082 .arg(ke->key()).arg(ke->text()));
3083 }
3084 }
3085 else
3086 {
3087 const auto * ge = dynamic_cast<MythGestureEvent*>(Event);
3088 if (ge)
3089 {
3090 LOG(VB_GENERAL, LOG_INFO, LOC + QString("mythgesture: g:%1 pos:%2,%3 b:%4")
3091 .arg(ge->GetGesture()).arg(ge->GetPosition().x())
3092 .arg(ge->GetPosition().y()).arg(ge->GetButton()));
3093 }
3094 }
3095#endif
3096 bool handled = false;
3101 if (handled)
3102 return true;
3103 }
3104
3105 switch (Event->type())
3106 {
3107 case QEvent::Paint:
3108 case QEvent::UpdateRequest:
3109 case QEvent::Enter:
3110 // These events aren't received by a direct call from the Qt
3111 // event dispacther, but are received by way of the event
3112 // dispatcher calling TV::eventFilter(MainWindow, Event).
3113 return true;
3114 default:
3115 break;
3116 }
3117
3118 return QObject::event(Event);
3119}
3120
3121bool TV::HandleTrackAction(const QString &Action)
3122{
3123 bool handled = true;
3124
3127 } else if (ACTION_ENABLEEXTTEXT == Action) {
3129 } else if (ACTION_DISABLEEXTTEXT == Action) {
3131 } else if (ACTION_ENABLEFORCEDSUBS == Action) {
3132 emit ChangeAllowForcedSubtitles(true);
3133 } else if (ACTION_DISABLEFORCEDSUBS == Action) {
3134 emit ChangeAllowForcedSubtitles(false);
3135 } else if (Action == ACTION_ENABLESUBS) {
3136 emit SetCaptionsEnabled(true, true);
3137 } else if (Action == ACTION_DISABLESUBS) {
3138 emit SetCaptionsEnabled(false, true);
3140 if (m_ccInputMode)
3141 {
3142 bool valid = false;
3143 int page = GetQueuedInputAsInt(&valid, 16);
3144 if (m_vbimode == VBIMode::PAL_TT && valid)
3145 emit SetTeletextPage(static_cast<uint>(page));
3146 else if (m_vbimode == VBIMode::NTSC_CC)
3147 emit SetTrack(kTrackTypeCC608, static_cast<uint>(std::clamp(page - 1, 0, 1)));
3148
3149 ClearInputQueues(true);
3150
3151 m_ccInputMode = false;
3152 if (m_ccInputTimerId)
3153 {
3155 m_ccInputTimerId = 0;
3156 }
3157 }
3159 {
3160 ClearInputQueues(false);
3162
3163 m_ccInputMode = true;
3164 m_asInputMode = false;
3166 if (m_asInputTimerId)
3167 {
3169 m_asInputTimerId = 0;
3170 }
3171 }
3172 else
3173 {
3174 emit ToggleCaptions();
3175 }
3176 }
3177 else if (Action.startsWith("TOGGLE"))
3178 {
3179 int type = to_track_type(Action.mid(6));
3181 emit EnableTeletext();
3182 else if (type >= kTrackTypeSubtitle)
3183 emit ToggleCaptionsByType(static_cast<uint>(type));
3184 else
3185 handled = false;
3186 }
3187 else if (Action.startsWith("SELECT"))
3188 {
3189 int type = to_track_type(Action.mid(6));
3190 uint num = Action.section("_", -1).toUInt();
3191 if (type >= kTrackTypeAudio)
3192 emit SetTrack(static_cast<uint>(type), num);
3193 else
3194 handled = false;
3195 }
3196 else if (Action.startsWith("NEXT") || Action.startsWith("PREV"))
3197 {
3198 int dir = (Action.startsWith("NEXT")) ? +1 : -1;
3199 int type = to_track_type(Action.mid(4));
3200 if (type >= kTrackTypeAudio)
3201 emit ChangeTrack(static_cast<uint>(type), dir);
3202 else if (Action.endsWith("CC"))
3203 emit ChangeCaptionTrack(dir);
3204 else
3205 handled = false;
3206 }
3207 else
3208 {
3209 handled = false;
3210 }
3211 return handled;
3212}
3213
3214// Make a special check for global system-related events.
3215//
3216// This check needs to be done early in the keypress event processing,
3217// because FF/REW processing causes unknown events to stop FF/REW, and
3218// manual zoom mode processing consumes all but a few event types.
3219// Ideally, we would just call MythScreenType::keyPressEvent()
3220// unconditionally, but we only want certain keypresses handled by
3221// that method.
3222//
3223// As a result, some of the MythScreenType::keyPressEvent() string
3224// compare logic is copied here.
3225static bool SysEventHandleAction(MythMainWindow* MainWindow, QKeyEvent *e, const QStringList &actions)
3226{
3227 QStringList::const_iterator it;
3228 for (it = actions.begin(); it != actions.end(); ++it)
3229 {
3230 if ((*it).startsWith("SYSEVENT") ||
3231 *it == ACTION_SCREENSHOT ||
3232 *it == ACTION_TVPOWERON ||
3233 *it == ACTION_TVPOWEROFF)
3234 {
3235 return MainWindow->GetMainStack()->GetTopScreen()->keyPressEvent(e);
3236 }
3237 }
3238 return false;
3239}
3240
3241QList<QKeyEvent*> TV::ConvertScreenPressKeyMap(const QString &KeyList)
3242{
3243 QList<QKeyEvent*> keyPressList;
3244 int i = 0;
3245 QStringList stringKeyList = KeyList.split(',');
3246 for (const auto & str : std::as_const(stringKeyList))
3247 {
3248 QKeySequence keySequence(str);
3249 for (i = 0; i < keySequence.count(); i++)
3250 {
3251#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
3252 int keynum = keySequence[i];
3253 int keyCode = keynum & ~Qt::KeyboardModifierMask;
3254 auto modifiers = static_cast<Qt::KeyboardModifiers>(keynum & Qt::KeyboardModifierMask);
3255#else
3256 int keyCode = keySequence[i].key();
3257 Qt::KeyboardModifiers modifiers = keySequence[i].keyboardModifiers();
3258#endif
3259 auto * keyEvent = new QKeyEvent(QEvent::None, keyCode, modifiers);
3260 keyPressList.append(keyEvent);
3261 }
3262 }
3263 if (stringKeyList.count() < kScreenPressRegionCount)
3264 {
3265 // add default remainders
3266 for(; i < kScreenPressRegionCount; i++)
3267 {
3268 auto * keyEvent = new QKeyEvent(QEvent::None, Qt::Key_Escape, Qt::NoModifier);
3269 keyPressList.append(keyEvent);
3270 }
3271 }
3272 return keyPressList;
3273}
3274
3275bool TV::TranslateGesture(const QString &Context, MythGestureEvent *Event,
3276 QStringList &Actions, bool IsLiveTV)
3277{
3278 if (Event && Context == "TV Playback")
3279 {
3280 // TODO make this configuable via a similar mechanism to
3281 // TranslateKeyPress
3282 // possibly with configurable hot zones of various sizes in a theme
3283 // TODO enhance gestures to support other non Click types too
3284 if ((Event->GetGesture() == MythGestureEvent::Click) &&
3285 (Event->GetButton() == Qt::LeftButton))
3286 {
3287 // divide screen into 12 regions
3288 QSize size = m_mainWindow->size();
3289 QPoint pos = Event->GetPosition();
3290 int region = 0;
3291 const int widthDivider = 4;
3292 int w4 = size.width() / widthDivider;
3293 region = pos.x() / w4;
3294 int h3 = size.height() / 3;
3295 region += (pos.y() / h3) * widthDivider;
3296
3297 if (IsLiveTV)
3298 return m_mainWindow->TranslateKeyPress(Context, m_screenPressKeyMapLiveTV[region], Actions, true);
3299 return m_mainWindow->TranslateKeyPress(Context, m_screenPressKeyMapPlayback[region], Actions, true);
3300 }
3301 return false;
3302 }
3303 return false;
3304}
3305
3306bool TV::TranslateKeyPressOrGesture(const QString &Context, QEvent *Event,
3307 QStringList &Actions, bool IsLiveTV, bool AllowJumps)
3308{
3309 if (Event)
3310 {
3311 if (QEvent::KeyPress == Event->type())
3312 return m_mainWindow->TranslateKeyPress(Context, dynamic_cast<QKeyEvent*>(Event), Actions, AllowJumps);
3313 if (MythGestureEvent::kEventType == Event->type())
3314 return TranslateGesture(Context, dynamic_cast<MythGestureEvent*>(Event), Actions, IsLiveTV);
3315 }
3316 return false;
3317}
3318
3320{
3321 if (Event == nullptr)
3322 return false;
3323
3324 bool ignoreKeys = m_playerContext.IsPlayerChangingBuffers();
3325
3326#if DEBUG_ACTIONS
3327 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("ignoreKeys: %1").arg(ignoreKeys));
3328#endif
3329
3330 if (m_idleTimerId)
3331 {
3334 }
3335
3336#ifdef Q_OS_LINUX
3337 // Fixups for _some_ linux native codes that QT doesn't know
3338 auto* eKeyEvent = dynamic_cast<QKeyEvent*>(Event);
3339 if (eKeyEvent) {
3340 if (eKeyEvent->key() <= 0)
3341 {
3342 int keycode = 0;
3343 switch(eKeyEvent->nativeScanCode())
3344 {
3345 case 209: // XF86AudioPause
3346 keycode = Qt::Key_MediaPause;
3347 break;
3348 default:
3349 break;
3350 }
3351
3352 if (keycode > 0)
3353 {
3354 auto *key = new QKeyEvent(QEvent::KeyPress, keycode, eKeyEvent->modifiers());
3355 QCoreApplication::postEvent(this, key);
3356 }
3357 }
3358 }
3359#endif
3360
3361 QStringList actions;
3362 bool handled = false;
3363 bool alreadyTranslatedPlayback = false;
3364
3365 TVState state = GetState();
3366 bool isLiveTV = StateIsLiveTV(state);
3367
3368 if (ignoreKeys)
3369 {
3370 handled = TranslateKeyPressOrGesture("TV Playback", Event, actions, isLiveTV);
3371 alreadyTranslatedPlayback = true;
3372
3373 if (handled || actions.isEmpty())
3374 return handled;
3375
3376 bool esc = IsActionable({ "ESCAPE", "BACK" }, actions);
3377 bool pause = IsActionable(ACTION_PAUSE, actions);
3378 bool play = IsActionable(ACTION_PLAY, actions);
3379
3380 if ((!esc || m_overlayState.m_browsing) && !pause && !play)
3381 return false;
3382 }
3383
3384 OSD *osd = GetOSDL();
3385 if (osd && osd->DialogVisible())
3386 {
3387 if (QEvent::KeyPress == Event->type())
3388 {
3389 auto *qke = dynamic_cast<QKeyEvent*>(Event);
3390 handled = (qke != nullptr) && osd->DialogHandleKeypress(qke);
3391 }
3392 if (MythGestureEvent::kEventType == Event->type())
3393 {
3394 auto *mge = dynamic_cast<MythGestureEvent*>(Event);
3395 handled = (mge != nullptr) && osd->DialogHandleGesture(mge);
3396 }
3397 }
3398 ReturnOSDLock();
3399
3400 if (m_overlayState.m_editing && !handled)
3401 {
3402 handled |= TranslateKeyPressOrGesture("TV Editing", Event, actions, isLiveTV);
3403
3404 if (!handled && m_player)
3405 {
3406 if (IsActionable("MENU", actions))
3407 {
3408 ShowOSDCutpoint("EDIT_CUT_POINTS");
3409 handled = true;
3410 }
3411 if (IsActionable(ACTION_MENUCOMPACT, actions))
3412 {
3413 ShowOSDCutpoint("EDIT_CUT_POINTS_COMPACT");
3414 handled = true;
3415 }
3416 if (IsActionable("ESCAPE", actions))
3417 {
3418 emit RefreshEditorState(true);
3420 ShowOSDCutpoint("EXIT_EDIT_MODE");
3421 else
3422 emit DisableEdit(0);
3423 handled = true;
3424 }
3425 else
3426 {
3427 emit RefreshEditorState();
3430 {
3431 ShowOSDCutpoint("EDIT_CUT_POINTS");
3432 handled = true;
3433 }
3434 else
3435 {
3436 handled |= m_player->HandleProgramEditorActions(actions);
3437 }
3438 }
3439 }
3440 }
3441
3442 if (handled)
3443 return true;
3444
3445 // If text is already queued up, be more lax on what is ok.
3446 // This allows hex teletext entry and minor channel entry.
3447 if (QEvent::KeyPress == Event->type())
3448 {
3449 auto *qke = dynamic_cast<QKeyEvent*>(Event);
3450 if (qke == nullptr)
3451 return false;
3452 const QString txt = qke->text();
3453 if (HasQueuedInput() && (1 == txt.length()))
3454 {
3455 bool ok = false;
3456 (void)txt.toInt(&ok, 16);
3457 if (ok || txt=="_" || txt=="-" || txt=="#" || txt==".")
3458 {
3459 AddKeyToInputQueue(txt.at(0).toLatin1());
3460 return true;
3461 }
3462 }
3463 }
3464
3465 // Teletext menu
3467 {
3468 QStringList tt_actions;
3469 handled = TranslateKeyPressOrGesture("Teletext Menu", Event, tt_actions, isLiveTV);
3470
3471 if (!handled && !tt_actions.isEmpty())
3472 {
3473 for (const QString& action : std::as_const(tt_actions))
3474 {
3475 emit HandleTeletextAction(action, handled);
3476 if (handled)
3477 return true;
3478 }
3479 }
3480 }
3481
3482 // Interactive television
3484 {
3485 if (!alreadyTranslatedPlayback)
3486 {
3487 handled = TranslateKeyPressOrGesture("TV Playback", Event, actions, isLiveTV);
3488 alreadyTranslatedPlayback = true;
3489 }
3490
3491 if (!handled && !actions.isEmpty())
3492 {
3493 for (const QString& action : std::as_const(actions))
3494 {
3495 emit HandleITVAction(action, handled);
3496 if (handled)
3497 return true;
3498 }
3499 }
3500 }
3501
3502 if (!alreadyTranslatedPlayback)
3503 handled = TranslateKeyPressOrGesture("TV Playback", Event, actions, isLiveTV);
3504
3505 if (handled || actions.isEmpty())
3506 return handled;
3507
3508 handled = false;
3509
3512
3513 if (QEvent::KeyPress == Event->type())
3514 handled = handled || SysEventHandleAction(m_mainWindow, dynamic_cast<QKeyEvent*>(Event), actions);
3515 handled = handled || BrowseHandleAction(actions);
3516 handled = handled || ManualZoomHandleAction(actions);
3517 handled = handled || PictureAttributeHandleAction(actions);
3518 handled = handled || TimeStretchHandleAction(actions);
3519 handled = handled || AudioSyncHandleAction(actions);
3520 handled = handled || SubtitleZoomHandleAction(actions);
3521 handled = handled || SubtitleDelayHandleAction(actions);
3522 handled = handled || DiscMenuHandleAction(actions);
3523 handled = handled || ActiveHandleAction(actions, isDVD, isMenuOrStill);
3524 handled = handled || ToggleHandleAction(actions, isDVD);
3525 handled = handled || FFRewHandleAction(actions);
3526 handled = handled || ActivePostQHandleAction(actions);
3527
3528#if DEBUG_ACTIONS
3529 for (int i = 0; i < actions.size(); ++i)
3530 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("handled(%1) actions[%2](%3)")
3531 .arg(handled).arg(i).arg(actions[i]));
3532#endif // DEBUG_ACTIONS
3533
3534 if (handled)
3535 return true;
3536
3537 if (!handled)
3538 {
3539 for (int i = 0; i < actions.size() && !handled; i++)
3540 {
3541 const QString& action = actions[i];
3542 bool ok = false;
3543 int val = action.toInt(&ok);
3544
3545 if (ok)
3546 {
3547 AddKeyToInputQueue(static_cast<char>('0' + val));
3548 handled = true;
3549 }
3550 }
3551 }
3552
3553 return true;
3554}
3555
3556bool TV::BrowseHandleAction(const QStringList &Actions)
3557{
3559 return false;
3560
3561 bool handled = true;
3562
3563 if (IsActionable({ ACTION_UP, ACTION_CHANNELUP }, Actions)) {
3565 } else if (IsActionable( { ACTION_DOWN, ACTION_CHANNELDOWN }, Actions)) {
3567 } else if (IsActionable(ACTION_LEFT, Actions)) {
3569 } else if (IsActionable(ACTION_RIGHT, Actions)) {
3571 } else if (IsActionable("NEXTFAV", Actions)) {
3573 } else if (IsActionable(ACTION_SELECT, Actions)) {
3574 BrowseEnd(true);
3575 } else if (IsActionable({ ACTION_CLEAROSD, "ESCAPE", "BACK", "TOGGLEBROWSE" }, Actions)) {
3576 BrowseEnd(false);
3577 } else if (IsActionable(ACTION_TOGGLERECORD, Actions)) {
3578 QuickRecord();
3579 } else {
3580 handled = false;
3581 for (const auto& action : std::as_const(Actions))
3582 {
3583 if (action.length() == 1 && action[0].isDigit())
3584 {
3585 AddKeyToInputQueue(action[0].toLatin1());
3586 handled = true;
3587 }
3588 }
3589 }
3590
3591 // only pass-through actions listed below
3592 static const QStringList passthrough =
3593 {
3594 ACTION_VOLUMEUP, ACTION_VOLUMEDOWN, "STRETCHINC", "STRETCHDEC",
3595 ACTION_MUTEAUDIO, "CYCLEAUDIOCHAN", "BOTTOMLINEMOVE", "BOTTOMLINESAVE", "TOGGLEASPECT"
3596 };
3597 return handled || !IsActionable(passthrough, Actions);
3598}
3599
3600bool TV::ManualZoomHandleAction(const QStringList &Actions)
3601{
3602 if (!m_zoomMode)
3603 return false;
3604
3605 bool endmanualzoom = false;
3606 bool handled = true;
3607 bool updateOSD = true;
3608 ZoomDirection zoom = kZoom_END;
3610 zoom = kZoomUp;
3611 } else if (IsActionable({ ACTION_ZOOMDOWN, ACTION_DOWN, ACTION_CHANNELDOWN }, Actions)) {
3612 zoom = kZoomDown;
3613 } else if (IsActionable({ ACTION_ZOOMLEFT, ACTION_LEFT }, Actions)) {
3614 zoom = kZoomLeft;
3615 } else if (IsActionable({ ACTION_ZOOMRIGHT, ACTION_RIGHT }, Actions)) {
3616 zoom = kZoomRight;
3617 } else if (IsActionable({ ACTION_ZOOMASPECTUP, ACTION_VOLUMEUP }, Actions)) {
3618 zoom = kZoomAspectUp;
3619 } else if (IsActionable({ ACTION_ZOOMASPECTDOWN, ACTION_VOLUMEDOWN }, Actions)) {
3620 zoom = kZoomAspectDown;
3621 } else if (IsActionable({ ACTION_ZOOMIN, ACTION_JUMPFFWD }, Actions)) {
3622 zoom = kZoomIn;
3623 } else if (IsActionable({ ACTION_ZOOMOUT, ACTION_JUMPRWND }, Actions)) {
3624 zoom = kZoomOut;
3625 } else if (IsActionable(ACTION_ZOOMVERTICALIN, Actions)) {
3626 zoom = kZoomVerticalIn;
3627 } else if (IsActionable(ACTION_ZOOMVERTICALOUT, Actions)) {
3628 zoom = kZoomVerticalOut;
3629 } else if (IsActionable(ACTION_ZOOMHORIZONTALIN, Actions)) {
3630 zoom = kZoomHorizontalIn;
3631 } else if (IsActionable(ACTION_ZOOMHORIZONTALOUT, Actions)) {
3632 zoom = kZoomHorizontalOut;
3633 } else if (IsActionable({ ACTION_ZOOMQUIT, "ESCAPE", "BACK" }, Actions)) {
3634 zoom = kZoomHome;
3635 endmanualzoom = true;
3636 } else if (IsActionable({ ACTION_ZOOMCOMMIT, ACTION_SELECT }, Actions)) {
3637 endmanualzoom = true;
3638 SetManualZoom(false, tr("Zoom Committed"));
3639 } else {
3640 updateOSD = false;
3641 // only pass-through actions listed below
3642 static const QStringList passthrough =
3643 {
3644 "STRETCHINC", "STRETCHDEC", ACTION_MUTEAUDIO,
3645 "CYCLEAUDIOCHAN", ACTION_PAUSE, ACTION_CLEAROSD
3646 };
3647 handled = !IsActionable(passthrough, Actions);
3648 }
3649
3650 QString msg = tr("Zoom Committed");
3651 if (zoom != kZoom_END)
3652 {
3653 emit ChangeZoom(zoom);
3654 msg = endmanualzoom ? tr("Zoom Ignored") :
3658 }
3659 else if (endmanualzoom)
3660 {
3661 msg = tr("%1 Committed").arg(GetZoomString(m_videoBoundsState.m_manualHorizScale,
3664 }
3665
3666 if (updateOSD)
3667 SetManualZoom(!endmanualzoom, msg);
3668
3669 return handled;
3670}
3671
3672bool TV::PictureAttributeHandleAction(const QStringList &Actions)
3673{
3674 if (!m_adjustingPicture)
3675 return false;
3676
3677 bool up = IsActionable(ACTION_RIGHT, Actions);
3678 bool down = up ? false : IsActionable(ACTION_LEFT, Actions);
3679 if (!(up || down))
3680 return false;
3681
3683 {
3685 VolumeChange(up);
3686 else
3688 return true;
3689 }
3690
3691 int value = 99;
3695 UpdateOSDStatus(toTitleString(m_adjustingPicture), text, QString::number(value),
3697 emit ChangeOSDPositionUpdates(false);
3698 return true;
3699}
3700
3701bool TV::TimeStretchHandleAction(const QStringList &Actions)
3702{
3704 return false;
3705
3706 bool handled = true;
3707
3708 if (IsActionable(ACTION_LEFT, Actions))
3710 else if (IsActionable(ACTION_RIGHT, Actions))
3712 else if (IsActionable(ACTION_DOWN, Actions))
3714 else if (IsActionable(ACTION_UP, Actions))
3716 else if (IsActionable("ADJUSTSTRETCH", Actions))
3718 else if (IsActionable(ACTION_SELECT, Actions))
3719 ClearOSD();
3720 else
3721 handled = false;
3722
3723 return handled;
3724}
3725
3726bool TV::AudioSyncHandleAction(const QStringList& Actions)
3727{
3729 return false;
3730
3731 bool handled = true;
3732
3733 if (IsActionable(ACTION_LEFT, Actions))
3734 emit ChangeAudioOffset(-1ms);
3735 else if (IsActionable(ACTION_RIGHT, Actions))
3736 emit ChangeAudioOffset(1ms);
3737 else if (IsActionable(ACTION_UP, Actions))
3738 emit ChangeAudioOffset(10ms);
3739 else if (IsActionable(ACTION_DOWN, Actions))
3740 emit ChangeAudioOffset(-10ms);
3741 else if (IsActionable({ ACTION_TOGGELAUDIOSYNC, ACTION_SELECT }, Actions))
3742 ClearOSD();
3743 else
3744 handled = false;
3745
3746 return handled;
3747}
3748
3749bool TV::SubtitleZoomHandleAction(const QStringList &Actions)
3750{
3752 return false;
3753
3754 bool handled = true;
3755
3756 if (IsActionable(ACTION_LEFT, Actions))
3757 emit AdjustSubtitleZoom(-1);
3758 else if (IsActionable(ACTION_RIGHT, Actions))
3759 emit AdjustSubtitleZoom(1);
3760 else if (IsActionable(ACTION_UP, Actions))
3761 emit AdjustSubtitleZoom(10);
3762 else if (IsActionable(ACTION_DOWN, Actions))
3763 emit AdjustSubtitleZoom(-10);
3765 ClearOSD();
3766 else
3767 handled = false;
3768
3769 return handled;
3770}
3771
3772bool TV::SubtitleDelayHandleAction(const QStringList &Actions)
3773{
3775 return false;
3776
3777 bool handled = true;
3778
3779 if (IsActionable(ACTION_LEFT, Actions))
3780 emit AdjustSubtitleDelay(-5ms);
3781 else if (IsActionable(ACTION_RIGHT, Actions))
3782 emit AdjustSubtitleDelay(5ms);
3783 else if (IsActionable(ACTION_UP, Actions))
3784 emit AdjustSubtitleDelay(25ms);
3785 else if (IsActionable(ACTION_DOWN, Actions))
3786 emit AdjustSubtitleDelay(-25ms);
3788 ClearOSD();
3789 else
3790 handled = false;
3791
3792 return handled;
3793}
3794
3795bool TV::DiscMenuHandleAction(const QStringList& Actions) const
3796{
3797 mpeg::chrono::pts pts = 0_pts;
3799 if (output)
3800 {
3801 MythVideoFrame *frame = output->GetLastShownFrame();
3802 // convert timecode (msec) to pts (90kHz)
3803 if (frame)
3804 pts = duration_cast<mpeg::chrono::pts>(frame->m_timecode);
3805 }
3807 return m_playerContext.m_buffer->HandleAction(Actions, pts);
3808 return false;
3809}
3810
3811bool TV::ActiveHandleAction(const QStringList &Actions,
3812 bool IsDVD, bool IsDVDStillFrame)
3813{
3814 bool handled = true;
3815
3816 if (IsActionable("SKIPCOMMERCIAL", Actions) && !IsDVD) {
3818 } else if (IsActionable("SKIPCOMMBACK", Actions) && !IsDVD) {
3820 } else if (IsActionable("QUEUETRANSCODE", Actions) && !IsDVD) {
3821 DoQueueTranscode("Default");
3822 } else if (IsActionable("QUEUETRANSCODE_AUTO", Actions) && !IsDVD) {
3823 DoQueueTranscode("Autodetect");
3824 } else if (IsActionable("QUEUETRANSCODE_HIGH", Actions) && !IsDVD) {
3825 DoQueueTranscode("High Quality");
3826 } else if (IsActionable("QUEUETRANSCODE_MEDIUM", Actions) && !IsDVD) {
3827 DoQueueTranscode("Medium Quality");
3828 } else if (IsActionable("QUEUETRANSCODE_LOW", Actions) && !IsDVD) {
3829 DoQueueTranscode("Low Quality");
3830 } else if (IsActionable(ACTION_PLAY, Actions)) {
3831 DoPlay();
3832 } else if (IsActionable(ACTION_PAUSE, Actions)) {
3833 DoTogglePause(true);
3834 } else if (IsActionable("SPEEDINC", Actions) && !IsDVDStillFrame) {
3835 ChangeSpeed(1);
3836 } else if (IsActionable("SPEEDDEC", Actions) && !IsDVDStillFrame) {
3837 ChangeSpeed(-1);
3838 } else if (IsActionable("ADJUSTSTRETCH", Actions)) {
3839 ChangeTimeStretch(0); // just display
3840 } else if (IsActionable("CYCLECOMMSKIPMODE",Actions) && !IsDVD) {
3842 } else if (IsActionable("NEXTSCAN", Actions)) {
3843 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
3845 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
3847 }
3848 else if (IsActionable(ACTION_SEEKARB, Actions) && !IsDVD)
3849 {
3850 if (m_asInputMode)
3851 {
3852 ClearInputQueues(true);
3853 emit ChangeOSDText(OSD_WIN_INPUT, {{"osd_number_entry", tr("Seek:")}}, kOSDTimeout_Med);
3854 m_asInputMode = false;
3855 if (m_asInputTimerId)
3856 {
3858 m_asInputTimerId = 0;
3859 }
3860 }
3861 else
3862 {
3863 ClearInputQueues(false);
3865 m_asInputMode = true;
3866 m_ccInputMode = false;
3868 if (m_ccInputTimerId)
3869 {
3871 m_ccInputTimerId = 0;
3872 }
3873 }
3874 }
3875 else if (IsActionable(ACTION_JUMPRWND, Actions))
3876 {
3877 DoJumpRWND();
3878 }
3879 else if (IsActionable(ACTION_JUMPFFWD, Actions))
3880 {
3881 DoJumpFFWD();
3882 }
3883 else if (IsActionable(ACTION_JUMPBKMRK, Actions))
3884 {
3885 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
3886 uint64_t bookmark = m_player->GetBookmark();
3887 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
3888
3889 if (bookmark)
3890 {
3891 DoPlayerSeekToFrame(bookmark);
3892 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
3893 UpdateOSDSeekMessage(tr("Jump to Bookmark"), kOSDTimeout_Med);
3894 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
3895 }
3896 }
3897 else if (IsActionable(ACTION_JUMPSTART,Actions))
3898 {
3899 DoSeek(0, tr("Jump to Beginning"), /*timeIsOffset*/false, /*honorCutlist*/true);
3900 }
3901 else if (IsActionable(ACTION_CLEAROSD, Actions))
3902 {
3903 ClearOSD();
3904 }
3905 else if (IsActionable(ACTION_VIEWSCHEDULED, Actions))
3906 {
3908 }
3909 else if (HandleJumpToProgramAction(Actions))
3910 { // NOLINT(bugprone-branch-clone)
3911 }
3912 else if (IsActionable(ACTION_SIGNALMON, Actions))
3913 {
3915 {
3916 QString input = m_playerContext.m_recorder->GetInput();
3918
3919 if (timeout == 0xffffffff)
3920 {
3921 emit ChangeOSDMessage("No Signal Monitor");
3922 return false;
3923 }
3924
3925 std::chrono::milliseconds rate = m_sigMonMode ? 0ms : 100ms;
3926 bool notify = !m_sigMonMode;
3927
3928 PauseLiveTV();
3930 UnpauseLiveTV();
3931
3932 m_lockTimerOn = false;
3934 }
3935 }
3936 else if (IsActionable(ACTION_SCREENSHOT, Actions))
3937 {
3939 }
3940 else if (IsActionable(ACTION_STOP, Actions))
3941 {
3942 PrepareToExitPlayer(__LINE__);
3943 SetExitPlayer(true, true);
3944 }
3945 else if (IsActionable(ACTION_EXITSHOWNOPROMPTS, Actions))
3946 {
3947 m_requestDelete = false;
3948 PrepareToExitPlayer(__LINE__);
3949 SetExitPlayer(true, true);
3950 }
3951 else if (IsActionable({ "ESCAPE", "BACK" }, Actions))
3952 {
3955 {
3956 ClearOSD();
3957 }
3958 else
3959 {
3960 bool visible = false;
3961 emit IsOSDVisible(visible);
3962 if (visible)
3963 {
3964 ClearOSD();
3965 return handled;
3966 }
3967 }
3968
3969 NormalSpeed();
3970 StopFFRew();
3971 bool exit = false;
3972 if (StateIsLiveTV(GetState()))
3973 {
3975 {
3977 return handled;
3978 }
3979 exit = true;
3980 }
3981 else
3982 {
3984 !m_underNetworkControl && !IsDVDStillFrame)
3985 {
3987 return handled;
3988 }
3989 if (16 & m_dbPlaybackExitPrompt)
3990 {
3991 m_clearPosOnExit = true;
3992 }
3993 PrepareToExitPlayer(__LINE__);
3994 m_requestDelete = false;
3995 exit = true;
3996 }
3997
3998 if (exit)
3999 {
4000 // If it's a DVD, and we're not trying to execute a
4001 // jumppoint, try to back up.
4002 if (IsDVD && !m_mainWindow->IsExitingToMain() && IsActionable("BACK", Actions) &&
4004 {
4005 return handled;
4006 }
4007 SetExitPlayer(true, true);
4008 }
4009 }
4010 else if (IsActionable(ACTION_ENABLEUPMIX, Actions))
4011 {
4012 emit ChangeUpmix(true);
4013 }
4014 else if (IsActionable(ACTION_DISABLEUPMIX, Actions))
4015 {
4016 emit ChangeUpmix(false);
4017 }
4018 else if (IsActionable(ACTION_VOLUMEDOWN, Actions))
4019 {
4020 VolumeChange(false);
4021 }
4022 else if (IsActionable(ACTION_VOLUMEUP, Actions))
4023 {
4024 VolumeChange(true);
4025 }
4026 else if (IsActionable("CYCLEAUDIOCHAN", Actions))
4027 {
4028 emit ChangeMuteState(true);
4029 }
4030 else if (IsActionable(ACTION_MUTEAUDIO, Actions))
4031 {
4032 emit ChangeMuteState();
4033 }
4034 else if (IsActionable("STRETCHINC", Actions))
4035 {
4037 }
4038 else if (IsActionable("STRETCHDEC", Actions))
4039 {
4041 }
4042 else if (IsActionable("MENU", Actions))
4043 {
4044 ShowOSDMenu();
4045 }
4046 else if (IsActionable(ACTION_MENUCOMPACT, Actions))
4047 {
4048 ShowOSDMenu(true);
4049 }
4050 else if (IsActionable({ "INFO", "INFOWITHCUTLIST" }, Actions))
4051 {
4052 if (HasQueuedInput())
4053 DoArbSeek(ARBSEEK_SET, IsActionable("INFOWITHCUTLIST", Actions));
4054 else
4055 ToggleOSD(true);
4056 }
4057 else if (IsActionable(ACTION_TOGGLEOSDDEBUG, Actions))
4058 {
4059 emit ChangeOSDDebug();
4060 }
4061 else if (!IsDVDStillFrame && SeekHandleAction(Actions, IsDVD))
4062 {
4063 }
4064 else if (IsActionable(ACTION_SELECT, Actions) && HasQueuedChannel())
4065 {
4067 }
4068 else
4069 {
4070 handled = false;
4071 for (auto it = Actions.cbegin(); it != Actions.cend() && !handled; ++it)
4072 handled = HandleTrackAction(*it);
4073 }
4074
4075 return handled;
4076}
4077
4078bool TV::FFRewHandleAction(const QStringList &Actions)
4079{
4080 bool handled = false;
4081
4083 {
4084 for (int i = 0; i < Actions.size() && !handled; i++)
4085 {
4086 const QString& action = Actions[i];
4087 bool ok = false;
4088 int val = action.toInt(&ok);
4089
4090 if (ok && val < static_cast<int>(m_ffRewSpeeds.size()))
4091 {
4092 SetFFRew(val);
4093 handled = true;
4094 }
4095 }
4096
4097 if (!handled)
4098 {
4101 handled = true;
4102 }
4103 }
4104
4106 {
4107 NormalSpeed();
4109 handled = true;
4110 }
4111
4112 return handled;
4113}
4114
4115bool TV::ToggleHandleAction(const QStringList &Actions, bool IsDVD)
4116{
4117 bool handled = true;
4118 bool islivetv = StateIsLiveTV(GetState());
4119
4120 if (IsActionable(ACTION_BOTTOMLINEMOVE, Actions)) {
4121 emit ToggleMoveBottomLine();
4122 } else if (IsActionable(ACTION_BOTTOMLINESAVE, Actions)) {
4123 emit SaveBottomLine();
4124 } else if (IsActionable("TOGGLEASPECT", Actions)) {
4125 emit ChangeAspectOverride();
4126 } else if (IsActionable("TOGGLEFILL", Actions)) {
4127 emit ChangeAdjustFill();
4128 } else if (IsActionable(ACTION_TOGGELAUDIOSYNC, Actions)) {
4129 emit ChangeAudioOffset(0ms); // just display
4130 } else if (IsActionable(ACTION_TOGGLESUBTITLEZOOM, Actions)) {
4131 emit AdjustSubtitleZoom(0); // just display
4132 } else if (IsActionable(ACTION_TOGGLESUBTITLEDELAY, Actions)) {
4133 emit AdjustSubtitleDelay(0ms); // just display
4134 } else if (IsActionable(ACTION_TOGGLEVISUALISATION, Actions)) {
4135 emit EnableVisualiser(false, true);
4136 } else if (IsActionable(ACTION_ENABLEVISUALISATION, Actions)) {
4137 emit EnableVisualiser(true);
4138 } else if (IsActionable(ACTION_DISABLEVISUALISATION, Actions)) {
4139 emit EnableVisualiser(false);
4140 } else if (IsActionable("TOGGLEPICCONTROLS", Actions)) {
4142 } else if (IsActionable("TOGGLESTRETCH", Actions)) {
4144 } else if (IsActionable(ACTION_TOGGLEUPMIX, Actions)) {
4145 emit ChangeUpmix(false, true);
4146 } else if (IsActionable(ACTION_TOGGLESLEEP, Actions)) {
4148 } else if (IsActionable(ACTION_TOGGLERECORD, Actions) && islivetv) {
4149 QuickRecord();
4150 } else if (IsActionable(ACTION_TOGGLEFAV, Actions) && islivetv) {
4152 } else if (IsActionable(ACTION_TOGGLECHANCONTROLS, Actions) && islivetv) {
4154 } else if (IsActionable(ACTION_TOGGLERECCONTROLS, Actions) && islivetv) {
4156 } else if (IsActionable("TOGGLEBROWSE", Actions)) {
4157 if (islivetv)
4158 BrowseStart();
4159 else if (!IsDVD)
4160 ShowOSDMenu();
4161 else
4162 handled = false;
4163 } else if (IsActionable("EDIT", Actions)) {
4164 if (islivetv)
4166 else if (!IsDVD)
4168 } else if (IsActionable(ACTION_OSDNAVIGATION, Actions)) {
4170 } else {
4171 handled = false;
4172 }
4173
4174 return handled;
4175}
4176
4178{
4179 if (Clear)
4180 {
4181 emit UpdateBookmark(true);
4182 emit ChangeOSDMessage(tr("Bookmark Cleared"));
4183 }
4184 else // if (IsBookmarkAllowed(ctx))
4185 {
4186 emit UpdateBookmark();
4187 osdInfo info;
4189 info.text["title"] = tr("Position");
4191 emit ChangeOSDMessage(tr("Bookmark Saved"));
4192 }
4193}
4194
4195bool TV::ActivePostQHandleAction(const QStringList &Actions)
4196{
4197 bool handled = true;
4198 TVState state = GetState();
4199 bool islivetv = StateIsLiveTV(state);
4200 bool isdvd = state == kState_WatchingDVD;
4201 bool isdisc = isdvd || state == kState_WatchingBD;
4202
4203 if (IsActionable(ACTION_SETBOOKMARK, Actions))
4204 {
4205 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4206 SetBookmark(false);
4207 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4208 }
4209 if (IsActionable(ACTION_TOGGLEBOOKMARK, Actions))
4210 {
4211 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4213 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4214 }
4215 else if (IsActionable("NEXTFAV", Actions) && islivetv)
4216 {
4218 }
4219 else if (IsActionable("NEXTSOURCE", Actions) && islivetv)
4220 {
4222 }
4223 else if (IsActionable("PREVSOURCE", Actions) && islivetv)
4224 {
4226 }
4227 else if (IsActionable("NEXTINPUT", Actions) && islivetv)
4228 {
4229 SwitchInputs();
4230 }
4231 else if (IsActionable(ACTION_GUIDE, Actions))
4232 {
4234 }
4235 else if (IsActionable("PREVCHAN", Actions) && islivetv)
4236 {
4237 PopPreviousChannel(false);
4238 }
4239 else if (IsActionable(ACTION_CHANNELUP, Actions))
4240 {
4241 if (islivetv)
4242 {
4243 if (m_dbBrowseAlways)
4245 else
4247 }
4248 else
4249 {
4250 DoJumpRWND();
4251 }
4252 }
4253 else if (IsActionable(ACTION_CHANNELDOWN, Actions))
4254 {
4255 if (islivetv)
4256 {
4257 if (m_dbBrowseAlways)
4259 else
4261 }
4262 else
4263 {
4264 DoJumpFFWD();
4265 }
4266 }
4267 else if (IsActionable("DELETE", Actions) && !islivetv)
4268 {
4269 NormalSpeed();
4270 StopFFRew();
4271 PrepareToExitPlayer(__LINE__);
4272 ShowOSDPromptDeleteRecording(tr("Are you sure you want to delete:"));
4273 }
4274 else if (IsActionable(ACTION_JUMPTODVDROOTMENU, Actions) && isdisc)
4275 {
4276 emit GoToMenu("root");
4277 }
4278 else if (IsActionable(ACTION_JUMPTODVDCHAPTERMENU, Actions) && isdisc)
4279 {
4280 emit GoToMenu("chapter");
4281 }
4282 else if (IsActionable(ACTION_JUMPTODVDTITLEMENU, Actions) && isdisc)
4283 {
4284 emit GoToMenu("title");
4285 }
4286 else if (IsActionable(ACTION_JUMPTOPOPUPMENU, Actions) && isdisc)
4287 {
4288 emit GoToMenu("popup");
4289 }
4290 else if (IsActionable(ACTION_FINDER, Actions))
4291 {
4293 }
4294 else
4295 {
4296 handled = false;
4297 }
4298
4299 return handled;
4300}
4301
4302
4304{
4305 bool ignoreKeys = m_playerContext.IsPlayerChangingBuffers();
4306
4307#ifdef DEBUG_ACTIONS
4308 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("(%1) ignoreKeys: %2").arg(Command).arg(ignoreKeys));
4309#endif
4310
4311 if (ignoreKeys)
4312 {
4313 LOG(VB_GENERAL, LOG_WARNING, LOC + "Ignoring network control command because ignoreKeys is set");
4314 return;
4315 }
4316
4317 QStringList tokens = Command.split(" ", Qt::SkipEmptyParts);
4318 if (tokens.size() < 2)
4319 {
4320 LOG(VB_GENERAL, LOG_ERR, LOC + "Not enough tokens in network control command " + QString("'%1'").arg(Command));
4321 return;
4322 }
4323
4324 OSD *osd = GetOSDL();
4325 bool dlg = false;
4326 if (osd)
4327 dlg = osd->DialogVisible();
4328 ReturnOSDLock();
4329
4330 if (dlg)
4331 {
4332 LOG(VB_GENERAL, LOG_WARNING, LOC +
4333 "Ignoring network control command\n\t\t\t" +
4334 QString("because dialog is waiting for a response"));
4335 return;
4336 }
4337
4338 if (tokens[1] != "QUERY")
4339 ClearOSD();
4340
4341 if (tokens.size() == 3 && tokens[1] == "CHANID")
4342 {
4343 m_queuedChanID = tokens[2].toUInt();
4344 m_queuedChanNum.clear();
4346 }
4347 else if (tokens.size() == 3 && tokens[1] == "CHANNEL")
4348 {
4349 if (StateIsLiveTV(GetState()))
4350 {
4351 static const QRegularExpression kChannelNumRE { R"(^[-\.\d_#]+$)" };
4352 if (tokens[2] == "UP")
4354 else if (tokens[2] == "DOWN")
4356 else if (tokens[2].contains(kChannelNumRE))
4357 ChangeChannel(0, tokens[2]);
4358 }
4359 }
4360 else if (tokens.size() == 3 && tokens[1] == "SPEED")
4361 {
4362 bool paused = ContextIsPaused(__FILE__, __LINE__);
4363
4364 if (tokens[2] == "0x")
4365 {
4366 NormalSpeed();
4367 StopFFRew();
4368 if (!paused)
4369 DoTogglePause(true);
4370 }
4371 else if (tokens[2] == "normal")
4372 {
4373 NormalSpeed();
4374 StopFFRew();
4375 if (paused)
4376 DoTogglePause(true);
4377 return;
4378 }
4379 else
4380 {
4381 static const QRegularExpression kSpeedRE { R"(^\-*(\d*\.)?\d+x$)" };
4382 float tmpSpeed = 1.0F;
4383 bool ok = false;
4384
4385 if (tokens[2].contains(kSpeedRE))
4386 {
4387 QString speed = tokens[2].left(tokens[2].length()-1);
4388 tmpSpeed = speed.toFloat(&ok);
4389 }
4390 else
4391 {
4392 static const QRegularExpression re { R"(^(\-*\d+)\/(\d+)x$)" };
4393 auto match = re.match(tokens[2]);
4394 if (match.hasMatch())
4395 {
4396 QStringList matches = match.capturedTexts();
4397 int numerator = matches[1].toInt(&ok);
4398 int denominator = matches[2].toInt(&ok);
4399
4400 if (ok && denominator != 0)
4401 tmpSpeed = static_cast<float>(numerator) / static_cast<float>(denominator);
4402 else
4403 ok = false;
4404 }
4405 }
4406
4407 if (ok)
4408 {
4409 float searchSpeed = fabs(tmpSpeed);
4410
4411 if (paused)
4412 DoTogglePause(true);
4413
4414 if (tmpSpeed == 0.0F)
4415 {
4416 NormalSpeed();
4417 StopFFRew();
4418
4419 if (!paused)
4420 DoTogglePause(true);
4421 }
4422 else if (tmpSpeed == 1.0F)
4423 {
4424 StopFFRew();
4426 ChangeTimeStretch(0, false);
4427 return;
4428 }
4429
4430 NormalSpeed();
4431
4432 size_t index = 0;
4433 for ( ; index < m_ffRewSpeeds.size(); index++)
4434 if (m_ffRewSpeeds[index] == static_cast<int>(searchSpeed))
4435 break;
4436
4437 if ((index < m_ffRewSpeeds.size()) && (m_ffRewSpeeds[index] == static_cast<int>(searchSpeed)))
4438 {
4439 if (tmpSpeed < 0)
4441 else if (tmpSpeed > 1)
4443 else
4444 StopFFRew();
4445
4447 SetFFRew(static_cast<int>(index));
4448 }
4449 else if (0.125F <= tmpSpeed && tmpSpeed <= 2.0F)
4450 {
4451 StopFFRew();
4452 m_playerContext.m_tsNormal = tmpSpeed; // alter speed before display
4453 ChangeTimeStretch(0, false);
4454 }
4455 else
4456 {
4457 LOG(VB_GENERAL, LOG_WARNING, QString("Couldn't find %1 speed. Setting Speed to 1x")
4458 .arg(static_cast<double>(searchSpeed)));
4461 }
4462 }
4463 else
4464 {
4465 LOG(VB_GENERAL, LOG_ERR, QString("Found an unknown speed of %1").arg(tokens[2]));
4466 }
4467 }
4468 }
4469 else if (tokens.size() == 2 && tokens[1] == "STOP")
4470 {
4471 PrepareToExitPlayer(__LINE__);
4472 SetExitPlayer(true, true);
4473 }
4474 else if (tokens.size() >= 3 && tokens[1] == "SEEK" && m_playerContext.HasPlayer())
4475 {
4476 static const QRegularExpression kDigitsRE { "^\\d+$" };
4478 return;
4479
4480 if (tokens[2] == "BEGINNING")
4481 {
4482 DoSeek(0, tr("Jump to Beginning"), /*timeIsOffset*/false, /*honorCutlist*/true);
4483 }
4484 else if (tokens[2] == "FORWARD")
4485 {
4486 DoSeek(m_playerContext.m_fftime, tr("Skip Ahead"), /*timeIsOffset*/true, /*honorCutlist*/true);
4487 }
4488 else if (tokens[2] == "BACKWARD")
4489 {
4490 DoSeek(-m_playerContext.m_rewtime, tr("Skip Back"), /*timeIsOffset*/true, /*honorCutlist*/true);
4491 }
4492 else if ((tokens[2] == "POSITION" ||
4493 tokens[2] == "POSITIONWITHCUTLIST") &&
4494 (tokens.size() == 4) &&
4495 (tokens[3].contains(kDigitsRE)))
4496 {
4497 DoSeekAbsolute(tokens[3].toInt(), tokens[2] == "POSITIONWITHCUTLIST");
4498 }
4499 }
4500 else if (tokens.size() >= 3 && tokens[1] == "SUBTITLES")
4501 {
4502 bool ok = false;
4503 uint track = tokens[2].toUInt(&ok);
4504
4505 if (!ok)
4506 return;
4507
4508 if (track == 0)
4509 {
4510 emit SetCaptionsEnabled(false, true);
4511 }
4512 else
4513 {
4514 QStringList subs = m_player->GetTracks(kTrackTypeSubtitle);
4515 uint size = static_cast<uint>(subs.size());
4516 uint start = 1;
4517 uint finish = start + size;
4518 if (track >= start && track < finish)
4519 {
4520 emit SetTrack(kTrackTypeSubtitle, track - start);
4522 return;
4523 }
4524
4525 start = finish + 1;
4527 finish = start + size;
4528 if (track >= start && track < finish)
4529 {
4530 emit SetTrack(kTrackTypeCC708, track - start);
4532 return;
4533 }
4534
4535 start = finish + 1;
4537 finish = start + size;
4538 if (track >= start && track < finish)
4539 {
4540 emit SetTrack(kTrackTypeCC608, track - start);
4542 return;
4543 }
4544
4545 start = finish + 1;
4547 finish = start + size;
4548 if (track >= start && track < finish)
4549 {
4552 return;
4553 }
4554
4555 start = finish + 1;
4557 finish = start + size;
4558 if (track >= start && track < finish)
4559 {
4560 emit SetTrack(kTrackTypeTeletextMenu, track - start);
4562 return;
4563 }
4564
4565 start = finish + 1;
4567 finish = start + size;
4568 if (track >= start && track < finish)
4569 {
4570 emit SetTrack(kTrackTypeRawText, track - start);
4572 return;
4573 }
4574 }
4575 }
4576 else if (tokens.size() >= 3 && tokens[1] == "VOLUME")
4577 {
4578 static const QRegularExpression re { "(\\d+)%?" };
4579 auto match = re.match(tokens[2]);
4580 if (match.hasMatch())
4581 {
4582 QStringList matches = match.capturedTexts();
4583
4584 LOG(VB_GENERAL, LOG_INFO, QString("Set Volume to %1%").arg(matches[1]));
4585
4586 bool ok = false;
4587 int vol = matches[1].toInt(&ok);
4588 if (!ok)
4589 return;
4590
4591 if (0 <= vol && vol <= 100)
4592 emit ChangeVolume(true, vol);
4593 }
4594 }
4595 else if (tokens.size() >= 3 && tokens[1] == "QUERY")
4596 {
4597 if (tokens[2] == "POSITION")
4598 {
4599 if (!m_player)
4600 return;
4601 QString speedStr;
4602 if (ContextIsPaused(__FILE__, __LINE__))
4603 {
4604 speedStr = "pause";
4605 }
4607 {
4608 speedStr = QString("%1x").arg(m_playerContext.m_ffRewSpeed);
4609 }
4610 else
4611 {
4612 static const QRegularExpression re { "Play (.*)x" };
4613 auto match = re.match(m_playerContext.GetPlayMessage());
4614 if (match.hasMatch())
4615 {
4616 QStringList matches = match.capturedTexts();
4617 speedStr = QString("%1x").arg(matches[1]);
4618 }
4619 else
4620 {
4621 speedStr = "1x";
4622 }
4623 }
4624
4625 osdInfo info;
4627
4628 QDateTime respDate = MythDate::current(true);
4629 QString infoStr = "";
4630
4631 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4632 uint64_t fplay = 0;
4633 double rate = 30.0;
4634 if (m_player)
4635 {
4636 fplay = m_player->GetFramesPlayed();
4637 rate = static_cast<double>(m_player->GetFrameRate()); // for display only
4638 }
4639 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4640
4641 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
4643 {
4644 infoStr = "LiveTV";
4647 }
4648 else
4649 {
4651 infoStr = "DVD";
4653 infoStr = "Recorded";
4654 else
4655 infoStr = "Video";
4656
4659 }
4660
4661 QString bufferFilename =
4662 m_playerContext.m_buffer ? m_playerContext.m_buffer->GetFilename() : QString("no buffer");
4663 if ((infoStr == "Recorded") || (infoStr == "LiveTV"))
4664 {
4665 infoStr += QString(" %1 %2 %3 %4 %5 %6 %7")
4666 .arg(info.text["description"],
4667 speedStr,
4669 ? QString::number(m_playerContext.m_playingInfo->GetChanID()) : "0",
4670 respDate.toString(Qt::ISODate),
4671 QString::number(fplay),
4672 bufferFilename,
4673 QString::number(rate));
4674 }
4675 else
4676 {
4677 QString position = info.text["description"].section(" ",0,0);
4678 infoStr += QString(" %1 %2 %3 %4 %5")
4679 .arg(position,
4680 speedStr,
4681 bufferFilename,
4682 QString::number(fplay),
4683 QString::number(rate));
4684 }
4685
4686 infoStr += QString(" Subtitles:");
4687
4689
4690 if (subtype == kDisplayNone)
4691 infoStr += QString(" *0:[None]*");
4692 else
4693 infoStr += QString(" 0:[None]");
4694
4695 uint n = 1;
4696
4697 QStringList subs = m_player->GetTracks(kTrackTypeSubtitle);
4698 for (int i = 0; i < subs.size(); i++)
4699 {
4700 if ((subtype & kDisplayAVSubtitle) && (m_player->GetTrack(kTrackTypeSubtitle) == i))
4701 infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
4702 else
4703 infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
4704 n++;
4705 }
4706
4708 for (int i = 0; i < subs.size(); i++)
4709 {
4710 if ((subtype & kDisplayCC708) && (m_player->GetTrack(kTrackTypeCC708) == i))
4711 infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
4712 else
4713 infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
4714 n++;
4715 }
4716
4718 for (int i = 0; i < subs.size(); i++)
4719 {
4720 if ((subtype & kDisplayCC608) && (m_player->GetTrack(kTrackTypeCC608) == i))
4721 infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
4722 else
4723 infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
4724 n++;
4725 }
4726
4728 for (int i = 0; i < subs.size(); i++)
4729 {
4731 infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
4732 else
4733 infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
4734 n++;
4735 }
4736
4738 for (int i = 0; i < subs.size(); i++)
4739 {
4741 infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
4742 else
4743 infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
4744 n++;
4745 }
4746
4748 for (int i = 0; i < subs.size(); i++)
4749 {
4751 infoStr += QString(" *%1:[%2]*").arg(n).arg(subs[i]);
4752 else
4753 infoStr += QString(" %1:[%2]").arg(n).arg(subs[i]);
4754 n++;
4755 }
4756
4757 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
4758
4759 QString message = QString("NETWORK_CONTROL ANSWER %1").arg(infoStr);
4760 MythEvent me(message);
4762 }
4763 else if (tokens[2] == "VOLUME")
4764 {
4765 QString infoStr = QString("%1%").arg(m_audioState.m_volume);
4766 QString message = QString("NETWORK_CONTROL ANSWER %1").arg(infoStr);
4767 MythEvent me(message);
4769 }
4770 }
4771}
4772
4773bool TV::StartPlayer(TVState desiredState)
4774{
4775 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("(%1) -- begin").arg(StateToString(desiredState)));
4776
4777 bool ok = CreatePlayer(desiredState);
4779
4780 if (ok)
4781 {
4782 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Created player."));
4783 SetSpeedChangeTimer(25ms, __LINE__);
4784 }
4785 else
4786 {
4787 LOG(VB_GENERAL, LOG_CRIT, LOC + QString("Failed to create player."));
4788 }
4789
4790 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("(%1) -- end %2")
4791 .arg(StateToString(desiredState), (ok) ? "ok" : "error"));
4792
4793 return ok;
4794}
4795
4797{
4798 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4799 if (!m_player)
4800 {
4801 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4802 return;
4803 }
4804
4805 float time = 0.0;
4806
4808 m_player->IsPaused())
4809 {
4811 time = StopFFRew();
4812 else if (m_player->IsPaused())
4814
4818 }
4819 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4820
4821 DoPlayerSeek(time);
4823
4825
4826 SetSpeedChangeTimer(0ms, __LINE__);
4828}
4829
4831{
4832
4834 return 0.0F;
4835
4837 float time = 0.0F;
4838
4839 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4840 if (!m_player)
4841 {
4842 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4843 return 0.0F;
4844 }
4845 if (m_player->IsPaused())
4846 {
4848 }
4849 else
4850 {
4852 time = StopFFRew();
4853 m_player->Pause();
4854 }
4855 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4856 return time;
4857}
4858
4859void TV::DoTogglePauseFinish(float Time, bool ShowOSD)
4860{
4862 return;
4863
4865 return;
4866
4867 if (ContextIsPaused(__FILE__, __LINE__))
4868 {
4871
4872 DoPlayerSeek(Time);
4873 if (ShowOSD)
4876 }
4877 else
4878 {
4879 DoPlayerSeek(Time);
4880 if (ShowOSD)
4883 }
4884
4885 SetSpeedChangeTimer(0ms, __LINE__);
4886}
4887
4895{
4896 bool paused = false;
4897 int dummy = 0;
4898 TV* tv = AcquireRelease(dummy, true);
4899 if (tv)
4900 {
4901 tv->GetPlayerReadLock();
4902 PlayerContext* context = tv->GetPlayerContext();
4903 if (!context->IsErrored())
4904 {
4905 context->LockDeletePlayer(__FILE__, __LINE__);
4906 if (context->m_player)
4907 paused = context->m_player->IsPaused();
4908 context->UnlockDeletePlayer(__FILE__, __LINE__);
4909 }
4910 tv->ReturnPlayerLock();
4911 AcquireRelease(dummy, false);
4912 }
4913 return paused;
4914}
4915
4916void TV::DoTogglePause(bool ShowOSD)
4917{
4918 bool ignore = false;
4919 bool paused = false;
4920 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4921 if (m_player)
4922 {
4923 ignore = m_player->GetEditMode();
4924 paused = m_player->IsPaused();
4925 }
4926 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4927
4928 if (paused)
4930 else
4932
4933 if (!ignore)
4935 // Emit Pause or Unpaused signal
4937}
4938
4939bool TV::DoPlayerSeek(float Time)
4940{
4942 return false;
4943
4944 if (Time > -0.001F && Time < +0.001F)
4945 return false;
4946
4947 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("%1 seconds").arg(static_cast<double>(Time)));
4948
4949 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4950 if (!m_player)
4951 {
4952 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4953 return false;
4954 }
4955
4957 {
4958 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4959 return false;
4960 }
4961
4962 emit PauseAudioUntilReady();
4963
4964 bool res = false;
4965
4966 if (Time > 0.0F)
4967 res = m_player->FastForward(Time);
4968 else if (Time < 0.0F)
4969 res = m_player->Rewind(-Time);
4970 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4971
4972 return res;
4973}
4974
4975bool TV::DoPlayerSeekToFrame(uint64_t FrameNum)
4976{
4978 return false;
4979
4980 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("%1").arg(FrameNum));
4981
4982 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
4983 if (!m_player)
4984 {
4985 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4986 return false;
4987 }
4988
4990 {
4991 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
4992 return false;
4993 }
4994
4995 emit PauseAudioUntilReady();
4996
4997 bool res = m_player->JumpToFrame(FrameNum);
4998
4999 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5000
5001 return res;
5002}
5003
5004bool TV::SeekHandleAction(const QStringList& Actions, const bool IsDVD)
5005{
5006 const int kRewind = 4;
5007 const int kForward = 8;
5008 const int kSticky = 16;
5009 const int kSlippery = 32;
5010 const int kRelative = 64;
5011 const int kAbsolute = 128;
5012 const int kIgnoreCutlist = 256;
5013 const int kWhenceMask = 3;
5014 int flags = 0;
5015 if (IsActionable(ACTION_SEEKFFWD, Actions))
5016 flags = ARBSEEK_FORWARD | kForward | kSlippery | kRelative;
5017 else if (IsActionable("FFWDSTICKY", Actions))
5018 flags = ARBSEEK_END | kForward | kSticky | kAbsolute;
5019 else if (IsActionable(ACTION_RIGHT, Actions))
5020 flags = ARBSEEK_FORWARD | kForward | kSticky | kRelative;
5021 else if (IsActionable(ACTION_SEEKRWND, Actions))
5022 flags = ARBSEEK_REWIND | kRewind | kSlippery | kRelative;
5023 else if (IsActionable("RWNDSTICKY", Actions))
5024 flags = ARBSEEK_SET | kRewind | kSticky | kAbsolute;
5025 else if (IsActionable(ACTION_LEFT, Actions))
5026 flags = ARBSEEK_REWIND | kRewind | kSticky | kRelative;
5027 else
5028 return false;
5029
5030 int direction = (flags & kRewind) ? -1 : 1;
5031 if (HasQueuedInput())
5032 {
5033 DoArbSeek(static_cast<ArbSeekWhence>(flags & kWhenceMask), (flags & kIgnoreCutlist) == 0);
5034 }
5035 else if (ContextIsPaused(__FILE__, __LINE__))
5036 {
5037 if (!IsDVD)
5038 {
5039 QString message = (flags & kRewind) ? tr("Rewind") :
5040 tr("Forward");
5041 if (flags & kAbsolute) // FFWDSTICKY/RWNDSTICKY
5042 {
5043 float time = direction;
5044 DoSeek(time, message, /*timeIsOffset*/true, /*honorCutlist*/(flags & kIgnoreCutlist) == 0);
5045 }
5046 else
5047 {
5048 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5049 uint64_t frameAbs = m_player->GetFramesPlayed();
5050 uint64_t frameRel = m_player->TranslatePositionAbsToRel(frameAbs);
5051 uint64_t targetRel = frameRel + static_cast<uint64_t>(direction);
5052 if (frameRel == 0 && direction < 0)
5053 targetRel = 0;
5054 uint64_t maxAbs = m_player->GetCurrentFrameCount();
5055 uint64_t maxRel = m_player->TranslatePositionAbsToRel(maxAbs);
5056 targetRel = std::min(targetRel, maxRel);
5057 uint64_t targetAbs = m_player->TranslatePositionRelToAbs(targetRel);
5058 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5059 DoPlayerSeekToFrame(targetAbs);
5061 }
5062 }
5063 }
5064 else if (flags & kSticky)
5065 {
5066 ChangeFFRew(direction);
5067 }
5068 else if (flags & kRewind)
5069 {
5070 if (m_smartForward)
5071 m_doSmartForward = true;
5072 DoSeek(-m_playerContext.m_rewtime, tr("Skip Back"), /*timeIsOffset*/true, /*honorCutlist*/(flags & kIgnoreCutlist) == 0);
5073 }
5074 else
5075 {
5077 {
5078 DoSeek(m_playerContext.m_rewtime, tr("Skip Ahead"), /*timeIsOffset*/true, /*honorCutlist*/(flags & kIgnoreCutlist) == 0);
5079 }
5080 else
5081 {
5082 DoSeek(m_playerContext.m_fftime, tr("Skip Ahead"), /*timeIsOffset*/true, /*honorCutlist*/(flags & kIgnoreCutlist) == 0);
5083 }
5084 }
5085 return true;
5086}
5087
5088void TV::DoSeek(float Time, const QString &Msg, bool TimeIsOffset, bool HonorCutlist)
5089{
5090 if (!m_player)
5091 return;
5092
5093 bool limitkeys = false;
5094
5095 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5097 limitkeys = true;
5098
5099 if (!limitkeys || (m_keyRepeatTimer.elapsed() > kKeyRepeatTimeout))
5100 {
5102 NormalSpeed();
5103 Time += StopFFRew();
5104 if (TimeIsOffset)
5105 {
5106 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5107 DoPlayerSeek(Time);
5108 }
5109 else
5110 {
5111 auto time = millisecondsFromFloat(Time * 1000);
5112 uint64_t desiredFrameRel = m_player->TranslatePositionMsToFrame(time, HonorCutlist);
5113 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5114 DoPlayerSeekToFrame(desiredFrameRel);
5115 }
5116 bool paused = m_player->IsPaused();
5118 }
5119 else
5120 {
5121 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5122 }
5123}
5124
5125void TV::DoSeekAbsolute(long long Seconds, bool HonorCutlist)
5126{
5127 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5128 if (!m_player)
5129 {
5130 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5132 return;
5133 }
5134 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5135 DoSeek(Seconds, tr("Jump To"), /*timeIsOffset*/false, HonorCutlist);
5137}
5138
5139void TV::DoArbSeek(ArbSeekWhence Whence, bool HonorCutlist)
5140{
5141 bool ok = false;
5142 int seek = GetQueuedInputAsInt(&ok);
5143 ClearInputQueues(true);
5144 if (!ok)
5145 return;
5146
5147 int64_t time = ((seek / 100) * 3600) + ((seek % 100) * 60);
5148
5149 if (Whence == ARBSEEK_FORWARD)
5150 {
5151 DoSeek(time, tr("Jump Ahead"), /*timeIsOffset*/true, HonorCutlist);
5152 }
5153 else if (Whence == ARBSEEK_REWIND)
5154 {
5155 DoSeek(-time, tr("Jump Back"), /*timeIsOffset*/true, HonorCutlist);
5156 }
5157 else if (Whence == ARBSEEK_END)
5158 {
5159 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5160 if (!m_player)
5161 {
5162 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5163 return;
5164 }
5165 uint64_t total_frames = m_player->GetCurrentFrameCount();
5166 float dur = m_player->ComputeSecs(total_frames, HonorCutlist);
5167 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5168 DoSeek(std::max(0.0F, dur - static_cast<float>(time)), tr("Jump To"), /*timeIsOffset*/false, HonorCutlist);
5169 }
5170 else
5171 {
5172 DoSeekAbsolute(time, HonorCutlist);
5173 }
5174}
5175
5177{
5179 return;
5180
5182
5183 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5184 if (m_player)
5186 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5187
5188 SetSpeedChangeTimer(0ms, __LINE__);
5189}
5190
5191void TV::ChangeSpeed(int Direction)
5192{
5193 int old_speed = m_playerContext.m_ffRewSpeed;
5194
5195 if (ContextIsPaused(__FILE__, __LINE__))
5197
5198 m_playerContext.m_ffRewSpeed += Direction;
5199
5200 float time = StopFFRew();
5201 float speed {NAN};
5202
5203 // Make sure these values for m_ffRewSpeed in TV::ChangeSpeed()
5204 // and PlayerContext::GetPlayMessage() stay in sync.
5205 if (m_playerContext.m_ffRewSpeed == 0) {
5207 } else if (m_playerContext.m_ffRewSpeed == -1) {
5208 speed = 1.0F / 3;
5209 } else if (m_playerContext.m_ffRewSpeed == -2) {
5210 speed = 1.0F / 8;
5211 } else if (m_playerContext.m_ffRewSpeed == -3) {
5212 speed = 1.0F / 16;
5213 } else if (m_playerContext.m_ffRewSpeed == -4) {
5214 DoTogglePause(true);
5215 return;
5216 } else {
5217 m_playerContext.m_ffRewSpeed = old_speed;
5218 return;
5219 }
5220
5221 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5222 if (m_player && !m_player->Play(speed, m_playerContext.m_ffRewSpeed == 0))
5223 {
5224 m_playerContext.m_ffRewSpeed = old_speed;
5225 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5226 return;
5227 }
5228 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5229 DoPlayerSeek(time);
5230 QString mesg = m_playerContext.GetPlayMessage();
5232
5233 SetSpeedChangeTimer(0ms, __LINE__);
5234}
5235
5237{
5238 float time = 0.0;
5239
5241 return time;
5242
5244 time = -m_ffRewSpeeds[static_cast<size_t>(m_playerContext.m_ffRewIndex)] * m_ffRewRepos;
5245 else
5246 time = m_ffRewSpeeds[static_cast<size_t>(m_playerContext.m_ffRewIndex)] * m_ffRewRepos;
5247
5250
5251 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5252 if (m_player)
5254 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5255
5256 SetSpeedChangeTimer(0ms, __LINE__);
5257
5258 return time;
5259}
5260
5261void TV::ChangeFFRew(int Direction)
5262{
5263 if (m_playerContext.m_ffRewState == Direction)
5264 {
5265 while (++m_playerContext.m_ffRewIndex < static_cast<int>(m_ffRewSpeeds.size()))
5266 if (m_ffRewSpeeds[static_cast<size_t>(m_playerContext.m_ffRewIndex)])
5267 break;
5268 if (m_playerContext.m_ffRewIndex >= static_cast<int>(m_ffRewSpeeds.size()))
5271 }
5272 else if (!m_ffRewReverse && m_playerContext.m_ffRewState == -Direction)
5273 {
5275 if (m_ffRewSpeeds[static_cast<size_t>(m_playerContext.m_ffRewIndex)])
5276 break;
5278 {
5280 }
5281 else
5282 {
5283 float time = StopFFRew();
5284 DoPlayerSeek(time);
5286 }
5287 }
5288 else
5289 {
5290 NormalSpeed();
5291 m_playerContext.m_ffRewState = Direction;
5293 }
5294}
5295
5296void TV::SetFFRew(int Index)
5297{
5299 return;
5300
5301 auto index = static_cast<size_t>(Index);
5302 if (!m_ffRewSpeeds[index])
5303 return;
5304
5305 auto ffrewindex = static_cast<size_t>(m_playerContext.m_ffRewIndex);
5306 int speed = 0;
5307 QString mesg;
5309 {
5310 speed = m_ffRewSpeeds[index];
5311 // Don't allow ffwd if seeking is needed but not available
5313 return;
5314
5316 mesg = tr("Forward %1X").arg(m_ffRewSpeeds[ffrewindex]);
5318 }
5319 else
5320 {
5321 // Don't rewind if we cannot seek
5323 return;
5324
5326 mesg = tr("Rewind %1X").arg(m_ffRewSpeeds[ffrewindex]);
5327 speed = -m_ffRewSpeeds[ffrewindex];
5329 }
5330
5331 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5332 if (m_player)
5333 m_player->Play(static_cast<float>(speed), (speed == 1) && (m_playerContext.m_ffRewState > 0));
5334 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5335
5337
5338 SetSpeedChangeTimer(0ms, __LINE__);
5339}
5340
5341void TV::DoQueueTranscode(const QString& Profile)
5342{
5343 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
5344
5346 {
5347 bool stop = false;
5348 if (m_queuedTranscode ||
5353 {
5354 stop = true;
5355 }
5356
5357 if (stop)
5358 {
5363 m_queuedTranscode = false;
5364 emit ChangeOSDMessage(tr("Stopping Transcode"));
5365 }
5366 else
5367 {
5369 recinfo.ApplyTranscoderProfileChange(Profile);
5370 QString jobHost = "";
5371
5374
5375 QString msg = tr("Try Again");
5379 jobHost, "", "", JOB_USE_CUTLIST))
5380 {
5381 m_queuedTranscode = true;
5382 msg = tr("Transcoding");
5383 }
5384 emit ChangeOSDMessage(msg);
5385 }
5386 }
5387 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
5388}
5389
5391{
5392 int num_chapters = 0;
5393 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5394 if (m_player)
5395 num_chapters = m_player->GetNumChapters();
5396 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5397 return num_chapters;
5398}
5399
5400void TV::GetChapterTimes(QList<std::chrono::seconds> &Times)
5401{
5402 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5403 if (m_player)
5404 m_player->GetChapterTimes(Times);
5405 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5406}
5407
5409{
5410 int chapter = 0;
5411 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5412 if (m_player)
5413 chapter = m_player->GetCurrentChapter();
5414 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5415 return chapter;
5416}
5417
5418void TV::DoJumpChapter(int Chapter)
5419{
5420 NormalSpeed();
5421 StopFFRew();
5422
5423 emit PauseAudioUntilReady();
5424
5425 UpdateOSDSeekMessage(tr("Jump Chapter"), kOSDTimeout_Med);
5426
5427 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5428 if (m_player)
5429 m_player->JumpChapter(Chapter);
5430 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5431}
5432
5434{
5435 int num_titles = 0;
5436 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5437 if (m_player)
5438 num_titles = m_player->GetNumTitles();
5439 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5440 return num_titles;
5441}
5442
5444{
5445 int currentTitle = 0;
5446 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5447 if (m_player)
5448 currentTitle = m_player->GetCurrentTitle();
5449 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5450 return currentTitle;
5451}
5452
5454{
5455 int num_angles = 0;
5456 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5457 if (m_player)
5458 num_angles = m_player->GetNumAngles();
5459 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5460 return num_angles;
5461}
5462
5464{
5465 int currentAngle = 0;
5466 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5467 if (m_player)
5468 currentAngle = m_player->GetCurrentAngle();
5469 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5470 return currentAngle;
5471}
5472
5473QString TV::GetAngleName(int Angle)
5474{
5475 QString name;
5476 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5477 if (m_player)
5478 name = m_player->GetAngleName(Angle);
5479 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5480 return name;
5481}
5482
5483std::chrono::seconds TV::GetTitleDuration(int Title)
5484{
5485 std::chrono::seconds seconds = 0s;
5486 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5487 if (m_player)
5488 seconds = m_player->GetTitleDuration(Title);
5489 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5490 return seconds;
5491}
5492
5493
5494QString TV::GetTitleName(int Title)
5495{
5496 QString name;
5497 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5498 if (m_player)
5499 name = m_player->GetTitleName(Title);
5500 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5501 return name;
5502}
5503
5504void TV::DoSwitchTitle(int Title)
5505{
5506 NormalSpeed();
5507 StopFFRew();
5508
5509 emit PauseAudioUntilReady();
5510
5511 UpdateOSDSeekMessage(tr("Switch Title"), kOSDTimeout_Med);
5512 emit ChangeOSDPositionUpdates(true);
5513
5514 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5515 if (m_player)
5516 m_player->SwitchTitle(Title);
5517 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5518}
5519
5520void TV::DoSwitchAngle(int Angle)
5521{
5522 NormalSpeed();
5523 StopFFRew();
5524
5525 emit PauseAudioUntilReady();
5526
5527 UpdateOSDSeekMessage(tr("Switch Angle"), kOSDTimeout_Med);
5528 emit ChangeOSDPositionUpdates(true);
5529
5530 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5531 if (m_player)
5532 m_player->SwitchAngle(Angle);
5533 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5534}
5535
5536void TV::DoSkipCommercials(int Direction)
5537{
5538 NormalSpeed();
5539 StopFFRew();
5540
5541 if (StateIsLiveTV(GetState()))
5542 return;
5543
5544 emit PauseAudioUntilReady();
5545
5546 osdInfo info;
5548 info.text["title"] = tr("Skip");
5549 info.text["description"] = tr("Searching");
5551 emit ChangeOSDPositionUpdates(true);
5552
5553 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
5554 if (m_player)
5555 m_player->SkipCommercials(Direction);
5556 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
5557}
5558
5559void TV::SwitchSource(uint Direction)
5560{
5561 QMap<uint,InputInfo> sources;
5562 uint cardid = m_playerContext.GetCardID();
5563
5564 InfoMap info;
5566 uint sourceid = info["sourceid"].toUInt();
5567
5568 std::vector<InputInfo> inputs = RemoteRequestFreeInputInfo(cardid);
5569 for (auto & input : inputs)
5570 {
5571 // prefer the current card's input in sources list
5572 if ((!sources.contains(input.m_sourceId)) ||
5573 ((cardid == input.m_inputId) && (cardid != sources[input.m_sourceId].m_inputId)))
5574 {
5575 sources[input.m_sourceId] = input;
5576 }
5577 }
5578
5579 // Source switching
5580 QMap<uint,InputInfo>::const_iterator beg = sources.constFind(sourceid);
5581 QMap<uint,InputInfo>::const_iterator sit = beg;
5582
5583 if (sit == sources.constEnd())
5584 return;
5585
5586 if (kNextSource == Direction)
5587 {
5588 ++sit;
5589 if (sit == sources.constEnd())
5590 sit = sources.constBegin();
5591 }
5592
5593 if (kPreviousSource == Direction)
5594 {
5595 if (sit != sources.constBegin())
5596 {
5597 --sit;
5598 }
5599 else
5600 {
5601 QMap<uint,InputInfo>::const_iterator tmp = sources.constBegin();
5602 while (tmp != sources.constEnd())
5603 {
5604 sit = tmp;
5605 ++tmp;
5606 }
5607 }
5608 }
5609
5610 if (sit == beg)
5611 return;
5612
5613 m_switchToInputId = (*sit).m_inputId;
5615}
5616
5617void TV::SwitchInputs(uint ChanID, QString ChanNum, uint InputID)
5618{
5620 return;
5621
5622 // this will re-create the player. Ensure any outstanding events are delivered
5623 // and processed before the player is deleted so that we don't confuse the
5624 // state of the new player e.g. when switching inputs from the guide grid,
5625 // "EPG_EXITING" may not be received until after the player is re-created
5626 // and we inadvertantly disable drawing...
5627 // TODO with recent changes, embedding should be ended synchronously and hence
5628 // this extra call should no longer be needed
5629 QCoreApplication::processEvents();
5630
5631 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("(%1,'%2',%3)").arg(ChanID).arg(ChanNum).arg(InputID));
5632
5633 RemoteEncoder *testrec = nullptr;
5634
5635 if (!StateIsLiveTV(GetState()))
5636 return;
5637
5638 QStringList reclist;
5639 if (InputID)
5640 {
5641 reclist.push_back(QString::number(InputID));
5642 }
5643 else if (ChanID || !ChanNum.isEmpty())
5644 {
5645 // If we are switching to a channel not on the current recorder
5646 // we need to find the next free recorder with that channel.
5647 reclist = ChannelUtil::GetValidRecorderList(ChanID, ChanNum);
5648 }
5649
5650 if (!reclist.empty())
5652
5653 if (testrec && testrec->IsValidRecorder())
5654 {
5655 InputID = static_cast<uint>(testrec->GetRecorderNumber());
5656
5657 // We are switching to a specific channel...
5658 if (ChanID && ChanNum.isEmpty())
5659 ChanNum = ChannelUtil::GetChanNum(static_cast<int>(ChanID));
5660
5661 if (!ChanNum.isEmpty())
5662 CardUtil::SetStartChannel(InputID, ChanNum);
5663 }
5664
5665 // If we are just switching recorders find first available recorder.
5666 if (!testrec)
5667 testrec = RemoteRequestNextFreeRecorder(static_cast<int>(m_playerContext.GetCardID()));
5668
5669 if (testrec && testrec->IsValidRecorder())
5670 {
5671 // Switching inputs so clear the pseudoLiveTVState.
5673 bool muted = m_audioState.m_muteState == kMuteAll;
5674
5675 // pause the decoder first, so we're not reading too close to the end.
5677 {
5680 }
5681
5682 if (m_player)
5684
5685 // shutdown stuff
5687 {
5690 }
5691
5694 m_playerContext.SetPlayer(nullptr);
5695 m_player = nullptr;
5696
5697 // now restart stuff
5699 m_lockTimerOn = false;
5700
5703 // We need to set channum for SpawnLiveTV..
5704 if (ChanNum.isEmpty() && ChanID)
5705 ChanNum = ChannelUtil::GetChanNum(static_cast<int>(ChanID));
5706 if (ChanNum.isEmpty() && InputID)
5707 ChanNum = CardUtil::GetStartChannel(InputID);
5709
5711 {
5712 LOG(VB_GENERAL, LOG_ERR, LOC + "LiveTV not successfully restarted");
5715 SetErrored();
5716 SetExitPlayer(true, false);
5717 }
5718 else
5719 {
5720 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
5721 QString playbackURL = m_playerContext.m_playingInfo->GetPlaybackURL(true);
5722 bool opennow = (m_playerContext.m_tvchain->GetInputType(-1) != "DUMMY");
5725 playbackURL, false, true,
5726 opennow ? MythMediaBuffer::kLiveTVOpenTimeout : -1ms));
5727
5731 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
5732 }
5733
5734 bool ok = false;
5736 {
5738 {
5740 ok = true;
5742 SetSpeedChangeTimer(25ms, __LINE__);
5743 }
5744 else
5745 {
5746 StopStuff(true, true, true);
5747 }
5748 }
5749
5750 if (!ok)
5751 {
5752 LOG(VB_GENERAL, LOG_ERR, LOC + "LiveTV not successfully started");
5755 SetErrored();
5756 SetExitPlayer(true, false);
5757 }
5758 else
5759 {
5760 m_lockTimer.start();
5761 m_lockTimerOn = true;
5762 }
5763 }
5764 else
5765 {
5766 LOG(VB_GENERAL, LOG_ERR, LOC + "No recorder to switch to...");
5767 delete testrec;
5768 }
5769
5770 UnpauseLiveTV();
5772
5773 ITVRestart(true);
5774}
5775
5777{
5778 // TOGGLEFAV was broken in [20523], this just prints something
5779 // out so as not to cause further confusion. See #8948.
5780 LOG(VB_GENERAL, LOG_ERR, "TV::ToggleChannelFavorite() -- currently disabled");
5781}
5782
5783void TV::ToggleChannelFavorite(const QString& ChangroupName) const
5784{
5787}
5788
5789QString TV::GetQueuedInput() const
5790{
5791 return m_queuedInput;
5792}
5793
5794int TV::GetQueuedInputAsInt(bool *OK, int Base) const
5795{
5796 return m_queuedInput.toInt(OK, Base);
5797}
5798
5800{
5801 if (m_queuedChanNum.isEmpty())
5802 return "";
5803
5804 // strip initial zeros and other undesirable characters
5805 int i = 0;
5806 for (; i < m_queuedChanNum.length(); i++)
5807 {
5808 if ((m_queuedChanNum[i] > '0') && (m_queuedChanNum[i] <= '9'))
5809 break;
5810 }
5811 m_queuedChanNum = m_queuedChanNum.right(m_queuedChanNum.length() - i);
5812
5813 // strip whitespace at end of string
5814 m_queuedChanNum = m_queuedChanNum.trimmed();
5815
5816 return m_queuedChanNum;
5817}
5818
5823void TV::ClearInputQueues(bool Hideosd)
5824{
5825 if (Hideosd)
5827
5828 m_queuedInput = "";
5829 m_queuedChanNum = "";
5830 m_queuedChanID = 0;
5832 {
5835 }
5836}
5837
5839{
5840 if (Key)
5841 {
5842 m_queuedInput = m_queuedInput.append(Key).right(kInputKeysMax);
5843 m_queuedChanNum = m_queuedChanNum.append(Key).right(kInputKeysMax);
5845 m_queueInputTimerId = StartTimer(10ms, __LINE__);
5846 }
5847
5848 bool commitSmart = false;
5849 QString inputStr = GetQueuedInput();
5850
5851 // Always use immediate channel change when channel numbers are entered
5852 // in browse mode because in browse mode space/enter exit browse
5853 // mode and change to the currently browsed channel.
5855 {
5856 commitSmart = ProcessSmartChannel(inputStr);
5857 }
5858
5859 // Handle OSD...
5860 inputStr = inputStr.isEmpty() ? "?" : inputStr;
5861 if (m_ccInputMode)
5862 {
5863 QString entryStr = (m_vbimode==VBIMode::PAL_TT) ? tr("TXT:") : tr("CC:");
5864 inputStr = entryStr + " " + inputStr;
5865 }
5866 else if (m_asInputMode)
5867 {
5868 inputStr = tr("Seek:", "seek to location") + " " + inputStr;
5869 }
5870 // NOLINTNEXTLINE(readability-misleading-indentation)
5871 emit ChangeOSDText(OSD_WIN_INPUT, {{ "osd_number_entry", inputStr}}, kOSDTimeout_Med);
5872
5873 // Commit the channel if it is complete and smart changing is enabled.
5874 if (commitSmart)
5876}
5877
5878static QString add_spacer(const QString &chan, const QString &spacer)
5879{
5880 if ((chan.length() >= 2) && !spacer.isEmpty())
5881 return chan.left(chan.length()-1) + spacer + chan.right(1);
5882 return chan;
5883}
5884
5885bool TV::ProcessSmartChannel(QString &InputStr)
5886{
5887 QString chan = GetQueuedChanNum();
5888
5889 if (chan.isEmpty())
5890 return false;
5891
5892 // Check for and remove duplicate separator characters
5893#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
5894 int size = chan.size();
5895#else
5896 qsizetype size = chan.size();
5897#endif
5898 if ((size > 2) && (chan.at(size - 1) == chan.at(size - 2)))
5899 {
5900 bool ok = false;
5901 chan.right(1).toUInt(&ok);
5902 if (!ok)
5903 {
5904 chan = chan.left(chan.length()-1);
5905 m_queuedChanNum = chan;
5907 m_queueInputTimerId = StartTimer(10ms, __LINE__);
5908 }
5909 }
5910
5911 // Look for channel in line-up
5912 QString needed_spacer;
5913 uint pref_cardid = 0;
5914 bool is_not_complete = true;
5915
5916 bool valid_prefix = false;
5918 {
5920 chan, pref_cardid, is_not_complete, needed_spacer);
5921 }
5922
5923#if DEBUG_CHANNEL_PREFIX
5924 LOG(VB_GENERAL, LOG_DEBUG, QString("valid_pref(%1) cardid(%2) chan(%3) "
5925 "pref_cardid(%4) complete(%5) sp(%6)")
5926 .arg(valid_prefix).arg(0).arg(chan)
5927 .arg(pref_cardid).arg(is_not_complete).arg(needed_spacer));
5928#endif
5929
5930 if (!valid_prefix)
5931 {
5932 // not a valid prefix.. reset...
5933 m_queuedChanNum = "";
5934 }
5935 else if (!needed_spacer.isEmpty())
5936 {
5937 // need a spacer..
5938 m_queuedChanNum = add_spacer(chan, needed_spacer);
5939 }
5940
5941#if DEBUG_CHANNEL_PREFIX
5942 LOG(VB_GENERAL, LOG_DEBUG, QString(" ValidPref(%1) CardId(%2) Chan(%3) "
5943 " PrefCardId(%4) Complete(%5) Sp(%6)")
5944 .arg(valid_prefix).arg(0).arg(GetQueuedChanNum())
5945 .arg(pref_cardid).arg(is_not_complete).arg(needed_spacer));
5946#endif
5947
5948 InputStr = m_queuedChanNum;
5950 m_queueInputTimerId = StartTimer(10ms, __LINE__);
5951
5952 return !is_not_complete;
5953}
5954
5956{
5957 bool commited = false;
5958
5959 LOG(VB_PLAYBACK, LOG_INFO, LOC +
5960 QString("livetv(%1) qchannum(%2) qchanid(%3)")
5961 .arg(StateIsLiveTV(GetState()))
5962 .arg(GetQueuedChanNum())
5963 .arg(GetQueuedChanID()));
5964
5965 if (m_ccInputMode)
5966 {
5967 commited = true;
5968 if (HasQueuedInput())
5970 }
5971 else if (m_asInputMode)
5972 {
5973 commited = true;
5974 if (HasQueuedInput())
5975 // XXX Should the cutlist be honored?
5976 DoArbSeek(ARBSEEK_FORWARD, /*honorCutlist*/false);
5977 }
5978 else if (StateIsLiveTV(GetState()))
5979 {
5980 QString channum = GetQueuedChanNum();
5982 {
5983 uint sourceid = 0;
5984 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
5987 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
5988
5989 commited = true;
5990 if (channum.isEmpty())
5991 channum = GetBrowsedInfo().m_chanNum;
5992 uint chanid = GetBrowseChanId(channum, m_playerContext.GetCardID(), sourceid);
5993 if (chanid)
5994 BrowseChannel(channum);
5995
5997 }
5998 else if (GetQueuedChanID() || !channum.isEmpty())
5999 {
6000 commited = true;
6001 ChangeChannel(GetQueuedChanID(), channum);
6002 }
6003 }
6004
6005 ClearInputQueues(true);
6006 return commited;
6007}
6008
6010{
6012 {
6013 uint old_chanid = 0;
6014 if (m_channelGroupId > -1)
6015 {
6016 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
6018 {
6019 LOG(VB_GENERAL, LOG_ERR, LOC +
6020 "no active ctx playingInfo.");
6021 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
6023 return;
6024 }
6025 // Collect channel info
6026 old_chanid = m_playerContext.m_playingInfo->GetChanID();
6027 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
6028 }
6029
6030 if (old_chanid)
6031 {
6032 QMutexLocker locker(&m_channelGroupLock);
6033 if (m_channelGroupId > -1)
6034 {
6036 m_channelGroupChannelList, old_chanid, 0, 0, Direction);
6037 if (chanid)
6038 ChangeChannel(chanid, "");
6039 return;
6040 }
6041 }
6042 }
6043
6044 if (Direction == CHANNEL_DIRECTION_FAVORITE)
6045 Direction = CHANNEL_DIRECTION_UP;
6046
6047 QString oldinputname = m_playerContext.m_recorder->GetInput();
6048
6049 if (ContextIsPaused(__FILE__, __LINE__))
6050 {
6053 }
6054
6055 // Save the current channel if this is the first time
6056 if (m_playerContext.m_prevChan.empty())
6058
6059 emit PauseAudioUntilReady();
6060 PauseLiveTV();
6061
6062 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
6063 if (m_player)
6064 {
6065 emit ResetCaptions();
6066 emit ResetTeletext();
6067 }
6068 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
6069
6071 ClearInputQueues(false);
6072
6073 emit ResetAudio();
6074
6075 UnpauseLiveTV();
6076
6077 if (oldinputname != m_playerContext.m_recorder->GetInput())
6079}
6080
6082 uint cardid, const QString &channum)
6083{
6084 uint chanid = 0;
6085 uint cur_sourceid = 0;
6086
6087 // try to find channel on current input
6088 if (ctx && ctx->m_playingInfo && ctx->m_playingInfo->GetSourceID())
6089 {
6090 cur_sourceid = ctx->m_playingInfo->GetSourceID();
6091 chanid = std::max(ChannelUtil::GetChanID(cur_sourceid, channum), 0);
6092 if (chanid)
6093 return chanid;
6094 }
6095
6096 // try to find channel on specified input
6097 uint sourceid = CardUtil::GetSourceID(cardid);
6098 if (cur_sourceid != sourceid && sourceid)
6099 chanid = std::max(ChannelUtil::GetChanID(sourceid, channum), 0);
6100 return chanid;
6101}
6102
6103void TV::ChangeChannel(uint Chanid, const QString &Channum)
6104{
6105 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("(%1, '%2')").arg(Chanid).arg(Channum));
6106
6107 if ((!Chanid && Channum.isEmpty()) || !m_playerContext.m_recorder)
6108 return;
6109
6110 QString channum = Channum;
6111 QStringList reclist;
6112 QVector<uint> tunable_on;
6113
6114 QString oldinputname = m_playerContext.m_recorder->GetInput();
6115
6116 if (channum.isEmpty() && Chanid)
6117 channum = ChannelUtil::GetChanNum(static_cast<int>(Chanid));
6118
6119 bool getit = false;
6121 {
6123 {
6124 getit = false;
6125 }
6127 {
6128 getit = true;
6129 }
6130 else if (Chanid)
6131 {
6132 tunable_on = IsTunableOn(&m_playerContext, Chanid);
6133 getit = !tunable_on.contains(m_playerContext.GetCardID());
6134 }
6135 else
6136 {
6137 QString needed_spacer;
6138 uint pref_cardid = 0;
6139 uint cardid = m_playerContext.GetCardID();
6140 bool dummy = false;
6141
6143 dummy, needed_spacer);
6144
6145 LOG(VB_CHANNEL, LOG_INFO, LOC +
6146 QString("CheckChannelPrefix(%1, pref_cardid %2, %3, '%4') "
6147 "cardid %5")
6148 .arg(Channum).arg(pref_cardid).arg(dummy).arg(needed_spacer)
6149 .arg(cardid));
6150
6151 channum = add_spacer(Channum, needed_spacer);
6152 if (pref_cardid != cardid)
6153 {
6154 getit = true;
6155 }
6156 else
6157 {
6158 if (!Chanid)
6159 Chanid = get_chanid(&m_playerContext, cardid, Channum);
6160 tunable_on = IsTunableOn(&m_playerContext, Chanid);
6161 getit = !tunable_on.contains(cardid);
6162 }
6163 }
6164
6165 if (getit)
6166 {
6167 QStringList tmp =
6168 ChannelUtil::GetValidRecorderList(Chanid, channum);
6169 if (tunable_on.empty())
6170 {
6171 if (!Chanid)
6173 tunable_on = IsTunableOn(&m_playerContext, Chanid);
6174 }
6175 for (const auto& rec : std::as_const(tmp))
6176 {
6177 if ((Chanid == 0U) || tunable_on.contains(rec.toUInt()))
6178 reclist.push_back(rec);
6179 }
6180 }
6181 }
6182
6183 if (!reclist.empty())
6184 {
6186 if (!testrec || !testrec->IsValidRecorder())
6187 {
6188 ClearInputQueues(true);
6190 delete testrec;
6191 return;
6192 }
6193
6194 if (!m_playerContext.m_prevChan.empty() &&
6195 m_playerContext.m_prevChan.back() == channum)
6196 {
6197 // need to remove it if the new channel is the same as the old.
6198 m_playerContext.m_prevChan.pop_back();
6199 }
6200
6201 // found the card on a different recorder.
6202 uint inputid = static_cast<uint>(testrec->GetRecorderNumber());
6203 delete testrec;
6204 // Save the current channel if this is the first time
6205 if (m_playerContext.m_prevChan.empty())
6207 SwitchInputs(Chanid, channum, inputid);
6208 return;
6209 }
6210
6212 return;
6213
6214 if (ContextIsPaused(__FILE__, __LINE__))
6215 {
6218 }
6219
6220 // Save the current channel if this is the first time
6221 if (m_playerContext.m_prevChan.empty())
6223
6224 emit PauseAudioUntilReady();
6225 PauseLiveTV();
6226
6227 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
6228 if (m_player)
6229 {
6230 emit ResetCaptions();
6231 emit ResetTeletext();
6232 }
6233 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
6234
6236
6237 emit ResetAudio();
6238
6239 UnpauseLiveTV((Chanid != 0U) && (GetQueuedChanID() != 0U));
6240
6241 if (oldinputname != m_playerContext.m_recorder->GetInput())
6243}
6244
6246{
6247 for (const auto & option : Options)
6248 {
6249 uint chanid = option.m_chanId;
6250 QString channum = option.m_chanNum;
6251
6252 if (chanid && !channum.isEmpty() && IsTunablePriv(chanid))
6253 {
6254 // hide the channel number, activated by certain signal monitors
6256 m_queuedInput = channum;
6257 m_queuedChanNum = channum;
6258 m_queuedChanID = chanid;
6260 m_queueInputTimerId = StartTimer(10ms, __LINE__);
6261 break;
6262 }
6263 }
6264}
6265
6267{
6268 QString channum = m_playerContext.GetPreviousChannel();
6269 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Previous channel number '%1'").arg(channum));
6270 if (channum.isEmpty())
6271 return;
6272 emit ChangeOSDText(OSD_WIN_INPUT, {{ "osd_number_entry", channum }}, kOSDTimeout_Med);
6273}
6274
6275void TV::PopPreviousChannel(bool ImmediateChange)
6276{
6278 return;
6279
6280 if (!ImmediateChange)
6282
6283 QString prev_channum = m_playerContext.PopPreviousChannel();
6284 QString cur_channum = m_playerContext.m_tvchain->GetChannelName(-1);
6285
6286 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("'%1'->'%2'")
6287 .arg(cur_channum, prev_channum));
6288
6289 // Only change channel if previous channel != current channel
6290 if (cur_channum != prev_channum && !prev_channum.isEmpty())
6291 {
6292 m_queuedInput = prev_channum;
6293 m_queuedChanNum = prev_channum;
6294 m_queuedChanID = 0;
6296 m_queueInputTimerId = StartTimer(10ms, __LINE__);
6297 }
6298
6299 if (ImmediateChange)
6300 {
6301 // Turn off OSD Channel Num so the channel changes right away
6303 }
6304}
6305
6307{
6309 ClearInputQueues(true);
6310
6311 emit DialogQuit();
6312 // pop OSD screen
6313 emit HideAll(true, nullptr, true);
6314
6316 BrowseEnd(false);
6317}
6318
6322void TV::ToggleOSD(bool IncludeStatusOSD)
6323{
6324 OSD *osd = GetOSDL();
6325 if (!osd)
6326 {
6327 ReturnOSDLock();
6328 return;
6329 }
6330
6331 bool hideAll = false;
6332 bool showStatus = false;
6333 bool paused = ContextIsPaused(__FILE__, __LINE__);
6334 bool is_status_disp = osd->IsWindowVisible(OSD_WIN_STATUS);
6335 bool has_prog_info = osd->HasWindow(OSD_WIN_PROGINFO);
6336 bool is_prog_info_disp = osd->IsWindowVisible(OSD_WIN_PROGINFO);
6337
6338 ReturnOSDLock();
6339
6340 if (is_status_disp)
6341 {
6342 if (has_prog_info)
6344 else
6345 hideAll = true;
6346 }
6347 else if (is_prog_info_disp && !paused)
6348 {
6349 hideAll = true;
6350 }
6351 else if (IncludeStatusOSD)
6352 {
6353 showStatus = true;
6354 }
6355 else
6356 {
6357 if (has_prog_info)
6359 }
6360
6361 if (hideAll || showStatus)
6362 emit HideAll();
6363
6364 if (showStatus)
6365 {
6366 osdInfo info;
6368 {
6369 info.text["title"] = (paused ? tr("Paused") : tr("Position"));
6372 emit ChangeOSDPositionUpdates(true);
6373 }
6374 else
6375 {
6376 emit ChangeOSDPositionUpdates(false);
6377 }
6378 }
6379 else
6380 {
6381 emit ChangeOSDPositionUpdates(false);
6382 }
6383}
6384
6388void TV::UpdateOSDProgInfo(const char *WhichInfo)
6389{
6390 InfoMap infoMap;
6392 if (m_player)
6393 m_player->GetCodecDescription(infoMap);
6394
6395 // Clear previous osd and add new info
6396 emit HideAll();
6397 emit ChangeOSDText(WhichInfo, infoMap, kOSDTimeout_Long);
6398}
6399
6400void TV::UpdateOSDStatus(osdInfo &Info, int Type, OSDTimeout Timeout)
6401{
6402 OSD *osd = GetOSDL();
6403 if (osd)
6404 {
6406 osd->SetValues(OSD_WIN_STATUS, Info.values, Timeout);
6407 emit ChangeOSDText(OSD_WIN_STATUS, Info.text, Timeout);
6408 if (Type != kOSDFunctionalType_Default)
6409 osd->SetFunctionalWindow(OSD_WIN_STATUS, static_cast<OSDFunctionalType>(Type));
6410 }
6411 ReturnOSDLock();
6412}
6413
6414void TV::UpdateOSDStatus(const QString& Title, const QString& Desc,
6415 const QString& Value, int Type, const QString& Units,
6416 int Position, OSDTimeout Timeout)
6417{
6418 osdInfo info;
6419 info.values.insert("position", Position);
6420 info.values.insert("relposition", Position);
6421 info.text.insert("title", Title);
6422 info.text.insert("description", Desc);
6423 info.text.insert("value", Value);
6424 info.text.insert("units", Units);
6425 UpdateOSDStatus(info, Type, Timeout);
6426}
6427
6428void TV::UpdateOSDSeekMessage(const QString &Msg, enum OSDTimeout Timeout)
6429{
6430 LOG(VB_PLAYBACK, LOG_INFO, QString("UpdateOSDSeekMessage(%1, %2)").arg(Msg).arg(Timeout));
6431
6432 osdInfo info;
6434 {
6436 info.text["title"] = Msg;
6437 UpdateOSDStatus(info, osdtype, Timeout);
6438 emit ChangeOSDPositionUpdates(true);
6439 }
6440}
6441
6443{
6445 return;
6446 QString displayName = CardUtil::GetDisplayName(m_playerContext.GetCardID());
6447 emit ChangeOSDMessage(displayName);
6448}
6449
6453void TV::UpdateOSDSignal(const QStringList &List)
6454{
6455 OSD *osd = GetOSDL();
6456 if (!osd || m_overlayState.m_browsing || !m_queuedChanNum.isEmpty())
6457 {
6458 if (&m_playerContext.m_lastSignalMsg != &List)
6460 ReturnOSDLock();
6461 m_signalMonitorTimerId = StartTimer(1ms, __LINE__);
6462 return;
6463 }
6464 ReturnOSDLock();
6465
6467
6471 infoMap["callsign"].isEmpty())
6472 {
6475 if (m_player)
6476 m_player->GetCodecDescription(infoMap);
6477
6480 }
6481
6482 int i = 0;
6483 SignalMonitorList::const_iterator it;
6484 for (it = slist.begin(); it != slist.end(); ++it)
6485 if ("error" == it->GetShortName())
6486 infoMap[QString("error%1").arg(i++)] = it->GetName();
6487 i = 0;
6488 for (it = slist.begin(); it != slist.end(); ++it)
6489 if ("message" == it->GetShortName())
6490 infoMap[QString("message%1").arg(i++)] = it->GetName();
6491
6492 int sig = 0;
6493 double snr = 0.0;
6494 uint ber = 0xffffffff;
6495 int pos = -1;
6496 int tuned = -1;
6497 QString pat("");
6498 QString pmt("");
6499 QString mgt("");
6500 QString vct("");
6501 QString nit("");
6502 QString sdt("");
6503 QString crypt("");
6504 QString err;
6505 QString msg;
6506 for (it = slist.begin(); it != slist.end(); ++it)
6507 {
6508 if ("error" == it->GetShortName())
6509 {
6510 err = it->GetName();
6511 continue;
6512 }
6513
6514 if ("message" == it->GetShortName())
6515 {
6516 msg = it->GetName();
6517 LOG(VB_GENERAL, LOG_INFO, "msg: " + msg);
6518 continue;
6519 }
6520
6521 infoMap[it->GetShortName()] = QString::number(it->GetValue());
6522 if ("signal" == it->GetShortName())
6523 sig = it->GetNormalizedValue(0, 100);
6524 else if ("snr" == it->GetShortName())
6525 snr = it->GetValue();
6526 else if ("ber" == it->GetShortName())
6527 ber = static_cast<uint>(it->GetValue());
6528 else if ("pos" == it->GetShortName())
6529 pos = it->GetValue();
6530 else if ("script" == it->GetShortName())
6531 tuned = it->GetValue();
6532 else if ("seen_pat" == it->GetShortName())
6533 pat = it->IsGood() ? "a" : "_";
6534 else if ("matching_pat" == it->GetShortName())
6535 pat = it->IsGood() ? "A" : pat;
6536 else if ("seen_pmt" == it->GetShortName())
6537 pmt = it->IsGood() ? "m" : "_";
6538 else if ("matching_pmt" == it->GetShortName())
6539 pmt = it->IsGood() ? "M" : pmt;
6540 else if ("seen_mgt" == it->GetShortName())
6541 mgt = it->IsGood() ? "g" : "_";
6542 else if ("matching_mgt" == it->GetShortName())
6543 mgt = it->IsGood() ? "G" : mgt;
6544 else if ("seen_vct" == it->GetShortName())
6545 vct = it->IsGood() ? "v" : "_";
6546 else if ("matching_vct" == it->GetShortName())
6547 vct = it->IsGood() ? "V" : vct;
6548 else if ("seen_nit" == it->GetShortName())
6549 nit = it->IsGood() ? "n" : "_";
6550 else if ("matching_nit" == it->GetShortName())
6551 nit = it->IsGood() ? "N" : nit;
6552 else if ("seen_sdt" == it->GetShortName())
6553 sdt = it->IsGood() ? "s" : "_";
6554 else if ("matching_sdt" == it->GetShortName())
6555 sdt = it->IsGood() ? "S" : sdt;
6556 else if ("seen_crypt" == it->GetShortName())
6557 crypt = it->IsGood() ? "c" : "_";
6558 else if ("matching_crypt" == it->GetShortName())
6559 crypt = it->IsGood() ? "C" : crypt;
6560 }
6561 if (sig)
6562 infoMap["signal"] = QString::number(sig); // use normalized value
6563
6564 bool allGood = SignalMonitorValue::AllGood(slist);
6565 QString tuneCode;
6566 QString slock = ("1" == infoMap["slock"]) ? "L" : "l";
6567 QString lockMsg = (slock=="L") ? tr("Partial Lock") : tr("No Lock");
6568 QString sigMsg = allGood ? tr("Lock") : lockMsg;
6569
6570 QString sigDesc = tr("Signal %1%").arg(sig,2);
6571 if (snr > 0.0)
6572 sigDesc += " | " + tr("S/N %1dB").arg(log10(snr), 3, 'f', 1);
6573 if (ber != 0xffffffff)
6574 sigDesc += " | " + tr("BE %1", "Bit Errors").arg(ber, 2);
6575 if ((pos >= 0) && (pos < 100))
6576 sigDesc += " | " + tr("Rotor %1%").arg(pos,2);
6577
6578 if (tuned == 1)
6579 tuneCode = "t";
6580 else if (tuned == 2)
6581 tuneCode = "F";
6582 else if (tuned == 3)
6583 tuneCode = "T";
6584 else
6585 tuneCode = "_";
6586
6587 sigDesc = sigDesc + QString(" | (%1%2%3%4%5%6%7%8%9) %10")
6588 .arg(tuneCode, slock, pat, pmt, mgt, vct,
6589 nit, sdt, crypt)
6590 .arg(sigMsg);
6591
6592 if (!err.isEmpty())
6593 sigDesc = err;
6594 else if (!msg.isEmpty())
6595 sigDesc = msg;
6596
6597 infoMap["description"] = sigDesc;
6599
6602
6603 // Turn off lock timer if we have an "All Good" or good PMT
6604 if (allGood || (pmt == "M"))
6605 {
6606 m_lockTimerOn = false;
6608 }
6609}
6610
6612{
6613 bool timed_out = false;
6614
6616 {
6617 QString input = m_playerContext.m_recorder->GetInput();
6619 timed_out = m_lockTimerOn && m_lockTimer.hasExpired(timeout);
6620 }
6621
6622 OSD *osd = GetOSDL();
6623
6624 if (!osd)
6625 {
6626 if (timed_out)
6627 {
6628 LOG(VB_GENERAL, LOG_ERR, LOC +
6629 "You have no OSD, but tuning has already taken too long.");
6630 }
6631 ReturnOSDLock();
6632 return;
6633 }
6634
6635 bool showing = osd->DialogVisible(OSD_DLG_INFO);
6636 if (!timed_out)
6637 {
6638 if (showing)
6639 emit DialogQuit();
6640 ReturnOSDLock();
6641 return;
6642 }
6643
6644 if (showing)
6645 {
6646 ReturnOSDLock();
6647 return;
6648 }
6649
6650 ReturnOSDLock();
6651
6652 // create dialog...
6653 static QString s_chanUp = GET_KEY("TV Playback", ACTION_CHANNELUP);
6654 static QString s_chanDown = GET_KEY("TV Playback", ACTION_CHANNELDOWN);
6655 static QString s_nextSrc = GET_KEY("TV Playback", "NEXTSOURCE");
6656 static QString s_togCards = GET_KEY("TV Playback", "NEXTINPUT");
6657
6658 QString message = tr(
6659 "You should have received a channel lock by now. "
6660 "You can continue to wait for a signal, or you "
6661 "can change the channel with %1 or %2, change "
6662 "video source (%3), inputs (%4), etc.")
6663 .arg(s_chanUp, s_chanDown, s_nextSrc, s_togCards);
6664
6665 emit ChangeOSDDialog(
6666 { .m_dialogName=OSD_DLG_INFO,
6667 .m_message=message,
6668 .m_timeout=0ms,
6669 .m_buttons={ {tr("OK"), "DIALOG_INFO_CHANNELLOCK_0" } },
6670 .m_back={ .m_text="", .m_data="DIALOG_INFO_CHANNELLOCK_0", .m_exit=true } });
6671}
6672
6673bool TV::CalcPlayerSliderPosition(osdInfo &info, bool paddedFields) const
6674{
6675 bool result = false;
6677 if (m_player)
6678 {
6679 m_player->UpdateSliderInfo(info, paddedFields);
6680 result = true;
6681 }
6683 return result;
6684}
6685
6686void TV::HideOSDWindow(const char *window)
6687{
6688 OSD *osd = GetOSDL();
6689 if (osd)
6690 osd->HideWindow(window);
6691 ReturnOSDLock();
6692}
6693
6695{
6696 // Make sure the LCD information gets updated shortly
6697 if (m_lcdTimerId)
6699 m_lcdTimerId = StartTimer(1ms, __LINE__);
6700}
6701
6703{
6704 LCD *lcd = LCD::Get();
6705 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
6706 if (!lcd || !m_playerContext.m_playingInfo)
6707 {
6708 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
6709 return;
6710 }
6711
6712 QString title = m_playerContext.m_playingInfo->GetTitle();
6713 QString subtitle = m_playerContext.m_playingInfo->GetSubtitle();
6715
6716 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
6717
6718 if ((callsign != m_lcdCallsign) || (title != m_lcdTitle) ||
6719 (subtitle != m_lcdSubtitle))
6720 {
6721 lcd->switchToChannel(callsign, title, subtitle);
6722 m_lcdCallsign = callsign;
6723 m_lcdTitle = title;
6724 m_lcdSubtitle = subtitle;
6725 }
6726}
6727
6729{
6730 LCD *lcd = LCD::Get();
6732 return;
6733
6735 QString dvdName;
6736 QString dvdSerial;
6737 QString mainStatus;
6738 QString subStatus;
6739
6740 if (!dvd->GetNameAndSerialNum(dvdName, dvdSerial))
6741 dvdName = tr("DVD");
6742
6743 if (dvd->IsInMenu())
6744 {
6745 mainStatus = tr("Menu");
6746 }
6747 else if (dvd->IsInStillFrame())
6748 {
6749 mainStatus = tr("Still Frame");
6750 }
6751 else
6752 {
6753 int playingTitle = 0;
6754 int playingPart = 0;
6755
6756 dvd->GetPartAndTitle(playingPart, playingTitle);
6757 int totalParts = dvd->NumPartsInTitle();
6758
6759 mainStatus = tr("Title: %1 (%2)").arg(playingTitle)
6760 .arg(MythDate::formatTime(dvd->GetTotalTimeOfTitle(), "HH:mm"));
6761 subStatus = tr("Chapter: %1/%2").arg(playingPart).arg(totalParts);
6762 }
6763 if ((dvdName != m_lcdCallsign) || (mainStatus != m_lcdTitle) || (subStatus != m_lcdSubtitle))
6764 {
6765 lcd->switchToChannel(dvdName, mainStatus, subStatus);
6766 m_lcdCallsign = dvdName;
6767 m_lcdTitle = mainStatus;
6768 m_lcdSubtitle = subStatus;
6769 }
6770}
6771
6773{
6774 int dummy = 0;
6775 TV* tv = AcquireRelease(dummy, true);
6776 if (tv)
6777 {
6778 tv->GetPlayerReadLock();
6779 bool result = !TV::IsTunableOn(tv->GetPlayerContext(), ChanId).empty();
6780 tv->ReturnPlayerLock();
6781 AcquireRelease(dummy, false);
6782 return result;
6783 }
6784
6785 return !TV::IsTunableOn(nullptr, ChanId).empty();
6786}
6787
6789{
6790 return !IsTunableOn(&m_playerContext, ChanId).empty();
6791}
6792
6793static QString toCommaList(const QVector<uint> &list)
6794{
6795 QString ret = "";
6796 for (uint i : std::as_const(list))
6797 ret += QString("%1,").arg(i);
6798
6799 if (!ret.isEmpty())
6800 return ret.left(ret.length()-1);
6801
6802 return "";
6803}
6804
6805QVector<uint> TV::IsTunableOn(PlayerContext* Context, uint ChanId)
6806{
6807 QVector<uint> tunable_cards;
6808
6809 if (!ChanId)
6810 {
6811 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("ChanId (%1) - no").arg(ChanId));
6812 return tunable_cards;
6813 }
6814
6815 uint mplexid = ChannelUtil::GetMplexID(ChanId);
6816 mplexid = (32767 == mplexid) ? 0 : mplexid;
6817
6818 uint excluded_input = 0;
6819 if (Context && Context->m_recorder && Context->m_pseudoLiveTVState == kPseudoNormalLiveTV)
6820 excluded_input = Context->GetCardID();
6821
6822 uint sourceid = ChannelUtil::GetSourceIDForChannel(ChanId);
6823
6824 std::vector<InputInfo> inputs = RemoteRequestFreeInputInfo(excluded_input);
6825
6826 for (auto & input : inputs)
6827 {
6828 if (input.m_sourceId != sourceid)
6829 continue;
6830
6831 if (input.m_mplexId &&
6832 input.m_mplexId != mplexid)
6833 continue;
6834
6835 if (!input.m_mplexId && input.m_chanId &&
6836 input.m_chanId != ChanId)
6837 continue;
6838
6839 tunable_cards.push_back(input.m_inputId);
6840 }
6841
6842 if (tunable_cards.empty())
6843 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("ChanId (%1) - no").arg(ChanId));
6844 else
6845 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("ChanId (%1) yes { %2 }").arg(ChanId).arg(toCommaList(tunable_cards)));
6846 return tunable_cards;
6847}
6848
6849void TV::Embed(bool Embed, QRect Rect, const QStringList& Data)
6850{
6851 emit EmbedPlayback(Embed, Rect);
6852 if (Embed)
6853 return;
6854
6855 emit ResizeScreenForVideo();
6856
6857 // m_playerBounds is not applicable when switching modes so
6858 // skip this logic in that case.
6859 if (!m_dbUseVideoModes)
6861
6862 // Restore pause
6864
6865 if (!m_weDisabledGUI)
6866 {
6867 m_weDisabledGUI = true;
6869 }
6870
6871 m_ignoreKeyPresses = false;
6872
6873 // additional data returned by PlaybackBox
6874 if (!Data.isEmpty())
6875 {
6876 ProgramInfo pginfo(Data);
6877 if (pginfo.HasPathname() || pginfo.GetChanID())
6879 }
6880}
6881
6882bool TV::DoSetPauseState(bool Pause)
6883{
6884 bool waspaused = ContextIsPaused(__FILE__, __LINE__);
6885 float time = 0.0F;
6886 if (Pause ^ waspaused)
6887 time = DoTogglePauseStart();
6888 if (Pause ^ waspaused)
6889 DoTogglePauseFinish(time, false);
6890 return waspaused;
6891}
6892
6893void TV::DoEditSchedule(int EditType, const QString & EditArg)
6894{
6895 // Prevent nesting of the pop-up UI
6897 return;
6898
6899 if ((EditType == kScheduleProgramGuide && !RunProgramGuidePtr) ||
6900 (EditType == kScheduleProgramFinder && !RunProgramFinderPtr) ||
6901 (EditType == kScheduledRecording && !RunScheduleEditorPtr) ||
6902 (EditType == kViewSchedule && !RunViewScheduledPtr) ||
6903 (EditType == kPlaybackBox && !RunPlaybackBoxPtr))
6904 {
6905 return;
6906 }
6907
6909
6910 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
6912 {
6913 LOG(VB_GENERAL, LOG_ERR, LOC + "no active ctx playingInfo.");
6914 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
6916 return;
6917 }
6918
6919 // Collect channel info
6921 uint chanid = pginfo.GetChanID();
6922 QString channum = pginfo.GetChanNum();
6923 QDateTime starttime = MythDate::current();
6924 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
6925
6926 ClearOSD();
6927
6928 // Pause playback as needed...
6929 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
6930 bool pause = !m_player || (!StateIsLiveTV(GetState()) && !m_dbContinueEmbedded);
6931 if (m_player)
6932 {
6933 pause |= !m_player->GetVideoOutput();
6934 pause |= m_player->IsPaused();
6935 if (!pause)
6936 pause |= (!StateIsLiveTV(GetState()) && m_player->IsNearEnd());
6937 }
6938 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
6939
6940 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Pausing player: %1").arg(pause));
6942
6943 // Resize window to the MythTV GUI size
6944 MythDisplay* display = m_mainWindow->GetDisplay();
6945 if (display->UsingVideoModes())
6946 {
6947 bool hide = display->NextModeIsLarger(display->GetGUIResolution());
6948 if (hide)
6949 m_mainWindow->hide();
6950 display->SwitchToGUI(true);
6951 if (hide)
6952 m_mainWindow->Show();
6953 }
6954
6957#ifdef Q_OS_ANDROID
6958 m_mainWindow->Show();
6959#else
6960 m_mainWindow->show();
6961#endif
6963
6964
6965 // Actually show the pop-up UI
6966 switch (EditType)
6967 {
6969 {
6970 RunProgramGuidePtr(chanid, channum, starttime, this,
6971 !pause, true, m_channelGroupId);
6972 m_ignoreKeyPresses = true;
6973 break;
6974 }
6976 {
6977 RunProgramFinderPtr(this, !pause, true);
6978 m_ignoreKeyPresses = true;
6979 break;
6980 }
6982 {
6983 /*
6984 4 = plPeopleSearch in mythfrontend/proglist.h
6985 This could be expanded to view other program lists...
6986 */
6987 RunProgramListPtr(this, 4, EditArg);
6988 m_ignoreKeyPresses = true;
6989 break;
6990 }
6992 {
6993 RunScheduleEditorPtr(&pginfo, reinterpret_cast<void*>(this));
6994 m_ignoreKeyPresses = true;
6995 break;
6996 }
6997 case kViewSchedule:
6998 {
6999 RunViewScheduledPtr(reinterpret_cast<void*>(this), !pause);
7000 m_ignoreKeyPresses = true;
7001 break;
7002 }
7003 case kPlaybackBox:
7004 {
7005 RunPlaybackBoxPtr(reinterpret_cast<void*>(this), !pause);
7006 m_ignoreKeyPresses = true;
7007 break;
7008 }
7009 }
7010
7011 // We are embedding in a mythui window so assuming no one
7012 // else has disabled painting show the MythUI window again.
7013 if (m_weDisabledGUI)
7014 {
7016 m_weDisabledGUI = false;
7017 }
7018}
7019
7020void TV::EditSchedule(int EditType, const QString& arg)
7021{
7022 // post the request so the guide will be created in the UI thread
7023 QString message = QString("START_EPG %1 %2").arg(EditType).arg(arg);
7024 auto* me = new MythEvent(message);
7025 QCoreApplication::postEvent(this, me);
7026}
7027
7028void TV::VolumeChange(bool Up, int NewVolume)
7029{
7031 return;
7032
7033 if ((m_audioState.m_muteState == kMuteAll) && (Up || NewVolume >= 0))
7034 emit ChangeMuteState();
7035
7036 emit ChangeVolume(Up, NewVolume);
7037
7039 {
7040 if (LCD *lcd = LCD::Get())
7041 {
7042 QString appName = tr("Video");
7043
7044 if (StateIsLiveTV(GetState()))
7045 appName = tr("TV");
7046
7048 appName = tr("DVD");
7049
7050 lcd->switchToVolume(appName);
7051 lcd->setVolumeLevel(static_cast<float>(m_audioState.m_volume) / 100);
7052
7055 m_lcdVolumeTimerId = StartTimer(2s, __LINE__);
7056 }
7057 }
7058}
7059
7061{
7062 if (m_playerContext.m_tsNormal == 1.0F)
7063 {
7065 }
7066 else
7067 {
7070 }
7071 ChangeTimeStretch(0, false);
7072}
7073
7074void TV::ChangeTimeStretch(int Dir, bool AllowEdit)
7075{
7076 const float kTimeStretchMin = 0.125;
7077 const float kTimeStretchMax = 2.0;
7078 const float kTimeStretchStep = 0.05F;
7079 float new_ts_normal = m_playerContext.m_tsNormal + (kTimeStretchStep * Dir);
7080 m_stretchAdjustment = AllowEdit;
7081
7082 if (new_ts_normal > kTimeStretchMax &&
7083 m_playerContext.m_tsNormal < kTimeStretchMax)
7084 {
7085 new_ts_normal = kTimeStretchMax;
7086 }
7087 else if (new_ts_normal < kTimeStretchMin &&
7088 m_playerContext.m_tsNormal > kTimeStretchMin)
7089 {
7090 new_ts_normal = kTimeStretchMin;
7091 }
7092
7093 if (new_ts_normal > kTimeStretchMax ||
7094 new_ts_normal < kTimeStretchMin)
7095 {
7096 return;
7097 }
7098
7099 m_playerContext.m_tsNormal = kTimeStretchStep * lroundf(new_ts_normal / kTimeStretchStep);
7100
7101 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
7102 if (m_player && !m_player->IsPaused())
7104 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
7105
7107 {
7108 if (!AllowEdit)
7109 {
7111 }
7112 else
7113 {
7114 UpdateOSDStatus(tr("Adjust Time Stretch"), tr("Time Stretch"),
7115 QString::number(static_cast<double>(m_playerContext.m_tsNormal), 'f', 2),
7117 static_cast<int>(m_playerContext.m_tsNormal * (1000 / kTimeStretchMax)),
7119 emit ChangeOSDPositionUpdates(false);
7120 }
7121 }
7122
7123 SetSpeedChangeTimer(0ms, __LINE__);
7124}
7125
7127{
7128 QString text;
7129
7130 // increment sleep index, cycle through
7131 if (++m_sleepIndex == kSleepTimes.size())
7132 m_sleepIndex = 0;
7133
7134 // set sleep timer to next sleep_index timeout
7135 if (m_sleepTimerId)
7136 {
7138 m_sleepTimerId = 0;
7139 m_sleepTimerTimeout = 0ms;
7140 }
7141
7142 if (kSleepTimes[m_sleepIndex].milliseconds != 0ms)
7143 {
7146 }
7147
7148 text = tr("Sleep ") + " " + kSleepTimes[m_sleepIndex].dispString;
7149 emit ChangeOSDMessage(text);
7150}
7151
7153{
7155 m_sleepTimerId = 0;
7156
7157 QString message = tr("MythTV was set to sleep after %1 minutes and will exit in %d seconds.\n"
7158 "Do you wish to continue watching?")
7159 .arg(duration_cast<std::chrono::minutes>(m_sleepTimerTimeout).count());
7160
7161 emit ChangeOSDDialog(
7162 { .m_dialogName=OSD_DLG_SLEEP,
7163 .m_message=message,
7164 .m_timeout=kSleepTimerDialogTimeout,
7165 .m_buttons={ { tr("Yes"), "DIALOG_SLEEP_YES_0" },
7166 { tr("No"), "DIALOG_SLEEP_NO_0" } }});
7167
7169}
7170
7171void TV::HandleOSDSleep(const QString& Action)
7172{
7174 return;
7175
7176 if (Action == "YES")
7177 {
7179 {
7182 }
7184 }
7185 else
7186 {
7187 LOG(VB_GENERAL, LOG_INFO, LOC + "No longer watching TV, exiting");
7188 SetExitPlayer(true, true);
7189 }
7190}
7191
7193{
7196
7197 LOG(VB_GENERAL, LOG_INFO, LOC + "Sleep timeout reached, exiting player.");
7198
7199 SetExitPlayer(true, true);
7200}
7201
7211{
7213 m_idleTimerId = 0;
7214
7215 QString message = tr("MythTV has been idle for %1 minutes and "
7216 "will exit in %d seconds. Are you still watching?")
7217 .arg(duration_cast<std::chrono::minutes>(m_dbIdleTimeout).count());
7218
7219 emit ChangeOSDDialog(
7220 { .m_dialogName=OSD_DLG_IDLE,
7221 .m_message=message,
7222 .m_timeout=kIdleTimerDialogTimeout,
7223 .m_buttons={ { tr("Yes"), "DIALOG_IDLE_YES_0" },
7224 { tr("No"), "DIALOG_IDLE_NO_0" }}});
7225
7227}
7228
7229void TV::HandleOSDIdle(const QString& Action)
7230{
7232 return;
7233
7234 if (Action == "YES")
7235 {
7237 {
7240 }
7241 if (m_idleTimerId)
7244 }
7245 else
7246 {
7247 LOG(VB_GENERAL, LOG_INFO, LOC + "No longer watching LiveTV, exiting");
7248 SetExitPlayer(true, true);
7249 }
7250}
7251
7253{
7256
7259 {
7260 LOG(VB_GENERAL, LOG_INFO, LOC + "Idle timeout reached, leaving LiveTV");
7261 SetExitPlayer(true, true);
7262 }
7264}
7265
7266// Retrieve the proper MythTVMenu object from The TV object, given its
7267// id number. This is used to find the original menu again, instead of
7268// serializing/deserializing the entire MythTVMenu object to/from a
7269// QVariant.
7271{
7272 switch (id) {
7273 case kMenuIdPlayback:
7274 return m_playbackMenu;
7276 return m_playbackCompactMenu;
7277 case kMenuIdCutlist:
7278 return m_cutlistMenu;
7280 return m_cutlistCompactMenu;
7281 default:
7282 return dummy_menubase;
7283 }
7284}
7285
7288{
7290 {
7292 return;
7293 }
7294
7295 if (Event->type() == MythEvent::kMythUserMessage)
7296 {
7297 auto *me = dynamic_cast<MythEvent*>(Event);
7298 if (me == nullptr)
7299 return;
7300 QString message = me->Message();
7301
7302 if (message.isEmpty())
7303 return;
7304
7305 std::chrono::milliseconds timeout = 0ms;
7306 if (me->ExtraDataCount() == 1)
7307 {
7308 auto t = std::chrono::seconds(me->ExtraData(0).toInt());
7309 if (t > 0s && t < 1000s)
7310 timeout = t;
7311 }
7312
7313 if (timeout > 0ms)
7314 message += " (%d)";
7315
7316 emit ChangeOSDDialog(
7317 { .m_dialogName=OSD_DLG_CONFIRM,
7318 .m_message=message,
7319 .m_timeout=timeout });
7320 return;
7321 }
7322
7324 {
7325 auto *b = reinterpret_cast<UpdateBrowseInfoEvent*>(Event);
7327 return;
7328 }
7329
7331 {
7332 auto *dce = reinterpret_cast<DialogCompletionEvent*>(Event);
7333 if (dce->GetData().userType() == qMetaTypeId<MythTVMenuNodeTuple>())
7334 {
7335 auto data = dce->GetData().value<MythTVMenuNodeTuple>();
7336 const MythTVMenu& Menu = getMenuFromId(data.m_id);
7337 QDomNode Node = Menu.GetNodeFromPath(data.m_path);
7338 if (dce->GetResult() == -1) // menu exit/back
7339 PlaybackMenuShow(Menu, Node.parentNode(), Node);
7340 else
7341 PlaybackMenuShow(Menu, Node, QDomNode());
7342 }
7343 else
7344 {
7345 OSDDialogEvent(dce->GetResult(), dce->GetResultText(), dce->GetData().toString());
7346 }
7347 return;
7348 }
7349
7350 // Stop DVD playback cleanly when the DVD is ejected
7351 if (Event->type() == MythMediaEvent::kEventType)
7352 {
7355 if (state != kState_WatchingDVD)
7356 {
7358 return;
7359 }
7360
7361 auto *me = dynamic_cast<MythMediaEvent*>(Event);
7362 if (me == nullptr)
7363 return;
7364 MythMediaDevice *device = me->getDevice();
7365
7367
7368 if (device && filename.endsWith(device->getDevicePath()) && (device->getStatus() == MEDIASTAT_OPEN))
7369 {
7370 LOG(VB_GENERAL, LOG_NOTICE, "DVD has been ejected, exiting playback");
7371 PrepareToExitPlayer(__LINE__);
7372 SetExitPlayer(true, true);
7373 }
7375 return;
7376 }
7377
7378 if (Event->type() != MythEvent::kMythEventMessage)
7379 return;
7380
7381 uint cardnum = 0;
7382 auto *me = dynamic_cast<MythEvent*>(Event);
7383 if (me == nullptr)
7384 return;
7385 QString message = me->Message();
7386
7387 // TODO Go through these and make sure they make sense...
7388 QStringList tokens = message.split(" ", Qt::SkipEmptyParts);
7389
7390 if (me->ExtraDataCount() == 1)
7391 {
7393 int value = me->ExtraData(0).toInt();
7394 if (message == ACTION_SETVOLUME)
7395 VolumeChange(false, value);
7396 else if (message == ACTION_SETAUDIOSYNC)
7397 emit ChangeAudioOffset(0ms, std::chrono::milliseconds(value));
7398 else if (message == ACTION_SETBRIGHTNESS)
7400 else if (message == ACTION_SETCONTRAST)
7402 else if (message == ACTION_SETCOLOUR)
7404 else if (message == ACTION_SETHUE)
7406 else if (message == ACTION_JUMPCHAPTER)
7407 DoJumpChapter(value);
7408 else if (message == ACTION_SWITCHTITLE)
7409 DoSwitchTitle(value - 1);
7410 else if (message == ACTION_SWITCHANGLE)
7411 DoSwitchAngle(value);
7412 else if (message == ACTION_SEEKABSOLUTE)
7413 DoSeekAbsolute(value, /*honorCutlist*/true);
7415 }
7416
7417 if (message == ACTION_SCREENSHOT)
7418 {
7419 int width = 0;
7420 int height = 0;
7421 QString filename;
7422
7423 if (me->ExtraDataCount() >= 2)
7424 {
7425 width = me->ExtraData(0).toInt();
7426 height = me->ExtraData(1).toInt();
7427
7428 if (me->ExtraDataCount() == 3)
7429 filename = me->ExtraData(2);
7430 }
7431 MythMainWindow::ScreenShot(width, height, filename);
7432 }
7433 else if (message == ACTION_GETSTATUS)
7434 {
7435 GetStatus();
7436 }
7437 else if (message.startsWith("DONE_RECORDING"))
7438 {
7439 std::chrono::seconds seconds = 0s;
7440 //long long frames = 0;
7441 int NUMTOKENS = 4; // Number of tokens expected
7442 if (tokens.size() == NUMTOKENS)
7443 {
7444 cardnum = tokens[1].toUInt();
7445 seconds = std::chrono::seconds(tokens[2].toInt());
7446 //frames = tokens[3].toLongLong();
7447 }
7448 else
7449 {
7450 LOG(VB_GENERAL, LOG_ERR, QString("DONE_RECORDING event received "
7451 "with invalid number of arguments, "
7452 "%1 expected, %2 actual")
7453 .arg(NUMTOKENS-1)
7454 .arg(tokens.size()-1));
7455 return;
7456 }
7457
7460 {
7462 {
7463 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
7464 if (m_player)
7465 {
7467 if (seconds > 0s)
7468 m_player->SetLength(seconds);
7469 }
7470 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
7471
7474 }
7475 }
7477 {
7480 {
7481 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
7482 if (m_player)
7483 {
7485 if (seconds > 0s)
7486 m_player->SetLength(seconds);
7487 }
7488 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
7489 }
7490 }
7492 }
7493
7494 if (message.startsWith("ASK_RECORDING "))
7495 {
7496 int timeuntil = 0;
7497 bool hasrec = false;
7498 bool haslater = false;
7499 if (tokens.size() >= 5)
7500 {
7501 cardnum = tokens[1].toUInt();
7502 timeuntil = tokens[2].toInt();
7503 hasrec = (tokens[3].toInt() != 0);
7504 haslater = (tokens[4].toInt() != 0);
7505 }
7506 LOG(VB_GENERAL, LOG_DEBUG,
7507 LOC + message + QString(" hasrec: %1 haslater: %2")
7508 .arg(hasrec).arg(haslater));
7509
7512 AskAllowRecording(me->ExtraDataList(), timeuntil, hasrec, haslater);
7513
7515 }
7516
7517 if (message.startsWith("QUIT_LIVETV"))
7518 {
7519 cardnum = (tokens.size() >= 2) ? tokens[1].toUInt() : 0;
7520
7522 bool match = m_playerContext.GetCardID() == cardnum;
7523 if (match && m_playerContext.m_recorder)
7524 {
7525 SetLastProgram(nullptr);
7526 m_jumpToProgram = true;
7527 SetExitPlayer(true, false);
7528 }
7530 }
7531
7532 if (message.startsWith("LIVETV_WATCH"))
7533 {
7534 int watch = 0;
7535 if (tokens.size() >= 3)
7536 {
7537 cardnum = tokens[1].toUInt();
7538 watch = tokens[2].toInt();
7539 }
7540
7542 if (m_playerContext.GetCardID() == cardnum)
7543 {
7544 if (watch)
7545 {
7546 ProgramInfo pi(me->ExtraDataList());
7547 if (pi.HasPathname() || pi.GetChanID())
7548 {
7551 m_pseudoChangeChanTimerId = StartTimer(0ms, __LINE__);
7552 }
7553 }
7554 else
7555 {
7557 }
7558 }
7560 }
7561
7562 if (message.startsWith("LIVETV_CHAIN"))
7563 {
7564 QString id;
7565 if ((tokens.size() >= 2) && tokens[1] == "UPDATE")
7566 id = tokens[2];
7567
7570 m_playerContext.UpdateTVChain(me->ExtraDataList());
7572 }
7573
7574 if (message.startsWith("EXIT_TO_MENU"))
7575 {
7577 PrepareToExitPlayer(__LINE__);
7578 SetExitPlayer(true, true);
7579 emit DisableEdit(-1);
7581 }
7582
7583 if (message.startsWith("SIGNAL"))
7584 {
7585 cardnum = (tokens.size() >= 2) ? tokens[1].toUInt() : 0;
7586 const QStringList& signalList = me->ExtraDataList();
7587
7589 OSD *osd = GetOSDL();
7590 if (osd)
7591 {
7592 if (m_playerContext.m_recorder && (m_playerContext.GetCardID() == cardnum) && !signalList.empty())
7593 {
7594 UpdateOSDSignal(signalList);
7596 }
7597 }
7598 ReturnOSDLock();
7600 }
7601
7602 if (message.startsWith("NETWORK_CONTROL"))
7603 {
7604 if ((tokens.size() >= 2) &&
7605 (tokens[1] != "ANSWER") && (tokens[1] != "RESPONSE"))
7606 {
7607 QStringList tokens2 = message.split(" ", Qt::SkipEmptyParts);
7608 if ((tokens2.size() >= 2) &&
7609 (tokens2[1] != "ANSWER") && (tokens2[1] != "RESPONSE"))
7610 {
7613 m_networkControlTimerId = StartTimer(1ms, __LINE__);
7614 }
7615 }
7616 }
7617
7618 if (message.startsWith("START_EPG"))
7619 {
7620 int editType = tokens[1].toInt();
7621 QString arg = message.section(" ", 2, -1);
7622 DoEditSchedule(editType, arg);
7623 }
7624
7625 if (message.startsWith("COMMFLAG_START") && (tokens.size() >= 2))
7626 {
7627 uint evchanid = 0;
7628 QDateTime evrecstartts;
7629 ProgramInfo::ExtractKey(tokens[1], evchanid, evrecstartts);
7630
7632 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
7633 bool doit = ((m_playerContext.m_playingInfo) &&
7634 (m_playerContext.m_playingInfo->GetChanID() == evchanid) &&
7636 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
7637
7638 if (doit)
7639 {
7640 QString msg = "COMMFLAG_REQUEST ";
7641 msg += ProgramInfo::MakeUniqueKey(evchanid, evrecstartts);
7643 }
7645 }
7646
7647 if (message.startsWith("COMMFLAG_UPDATE") && (tokens.size() >= 3))
7648 {
7649 uint evchanid = 0;
7650 QDateTime evrecstartts;
7651 ProgramInfo::ExtractKey(tokens[1], evchanid, evrecstartts);
7652
7654 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
7655 bool doit = ((m_playerContext.m_playingInfo) &&
7656 (m_playerContext.m_playingInfo->GetChanID() == evchanid) &&
7658 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
7659
7660 if (doit)
7661 {
7662 frm_dir_map_t newMap;
7663 QStringList mark;
7664 QStringList marks = tokens[2].split(",", Qt::SkipEmptyParts);
7665 for (int j = 0; j < marks.size(); j++)
7666 {
7667 mark = marks[j].split(":", Qt::SkipEmptyParts);
7668 if (marks.size() >= 2)
7669 newMap[mark[0].toULongLong()] = static_cast<MarkTypes>(mark[1].toInt());
7670 }
7671 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
7672 if (m_player)
7673 m_player->SetCommBreakMap(newMap);
7674 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
7675 }
7677 }
7678
7679 if (message == "NOTIFICATION")
7680 {
7681 if (!GetNotificationCenter())
7682 return;
7683 MythNotification mn(*me);
7685 }
7686}
7687
7689{
7691 if (bi.m_chanId)
7692 {
7693 InfoMap infoMap;
7694 QDateTime startts = MythDate::fromString(bi.m_startTime);
7695
7697 RecordingInfo recinfo(bi.m_chanId, startts, false, 0h, &status);
7698 if (RecordingInfo::kFoundProgram == status)
7699 recinfo.QuickRecord();
7700 recinfo.ToMap(infoMap);
7701 infoMap["iconpath"] = ChannelUtil::GetIcon(recinfo.GetChanID());
7702 if ((recinfo.IsVideoFile() || recinfo.IsVideoDVD() ||
7703 recinfo.IsVideoBD()) && recinfo.GetPathname() != recinfo.GetBasename())
7704 {
7705 infoMap["coverartpath"] = VideoMetaDataUtil::GetArtPath(
7706 recinfo.GetPathname(), "Coverart");
7707 infoMap["fanartpath"] = VideoMetaDataUtil::GetArtPath(
7708 recinfo.GetPathname(), "Fanart");
7709 infoMap["bannerpath"] = VideoMetaDataUtil::GetArtPath(
7710 recinfo.GetPathname(), "Banners");
7711 infoMap["screenshotpath"] = VideoMetaDataUtil::GetArtPath(
7712 recinfo.GetPathname(), "Screenshots");
7713 }
7714
7716 InfoMap map;
7717 map.insert("message_text", tr("Record"));
7719 return;
7720 }
7721
7722 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
7724 {
7725 LOG(VB_GENERAL, LOG_CRIT, LOC + "Unknown recording during live tv.");
7726 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
7727 return;
7728 }
7729
7730 QString cmdmsg("");
7732 {
7735 recInfo.ApplyRecordRecGroupChange("Default");
7736 *m_playerContext.m_playingInfo = recInfo;
7737
7738 cmdmsg = tr("Record");
7741 LOG(VB_RECORD, LOG_INFO, LOC + "Toggling Record on");
7742 }
7743 else
7744 {
7747 recInfo.ApplyRecordRecGroupChange("LiveTV");
7748 *m_playerContext.m_playingInfo = recInfo;
7749
7750 cmdmsg = tr("Cancel Record");
7753 LOG(VB_RECORD, LOG_INFO, LOC + "Toggling Record off");
7754 }
7755
7756 QString msg = cmdmsg + " \"" + m_playerContext.m_playingInfo->GetTitle() + "\"";
7757
7758 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
7759
7760 emit ChangeOSDMessage(msg);
7761}
7762
7763void TV::HandleOSDClosed(int OSDType)
7764{
7765 switch (OSDType)
7766 {
7770 break;
7772 m_doSmartForward = false;
7773 break;
7775 m_stretchAdjustment = false;
7776 break;
7778 m_audiosyncAdjustment = false;
7779 gCoreContext->SaveSetting("AudioSyncOffset", QString::number(m_audioState.m_audioOffset.count()));
7780 break;
7783 break;
7786 break;
7788 break;
7789 }
7790}
7791
7793{
7795 if ((kAdjustingPicture_Playback == Type))
7796 {
7800 // Filter out range
7801 sup &= ~kPictureAttributeSupported_Range;
7802 }
7803 else if ((kAdjustingPicture_Channel == Type) || (kAdjustingPicture_Recording == Type))
7804 {
7809 }
7810
7811 return ::next_picattr(static_cast<PictureAttributeSupported>(sup), Attr);
7812}
7813
7815{
7817 if (kPictureAttribute_None == attr)
7818 return;
7819
7820 m_adjustingPicture = Type;
7822
7823 QString title = toTitleString(Type);
7824
7825 int value = 99;
7826 if (kAdjustingPicture_Playback == Type)
7827 {
7829 {
7830 value = m_videoColourState.GetValue(attr);
7831 }
7833 {
7834 value = static_cast<int>(m_audioState.m_volume);
7835 title = tr("Adjust Volume");
7836 }
7837 }
7838
7841
7842 QString text = toString(attr) + " " + toTypeString(Type);
7843
7844 UpdateOSDStatus(title, text, QString::number(value),
7846 value * 10, kOSDTimeout_Med);
7847 emit ChangeOSDPositionUpdates(false);
7848}
7849
7850void TV::ShowOSDCutpoint(const QString &Type)
7851{
7852 if (Type == "EDIT_CUT_POINTS")
7853 {
7854 if (!m_cutlistMenu.IsLoaded())
7855 {
7856 // TODO which translation context to use?
7858 "menu_cutlist.xml", tr("Edit Cut Points"),
7859 metaObject()->className(), "TV Editing");
7860 }
7861
7862 if (m_cutlistMenu.IsLoaded())
7864 }
7865 else if (Type == "EDIT_CUT_POINTS_COMPACT")
7866 {
7868 {
7869 // TODO which translation context to use?
7871 "menu_cutlist_compact.xml", tr("Edit Cut Points"),
7872 metaObject()->className(), "TV Editing");
7873 }
7874
7877 }
7878 else if (Type == "EXIT_EDIT_MODE")
7879 {
7881 .m_message=tr("Exit Recording Editor") };
7882 dialog.m_buttons.push_back( { tr("Save Cuts and Exit"), "DIALOG_CUTPOINT_SAVEEXIT_0" } );
7883 dialog.m_buttons.push_back( { tr("Exit Without Saving"), "DIALOG_CUTPOINT_REVERTEXIT_0" } );
7884 dialog.m_buttons.push_back( { tr("Save Cuts"), "DIALOG_CUTPOINT_SAVEMAP_0" } );
7885 dialog.m_buttons.push_back( { tr("Undo Changes"), "DIALOG_CUTPOINT_REVERT_0" } );
7886 dialog.m_back = { .m_text="",
7887 .m_data="DIALOG_CUTPOINT_DONOTHING_0",
7888 .m_exit=true };
7889 emit ChangeOSDDialog(dialog);
7890
7891 InfoMap map;
7892 map.insert("title", tr("Edit"));
7894 }
7895}
7896
7897bool TV::HandleOSDCutpoint(const QString& Action)
7898{
7899 bool res = true;
7901 return res;
7902
7903 OSD *osd = GetOSDL();
7904 if (Action == "DONOTHING" && osd)
7905 {
7906 }
7907 else if (osd)
7908 {
7909 QStringList actions(Action);
7910 if (!m_player->HandleProgramEditorActions(actions))
7911 LOG(VB_GENERAL, LOG_ERR, LOC + "Unrecognised cutpoint action");
7912 }
7913 ReturnOSDLock();
7914 return res;
7915}
7916
7921{
7922 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
7923 bool isEditing = m_playerContext.m_playingInfo->QueryIsEditing();
7924 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
7925
7926 if (isEditing)
7927 {
7929 return;
7930 }
7931
7932 emit EnableEdit();
7933}
7934
7936{
7937 bool paused = ContextIsPaused(__FILE__, __LINE__);
7938 if (!paused)
7939 DoTogglePause(true);
7940
7941 QString message = tr("This program is currently being edited");
7942 QString def = QString("DIALOG_EDITING_CONTINUE_%1").arg(static_cast<int>(paused));
7943 emit ChangeOSDDialog(
7944 { .m_dialogName=OSD_DLG_EDITING,
7945 .m_message=message,
7946 .m_timeout=0ms,
7947 .m_buttons={ { tr("Continue Editing"), def, false, true },
7948 { tr("Do not edit"), QString("DIALOG_EDITING_STOP_%1").arg(static_cast<int>(paused)) }},
7949 .m_back={ .m_text="", .m_data=def, .m_exit=true} });
7950}
7951
7952void TV::HandleOSDAlreadyEditing(const QString& Action, bool WasPaused)
7953{
7955 return;
7956
7957 bool paused = ContextIsPaused(__FILE__, __LINE__);
7958
7959 if (Action == "STOP")
7960 {
7961 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
7964 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
7965 if (!WasPaused && paused)
7966 DoTogglePause(true);
7967 }
7968 else // action == "CONTINUE"
7969 {
7970 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
7972 emit EnableEdit();
7973 if (!m_overlayState.m_editing && !WasPaused && paused)
7974 DoTogglePause(false);
7975 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
7976 }
7977
7978}
7979
7980static void insert_map(InfoMap &infoMap, const InfoMap &newMap)
7981{
7982 for (auto it = newMap.cbegin(); it != newMap.cend(); ++it)
7983 infoMap.insert(it.key(), *it);
7984}
7985
7990{
7991 OSD *osd = GetOSDL();
7992 if (!m_playerContext.m_recorder || !osd)
7993 {
7994 ReturnOSDLock();
7995 return;
7996 }
7997 ReturnOSDLock();
7998
7999 QMutexLocker locker(&m_chanEditMapLock);
8000
8001 // Get the info available from the backend
8002 m_chanEditMap.clear();
8004
8005 // Update with XDS Info
8007
8008 // Set proper initial values for channel editor, and make it visible..
8009 osd = GetOSDL();
8010 if (osd)
8011 {
8012 emit ChangeOSDDialog({ .m_dialogName=OSD_DLG_EDITOR });
8014 }
8015 ReturnOSDLock();
8016}
8017
8019{
8020 OSD *osd = GetOSDL();
8021 if (osd)
8022 {
8023 emit HideAll();
8024 ToggleOSD(true);
8025 emit ChangeOSDDialog({ .m_dialogName=OSD_DLG_NAVIGATE });
8026 }
8027 ReturnOSDLock();
8028}
8029
8034{
8035 QMutexLocker locker(&m_chanEditMapLock);
8036 bool hide = false;
8037
8039 return hide;
8040
8041 OSD *osd = GetOSDL();
8042 if (osd && Action == "PROBE")
8043 {
8044 InfoMap infoMap;
8045 osd->DialogGetText(infoMap);
8046 ChannelEditAutoFill(infoMap);
8047 insert_map(m_chanEditMap, infoMap);
8049 }
8050 else if (osd && Action == "OK")
8051 {
8052 InfoMap infoMap;
8053 osd->DialogGetText(infoMap);
8054 insert_map(m_chanEditMap, infoMap);
8056 hide = true;
8057 }
8058 else if (osd && Action == "QUIT")
8059 {
8060 hide = true;
8061 }
8062 ReturnOSDLock();
8063 return hide;
8064}
8065
8070{
8071#if 0
8072 const QString keys[4] = { "XMLTV", "callsign", "channame", "channum", };
8073#endif
8074
8075 // fill in uninitialized and unchanged fields from XDS
8076 ChannelEditXDSFill(Info);
8077}
8078
8080{
8081 QMap<QString,bool> modifiable;
8082 modifiable["callsign"] = Info["callsign"].isEmpty();
8083 if (!modifiable["callsign"])
8084 {
8085 QString unsetsign = tr("UNKNOWN%1", "Synthesized callsign");
8086 int unsetcmpl = unsetsign.length() - 2;
8087 unsetsign = unsetsign.left(unsetcmpl);
8088 if (Info["callsign"].left(unsetcmpl) == unsetsign) // was unsetcmpl????
8089 modifiable["callsign"] = true;
8090 }
8091 modifiable["channame"] = Info["channame"].isEmpty();
8092
8093 const std::array<const QString,2> xds_keys { "callsign", "channame", };
8094 for (const auto & key : xds_keys)
8095 {
8096 if (!modifiable[key])
8097 continue;
8098
8099 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
8100 QString tmp = m_player->GetXDS(key).toUpper();
8101 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
8102
8103 if (tmp.isEmpty())
8104 continue;
8105
8106 if ((key == "callsign") &&
8107 ((tmp.length() > 5) || (tmp.indexOf(" ") >= 0)))
8108 {
8109 continue;
8110 }
8111
8112 Info[key] = tmp;
8113 }
8114}
8115
8116void TV::OSDDialogEvent(int Result, const QString& Text, QString Action)
8117{
8119 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("result %1 text %2 action %3")
8120 .arg(QString::number(Result), Text, Action));
8121
8122 bool hide = true;
8123 if (Result == 100)
8124 hide = false;
8125
8126 bool handled = true;
8127 if (Action.startsWith("DIALOG_"))
8128 {
8129 Action.remove("DIALOG_");
8130 QStringList desc = Action.split("_");
8131 bool valid = desc.size() == 3;
8132 if (valid && desc[0] == ACTION_JUMPREC)
8133 {
8134 FillOSDMenuJumpRec(desc[1], desc[2].toInt(), Text);
8135 hide = false;
8136 }
8137 else if (valid && desc[0] == "VIDEOEXIT")
8138 {
8139 hide = HandleOSDVideoExit(desc[1]);
8140 }
8141 else if (valid && desc[0] == "SLEEP")
8142 {
8143 HandleOSDSleep(desc[1]);
8144 }
8145 else if (valid && desc[0] == "IDLE")
8146 {
8147 HandleOSDIdle(desc[1]);
8148 }
8149 else if (valid && desc[0] == "INFO")
8150 {
8151 HandleOSDInfo(desc[1]);
8152 }
8153 else if (valid && desc[0] == "EDITING")
8154 {
8155 HandleOSDAlreadyEditing(desc[1], desc[2].toInt() != 0);
8156 }
8157 else if (valid && desc[0] == "ASKALLOW")
8158 {
8159 HandleOSDAskAllow(desc[1]);
8160 }
8161 else if (valid && desc[0] == "EDITOR")
8162 {
8163 hide = HandleOSDChannelEdit(desc[1]);
8164 }
8165 else if (valid && desc[0] == "CUTPOINT")
8166 {
8167 hide = HandleOSDCutpoint(desc[1]);
8168 }
8169 else if ((valid && desc[0] == "DELETE") ||
8170 (valid && desc[0] == "CONFIRM"))
8171 {
8172 }
8173 else if (valid && desc[0] == ACTION_PLAY)
8174 {
8175 DoPlay();
8176 }
8177 else
8178 {
8179 LOG(VB_GENERAL, LOG_ERR, "Unrecognised dialog event.");
8180 }
8181 }
8182 else if (Result < 0)
8183 { // NOLINT(bugprone-branch-clone)
8184 ; // exit dialog
8185 }
8186 else if (HandleTrackAction(Action))
8187 {
8188 ;
8189 }
8190 else if (Action == ACTION_PAUSE)
8191 {
8192 DoTogglePause(true);
8193 }
8194 else if (Action == ACTION_STOP)
8195 {
8196 PrepareToExitPlayer(__LINE__);
8197 SetExitPlayer(true, true);
8198 }
8199 else if (Action == "CANCELPLAYLIST")
8200 {
8201 SetInPlayList(false);
8202 MythEvent xe("CANCEL_PLAYLIST");
8204 }
8205 else if (Action == ACTION_JUMPFFWD)
8206 {
8207 DoJumpFFWD();
8208 }
8209 else if (Action == ACTION_JUMPRWND)
8210 {
8211 DoJumpRWND();
8212 }
8213 else if (Action == ACTION_SEEKFFWD)
8214 {
8215 DoSeekFFWD();
8216 }
8217 else if (Action == ACTION_SEEKRWND)
8218 {
8219 DoSeekRWND();
8220 }
8221 else if (Action == ACTION_TOGGLEOSDDEBUG)
8222 {
8223 emit ChangeOSDDebug();
8224 }
8225 else if (Action == "TOGGLEMANUALZOOM")
8226 {
8227 SetManualZoom(true, tr("Zoom Mode ON"));
8228 }
8229 else if (Action == ACTION_BOTTOMLINEMOVE)
8230 {
8231 emit ToggleMoveBottomLine();
8232 }
8233 else if (Action == ACTION_BOTTOMLINESAVE)
8234 {
8235 emit SaveBottomLine();
8236 }
8237 else if (Action == "TOGGLESTRETCH")
8238 {
8240 }
8241 else if (Action == ACTION_ENABLEUPMIX)
8242 {
8243 emit ChangeUpmix(true);
8244 }
8245 else if (Action == ACTION_DISABLEUPMIX)
8246 {
8247 emit ChangeUpmix(false);
8248 }
8249 else if (Action.startsWith("ADJUSTSTRETCH"))
8250 {
8251 bool floatRead = false;
8252 float stretch = Action.right(Action.length() - 13).toFloat(&floatRead);
8253 if (floatRead &&
8254 stretch <= 2.0F &&
8255 stretch >= 0.48F)
8256 {
8257 m_playerContext.m_tsNormal = stretch; // alter speed before display
8258 }
8259
8260 StopFFRew();
8261
8262 if (ContextIsPaused(__FILE__, __LINE__))
8263 DoTogglePause(true);
8264
8265 ChangeTimeStretch(0, !floatRead); // just display
8266 }
8267 else if (Action.startsWith("SELECTSCAN_"))
8268 {
8269 OverrideScan(static_cast<FrameScanType>(Action.right(1).toInt()));
8270 }
8271 else if (Action.startsWith(ACTION_TOGGELAUDIOSYNC))
8272 {
8273 emit ChangeAudioOffset(0ms);
8274 }
8276 {
8277 emit AdjustSubtitleZoom(0);
8278 }
8280 {
8281 emit AdjustSubtitleDelay(0ms);
8282 }
8284 {
8285 emit EnableVisualiser(false, true);
8286 }
8288 {
8289 emit EnableVisualiser(true);
8290 }
8292 {
8293 emit EnableVisualiser(false);
8294 }
8295 else if (Action.startsWith(ACTION_TOGGLESLEEP))
8296 {
8297 ToggleSleepTimer(Action.left(13));
8298 }
8299 else if (Action.startsWith("TOGGLEPICCONTROLS"))
8300 {
8301 m_adjustingPictureAttribute = static_cast<PictureAttribute>(Action.right(1).toInt() - 1);
8303 }
8304 else if (Action == "TOGGLEASPECT")
8305 {
8306 emit ChangeAspectOverride();
8307 }
8308 else if (Action.startsWith("TOGGLEASPECT"))
8309 {
8310 emit ChangeAspectOverride(static_cast<AspectOverrideMode>(Action.right(1).toInt()));
8311 }
8312 else if (Action == "TOGGLEFILL")
8313 {
8314 emit ChangeAdjustFill();
8315 }
8316 else if (Action.startsWith("TOGGLEFILL"))
8317 {
8318 emit ChangeAdjustFill(static_cast<AdjustFillMode>(Action.right(1).toInt()));
8319 }
8320 else if (Action == "MENU")
8321 {
8322 ShowOSDMenu();
8323 }
8324 else if (Action == "AUTODETECT_FILL")
8325 {
8326 emit ToggleDetectLetterBox();
8327 }
8328 else if (Action == ACTION_GUIDE)
8329 {
8331 }
8332 else if (Action.startsWith("CHANGROUP_") && m_dbUseChannelGroups)
8333 {
8334 if (Action == "CHANGROUP_ALL_CHANNELS")
8335 {
8337 }
8338 else
8339 {
8340 Action.remove("CHANGROUP_");
8341
8342 UpdateChannelList(Action.toInt());
8343
8344 // make sure the current channel is from the selected group
8345 // or tune to the first in the group
8346 QString cur_channum;
8347 QString new_channum;
8349 {
8350 QMutexLocker locker(&m_channelGroupLock);
8352 cur_channum = m_playerContext.m_tvchain->GetChannelName(-1);
8353 new_channum = cur_channum;
8354
8355 auto it = list.cbegin();
8356 for (; it != list.cend(); ++it)
8357 {
8358 if ((*it).m_chanNum == cur_channum)
8359 {
8360 break;
8361 }
8362 }
8363
8364 if (it == list.end())
8365 {
8366 // current channel not found so switch to the
8367 // first channel in the group
8368 it = list.begin();
8369 if (it != list.end())
8370 new_channum = (*it).m_chanNum;
8371 }
8372
8373 LOG(VB_CHANNEL, LOG_INFO, LOC +
8374 QString("Channel Group: '%1'->'%2'")
8375 .arg(cur_channum, new_channum));
8376 }
8377
8379 {
8380 // Only change channel if new channel != current channel
8381 if (cur_channum != new_channum && !new_channum.isEmpty())
8382 {
8383 m_queuedInput = new_channum;
8384 m_queuedChanNum = new_channum;
8385 m_queuedChanID = 0;
8387 m_queueInputTimerId = StartTimer(10ms, __LINE__);
8388 }
8389
8390 // Turn off OSD Channel Num so the channel
8391 // changes right away
8393 }
8394 }
8395 }
8396 else if (Action == ACTION_FINDER)
8397 {
8399 }
8400 else if (Action == "SCHEDULE")
8401 {
8403 }
8404 else if (Action == ACTION_VIEWSCHEDULED)
8405 {
8407 }
8408 else if (Action == ACTION_CAST)
8409 {
8411 hide = false;
8412 }
8413 else if (Action.startsWith("JUMPCAST|"))
8414 {
8415 QStringList tokens = Action.split("|");
8416 if (tokens.size() == 3)
8417 FillOSDMenuActorShows(tokens[1], tokens[2].toInt());
8418 else if (tokens.size() == 4)
8419 FillOSDMenuActorShows(tokens[1], tokens[2].toInt(), tokens[3]);
8420
8421 hide = false;
8422 }
8423 else if (Action.startsWith("VISUALISER"))
8424 {
8425 emit EnableVisualiser(true, false, Action.mid(11));
8426 }
8427 else if (Action.startsWith("3D"))
8428 {
8430 }
8431 else if (HandleJumpToProgramAction(QStringList(Action)))
8432 {
8433 }
8434 else if (StateIsLiveTV(GetState()))
8435 {
8436 if (Action == "TOGGLEBROWSE")
8437 {
8438 BrowseStart();
8439 }
8440 else if (Action == "PREVCHAN")
8441 {
8442 PopPreviousChannel(true);
8443 }
8444 else if (Action.startsWith("SWITCHTOINPUT_"))
8445 {
8446 m_switchToInputId = Action.mid(14).toUInt();
8448 }
8449 else if (Action == "EDIT")
8450 {
8452 hide = false;
8453 }
8454 else
8455 {
8456 handled = false;
8457 }
8458 }
8459 else
8460 {
8461 handled = false;
8462 }
8463 if (!handled && StateIsPlaying(m_playerContext.GetState()))
8464 {
8465 handled = true;
8468 {
8470 emit GoToMenu("chapter");
8472 emit GoToMenu("title");
8473 else if (Action == ACTION_JUMPTOPOPUPMENU)
8474 emit GoToMenu("popup");
8475 else
8476 emit GoToMenu("root");
8477 }
8478 else if (Action.startsWith(ACTION_JUMPCHAPTER))
8479 {
8480 int chapter = Action.right(3).toInt();
8481 DoJumpChapter(chapter);
8482 }
8483 else if (Action.startsWith(ACTION_SWITCHTITLE))
8484 {
8485 int title = Action.right(3).toInt();
8486 DoSwitchTitle(title);
8487 }
8488 else if (Action.startsWith(ACTION_SWITCHANGLE))
8489 {
8490 int angle = Action.right(3).toInt();
8491 DoSwitchAngle(angle);
8492 }
8493 else if (Action == "EDIT")
8494 {
8496 hide = false;
8497 }
8498 else if (Action == "TOGGLEAUTOEXPIRE")
8499 {
8501 }
8502 else if (Action.startsWith("TOGGLECOMMSKIP"))
8503 {
8504 SetAutoCommercialSkip(static_cast<CommSkipMode>(Action.right(1).toInt()));
8505 }
8506 else if (Action == "QUEUETRANSCODE")
8507 {
8508 DoQueueTranscode("Default");
8509 }
8510 else if (Action == "QUEUETRANSCODE_AUTO")
8511 {
8512 DoQueueTranscode("Autodetect");
8513 }
8514 else if (Action == "QUEUETRANSCODE_HIGH")
8515 {
8516 DoQueueTranscode("High Quality");
8517 }
8518 else if (Action == "QUEUETRANSCODE_MEDIUM")
8519 {
8520 DoQueueTranscode("Medium Quality");
8521 }
8522 else if (Action == "QUEUETRANSCODE_LOW")
8523 {
8524 DoQueueTranscode("Low Quality");
8525 }
8526 else
8527 {
8528 handled = false;
8529 }
8530 }
8531
8532 if (!handled)
8533 {
8536 handled = ActiveHandleAction(QStringList(Action), isDVD, isMenuOrStill);
8537 }
8538
8539 if (!handled)
8540 handled = ActivePostQHandleAction(QStringList(Action));
8541
8542 if (!handled)
8543 {
8544 LOG(VB_GENERAL, LOG_ERR, LOC +
8545 "Unknown menu action selected: " + Action);
8546 hide = false;
8547 }
8548
8549 if (hide)
8550 emit DialogQuit();
8552}
8553
8554bool TV::DialogIsVisible(const QString &Dialog)
8555{
8556 bool visible = false;
8557 OSD *osd = GetOSDL();
8558 if (osd)
8559 visible = osd->DialogVisible(Dialog);
8560 ReturnOSDLock();
8561 return visible;
8562}
8563
8564void TV::HandleOSDInfo(const QString& Action)
8565{
8567 return;
8568
8569 if (Action == "CHANNELLOCK")
8570 m_lockTimerOn = false;
8571}
8572
8573// NOLINTBEGIN(cppcoreguidelines-macro-usage)
8574#define BUTTON(action, text) \
8575 result = Context.AddButton(Menu, active, (action), (text), "", false, "")
8576#define BUTTON2(action, textActive, textInactive) \
8577 result = Context.AddButton(Menu, active, (action), (textActive), (textInactive), false, "")
8578#define BUTTON3(action, textActive, textInactive, isMenu) \
8579 result = Context.AddButton(Menu, active, (action), (textActive), (textInactive), (isMenu), "")
8580// NOLINTEND(cppcoreguidelines-macro-usage)
8581
8583{
8584 if (&Context.m_menu == &m_playbackMenu || &Context.m_menu == &m_playbackCompactMenu)
8585 return MenuItemDisplayPlayback(Context, Menu);
8586 if (&Context.m_menu == &m_cutlistMenu || &Context.m_menu == &m_cutlistCompactMenu)
8587 return MenuItemDisplayCutlist(Context, Menu);
8588 return false;
8589}
8590
8592{
8593 MenuCategory category = Context.m_category;
8594 const QString &actionName = Context.m_action;
8595
8596 bool result = false;
8597 if (category == kMenuCategoryMenu)
8598 {
8599 result = Context.m_menu.Show(Context.m_node, QDomNode(), *this, Menu, false);
8600 if (result && Context.m_visible)
8601 {
8602 QVariant v;
8603 v.setValue(MythTVMenuNodeTuple(Context.m_menu.m_id,
8605 Menu->m_buttons.push_back( { Context.m_menuName, v, true,
8607 }
8608 return result;
8609 }
8610
8611 emit RefreshEditorState();
8612
8613 if (category == kMenuCategoryItem)
8614 {
8615 bool active = true;
8616 if (actionName == "DIALOG_CUTPOINT_MOVEPREV_0")
8617 {
8622 {
8624 BUTTON2(actionName, tr("Move Previous Cut End Here"), tr("Move Start of Cut Here"));
8625 }
8626 }
8627 else if (actionName == "DIALOG_CUTPOINT_MOVENEXT_0")
8628 {
8633 {
8635 BUTTON2(actionName, tr("Move Next Cut Start Here"), tr("Move End of Cut Here"));
8636 }
8637 }
8638 else if (actionName == "DIALOG_CUTPOINT_CUTTOBEGINNING_0")
8639 {
8641 BUTTON(actionName, tr("Cut to Beginning"));
8642 }
8643 else if (actionName == "DIALOG_CUTPOINT_CUTTOEND_0")
8644 {
8647 {
8648 BUTTON(actionName, tr("Cut to End"));
8649 }
8650 }
8651 else if (actionName == "DIALOG_CUTPOINT_DELETE_0")
8652 {
8654 BUTTON2(actionName, tr("Delete This Cut"), tr("Join Surrounding Cuts"));
8655 }
8656 else if (actionName == "DIALOG_CUTPOINT_NEWCUT_0")
8657 {
8659 BUTTON(actionName, tr("Add New Cut"));
8660 }
8661 else if (actionName == "DIALOG_CUTPOINT_UNDO_0")
8662 {
8663 active = m_editorState.m_hasUndo;
8664 //: %1 is the undo message
8665 QString text = tr("Undo - %1");
8666 result = Context.AddButton(Menu, active, actionName, text, "", false,
8668 }
8669 else if (actionName == "DIALOG_CUTPOINT_REDO_0")
8670 {
8671 active = m_editorState.m_hasRedo;
8672 //: %1 is the redo message
8673 QString text = tr("Redo - %1");
8674 result = Context.AddButton(Menu, active, actionName, text, "", false,
8676 }
8677 else if (actionName == "DIALOG_CUTPOINT_CLEARMAP_0")
8678 {
8679 BUTTON(actionName, tr("Clear Cuts"));
8680 }
8681 else if (actionName == "DIALOG_CUTPOINT_INVERTMAP_0")
8682 {
8683 BUTTON(actionName, tr("Reverse Cuts"));
8684 }
8685 else if (actionName == "DIALOG_CUTPOINT_LOADCOMMSKIP_0")
8686 {
8687 BUTTON(actionName, tr("Load Detected Commercials"));
8688 }
8689 else if (actionName == "DIALOG_CUTPOINT_REVERT_0")
8690 {
8691 BUTTON(actionName, tr("Undo Changes"));
8692 }
8693 else if (actionName == "DIALOG_CUTPOINT_REVERTEXIT_0")
8694 {
8695 BUTTON(actionName, tr("Exit Without Saving"));
8696 }
8697 else if (actionName == "DIALOG_CUTPOINT_SAVEMAP_0")
8698 {
8699 BUTTON(actionName, tr("Save Cuts"));
8700 }
8701 else if (actionName == "DIALOG_CUTPOINT_SAVEEXIT_0")
8702 {
8703 BUTTON(actionName, tr("Save Cuts and Exit"));
8704 }
8705 else
8706 {
8707 // Allow an arbitrary action if it has a translated
8708 // description available to be used as the button text.
8709 // Look in the specified keybinding context as well as the
8710 // Global context.
8711 // XXX This doesn't work well (yet) because a keybinding
8712 // action named "foo" is actually a menu action named
8713 // "DIALOG_CUTPOINT_foo_0".
8714 QString text = m_mainWindow->GetActionText(Context.m_menu.GetKeyBindingContext(), actionName);
8715 if (text.isEmpty())
8716 text = m_mainWindow->GetActionText("Global", actionName);
8717 if (!text.isEmpty())
8718 BUTTON(actionName, text);
8719 }
8720 }
8721
8722 return result;
8723}
8724
8725// Returns true if at least one item should be displayed.
8727 MythOSDDialogData *Menu)
8728{
8729 MenuCategory category = Context.m_category;
8730 const QString &actionName = Context.m_action;
8731
8732 bool result = false;
8733 bool active = true;
8734
8735 if (category == kMenuCategoryMenu)
8736 {
8737 result = Context.m_menu.Show(Context.m_node, QDomNode(), *this, Menu, false);
8738 if (result && Context.m_visible)
8739 {
8740 QVariant v;
8741 v.setValue(MythTVMenuNodeTuple(Context.m_menu.m_id,
8743 Menu->m_buttons.push_back( { Context.m_menuName, v, true,
8745 }
8746 return result;
8747 }
8748 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
8749 QString prefix;
8750 if (MythTVMenu::MatchesGroup(actionName, "VISUALISER_", category, prefix) &&
8752 {
8753 for (auto & visualiser : m_visualiserState.m_visualiserList)
8754 {
8755 active = m_visualiserState.m_visualiserName == visualiser;
8756 BUTTON(prefix + visualiser, visualiser);
8757 }
8758 }
8759 else if (MythTVMenu::MatchesGroup(actionName, "TOGGLEASPECT", category, prefix))
8760 {
8761 for (int j = kAspect_Off; j < kAspect_END; j++)
8762 {
8763 // swap 14:9 and 16:9
8764 int i {j};
8765 if (kAspect_14_9 == j)
8766 i = kAspect_16_9;
8767 else if (kAspect_16_9 == j)
8768 i = kAspect_14_9;
8769 QString action = prefix + QString::number(i);
8771 BUTTON(action, toString(static_cast<AspectOverrideMode>(i)));
8772 }
8773 }
8774 else if (MythTVMenu::MatchesGroup(actionName, "TOGGLEFILL", category, prefix))
8775 {
8776 for (int i = kAdjustFill_Off; i < kAdjustFill_END; i++)
8777 {
8778 QString action = prefix + QString::number(i);
8779 active = (m_videoBoundsState.m_adjustFillMode == i);
8780 BUTTON(action, toString(static_cast<AdjustFillMode>(i)));
8781 }
8782 }
8783 else if (MythTVMenu::MatchesGroup(actionName, "TOGGLEPICCONTROLS", category, prefix))
8784 {
8785 for (int i = kPictureAttribute_MIN; i < kPictureAttribute_MAX; i++)
8786 {
8788 {
8789 QString action = prefix + QString::number(i - kPictureAttribute_MIN);
8790 if (static_cast<PictureAttribute>(i) != kPictureAttribute_Range)
8791 BUTTON(action, toString(static_cast<PictureAttribute>(i)));
8792 }
8793 }
8794 }
8795 else if (MythTVMenu::MatchesGroup(actionName, "3D", category, prefix))
8796 {
8798 BUTTON(ACTION_3DNONE, tr("Auto"));
8800 BUTTON(ACTION_3DIGNORE, tr("Ignore"));
8802 BUTTON(ACTION_3DSIDEBYSIDEDISCARD, tr("Discard Side by Side"));
8804 BUTTON(ACTION_3DTOPANDBOTTOMDISCARD, tr("Discard Top and Bottom"));
8805 }
8806 else if (MythTVMenu::MatchesGroup(actionName, "SELECTSCAN_", category, prefix) && m_player)
8807 {
8809 active = (scan == kScan_Detect);
8810 BUTTON("SELECTSCAN_0", ScanTypeToUserString(kScan_Detect));
8811 active = (scan == kScan_Progressive);
8813 active = (scan == kScan_Interlaced);
8815 active = (scan == kScan_Intr2ndField);
8817 }
8818 else if (MythTVMenu::MatchesGroup(actionName, "SELECTSUBTITLE_", category, prefix) ||
8819 MythTVMenu::MatchesGroup(actionName, "SELECTRAWTEXT_", category, prefix) ||
8820 MythTVMenu::MatchesGroup(actionName, "SELECTCC708_", category, prefix) ||
8821 MythTVMenu::MatchesGroup(actionName, "SELECTCC608_", category, prefix) ||
8822 MythTVMenu::MatchesGroup(actionName, "SELECTTTC_", category, prefix) ||
8823 MythTVMenu::MatchesGroup(actionName, "SELECTAUDIO_", category, prefix))
8824 {
8825 int i = 0;
8827 if (prefix == "SELECTSUBTITLE_") {
8829 } else if (prefix == "SELECTRAWTEXT_") {
8831 } else if (prefix == "SELECTCC708_") {
8833 } else if (prefix == "SELECTCC608_") {
8835 } else if (prefix == "SELECTTTC_") {
8837 } else if (prefix == "SELECTAUDIO_") {
8839 if (m_tvmTracks[type].size() <= 1)
8840 i = 1; // don't show choices if only 1 audio track
8841 }
8842
8843 for (; i < m_tvmTracks[type].size(); i++)
8844 {
8845 QString action = prefix + QString::number(i);
8846 active = (i == m_tvmCurtrack[type]);
8848 }
8849 }
8850 else if (MythTVMenu::MatchesGroup(actionName, "ADJUSTSTRETCH", category, prefix))
8851 {
8852 struct speed
8853 {
8854 int m_speedX100;
8855 QString m_suffix;
8856 QString m_trans;
8857 };
8858
8859 static const std::array<const speed,9> s_speeds {{
8860 { .m_speedX100=0, .m_suffix="", .m_trans=tr("Adjust")},
8861 { .m_speedX100=50, .m_suffix="0.5", .m_trans=tr("0.5x")},
8862 { .m_speedX100=90, .m_suffix="0.9", .m_trans=tr("0.9x")},
8863 {.m_speedX100=100, .m_suffix="1.0", .m_trans=tr("1.0x")},
8864 {.m_speedX100=110, .m_suffix="1.1", .m_trans=tr("1.1x")},
8865 {.m_speedX100=120, .m_suffix="1.2", .m_trans=tr("1.2x")},
8866 {.m_speedX100=130, .m_suffix="1.3", .m_trans=tr("1.3x")},
8867 {.m_speedX100=140, .m_suffix="1.4", .m_trans=tr("1.4x")},
8868 {.m_speedX100=150, .m_suffix="1.5", .m_trans=tr("1.5x")},
8869 }};
8870
8871 for (const auto & speed : s_speeds)
8872 {
8873 QString action = prefix + speed.m_suffix;
8874 active = (m_tvmSpeedX100 == speed.m_speedX100);
8875 BUTTON(action, speed.m_trans);
8876 }
8877 }
8878 else if (MythTVMenu::MatchesGroup(actionName, "TOGGLESLEEP", category, prefix))
8879 {
8880 active = false;
8881 if (m_sleepTimerId)
8882 BUTTON(ACTION_TOGGLESLEEP + "ON", tr("Sleep Off"));
8883 BUTTON(ACTION_TOGGLESLEEP + "30", tr("%n minute(s)", "", 30));
8884 BUTTON(ACTION_TOGGLESLEEP + "60", tr("%n minute(s)", "", 60));
8885 BUTTON(ACTION_TOGGLESLEEP + "90", tr("%n minute(s)", "", 90));
8886 BUTTON(ACTION_TOGGLESLEEP + "120", tr("%n minute(s)", "", 120));
8887 }
8888 else if (MythTVMenu::MatchesGroup(actionName, "CHANGROUP_", category, prefix))
8889 {
8891 {
8892 active = false;
8893 BUTTON("CHANGROUP_ALL_CHANNELS", tr("All Channels"));
8894 ChannelGroupList::const_iterator it;
8895 for (it = m_dbChannelGroups.begin();
8896 it != m_dbChannelGroups.end(); ++it)
8897 {
8898 QString action = prefix + QString::number(it->m_grpId);
8899 active = (static_cast<int>(it->m_grpId) == m_channelGroupId);
8900 BUTTON(action, it->m_name);
8901 }
8902 }
8903 }
8904 else if (MythTVMenu::MatchesGroup(actionName, "TOGGLECOMMSKIP", category, prefix))
8905 {
8907 {
8908 static constexpr std::array<const uint,3> kCasOrd { 0, 2, 1 };
8909 for (uint csm : kCasOrd)
8910 {
8911 const auto mode = static_cast<CommSkipMode>(csm);
8912 QString action = prefix + QString::number(csm);
8913 active = (mode == m_tvmCurSkip);
8914 BUTTON(action, toString(static_cast<CommSkipMode>(csm)));
8915 }
8916 }
8917 }
8918 else if (MythTVMenu::MatchesGroup(actionName, "JUMPTOCHAPTER", category, prefix))
8919 {
8920 if (m_tvmNumChapters &&
8922 {
8923 int size = QString::number(m_tvmNumChapters).size();
8924 for (int i = 0; i < m_tvmNumChapters; i++)
8925 {
8926 QString chapter1 = QString("%1").arg(i+1, size, 10, QChar{'0'});
8927 QString chapter2 = QString("%1").arg(i+1, 3 , 10, QChar{'0'});
8928 QString timestr = MythDate::formatTime(m_tvmChapterTimes[i], "HH:mm:ss");
8929 QString desc = chapter1 + QString(" (%1)").arg(timestr);
8930 QString action = prefix + chapter2;
8931 active = (m_tvmCurrentChapter == (i + 1));
8932 BUTTON(action, desc);
8933 }
8934 }
8935 }
8936 else if (MythTVMenu::MatchesGroup(actionName, "SWITCHTOANGLE", category, prefix))
8937 {
8938 if (m_tvmNumAngles > 1)
8939 {
8940 for (int i = 1; i <= m_tvmNumAngles; i++)
8941 {
8942 QString angleIdx = QString("%1").arg(i, 3, 10, QChar{'0'});
8943 QString desc = GetAngleName(i);
8944 QString action = prefix + angleIdx;
8945 active = (m_tvmCurrentAngle == i);
8946 BUTTON(action, desc);
8947 }
8948 }
8949 }
8950 else if (MythTVMenu::MatchesGroup(actionName, "JUMPTOTITLE", category, prefix))
8951 {
8952 for (int i = 0; i < m_tvmNumTitles; i++)
8953 {
8954 if (GetTitleDuration(i) < 2min) // Ignore < 2 minutes long
8955 continue;
8956
8957 QString titleIdx = QString("%1").arg(i, 3, 10, QChar{'0'});
8958 QString desc = GetTitleName(i);
8959 QString action = prefix + titleIdx;
8960 active = (m_tvmCurrentTitle == i);
8961 BUTTON(action, desc);
8962 }
8963 }
8964 else if (MythTVMenu::MatchesGroup(actionName, "SWITCHTOINPUT_", category, prefix))
8965 {
8967 {
8968 uint inputid = m_playerContext.GetCardID();
8969 std::vector<InputInfo> inputs = RemoteRequestFreeInputInfo(inputid);
8970 QVector <QString> addednames;
8971 addednames += CardUtil::GetDisplayName(inputid);
8972 for (auto & input : inputs)
8973 {
8974 if (input.m_inputId == inputid ||
8975 addednames.contains(input.m_displayName))
8976 continue;
8977 active = false;
8978 addednames += input.m_displayName;
8979 QString action = QString("SWITCHTOINPUT_") +
8980 QString::number(input.m_inputId);
8981 BUTTON(action, input.m_displayName);
8982 }
8983 }
8984 }
8985 else if (MythTVMenu::MatchesGroup(actionName, "SWITCHTOSOURCE_", category, prefix))
8986 {
8988 {
8989 uint inputid = m_playerContext.GetCardID();
8990 InfoMap info;
8992 uint sourceid = info["sourceid"].toUInt();
8993 QMap<uint, bool> sourceids;
8994 std::vector<InputInfo> inputs = RemoteRequestFreeInputInfo(inputid);
8995 for (auto & input : inputs)
8996 {
8997 if (input.m_sourceId == sourceid ||
8998 sourceids[input.m_sourceId])
8999 continue;
9000 active = false;
9001 sourceids[input.m_sourceId] = true;
9002 QString action = QString("SWITCHTOINPUT_") +
9003 QString::number(input.m_inputId);
9004 BUTTON(action, SourceUtil::GetSourceName(input.m_sourceId));
9005 }
9006 }
9007 }
9008 else if (category == kMenuCategoryItem)
9009 {
9010 if (actionName == "TOGGLEAUDIOSYNC")
9011 {
9012 BUTTON(actionName, tr("Adjust Audio Sync"));
9013 }
9014 else if (m_visualiserState.m_canVisualise && (actionName == "DISABLEVISUALISATION"))
9015 {
9016 BUTTON(actionName, tr("None"));
9017 }
9018 else if (actionName == "DISABLEUPMIX")
9019 {
9021 {
9022 active = !m_audioState.m_isUpmixing;
9023 BUTTON(actionName, tr("Disable Audio Upmixer"));
9024 }
9025 }
9026 else if (actionName == "ENABLEUPMIX")
9027 {
9029 {
9030 active = m_audioState.m_isUpmixing;
9031 BUTTON(actionName, tr("Auto Detect"));
9032 }
9033 }
9034 else if (actionName == "AUTODETECT_FILL")
9035 {
9037 {
9038 active =
9041 BUTTON(actionName, tr("Auto Detect"));
9042 }
9043 }
9044 else if (actionName == "TOGGLEMANUALZOOM")
9045 {
9046 BUTTON(actionName, tr("Manual Zoom Mode"));
9047 }
9048 else if (actionName == "DISABLESUBS")
9049 {
9052 BUTTON(actionName, tr("Disable Subtitles"));
9053 }
9054 else if (actionName == "ENABLESUBS")
9055 {
9058 BUTTON(actionName, tr("Enable Subtitles"));
9059 }
9060 else if (actionName == "DISABLEFORCEDSUBS")
9061 {
9062 active = !m_tvmSubsForcedOn;
9063 if (!m_tvmTracks[kTrackTypeSubtitle].empty() ||
9065 {
9066 BUTTON(actionName, tr("Disable Forced Subtitles"));
9067 }
9068 }
9069 else if (actionName == "ENABLEFORCEDSUBS")
9070 {
9071 active = m_tvmSubsForcedOn;
9072 if (!m_tvmTracks[kTrackTypeSubtitle].empty() ||
9074 {
9075 BUTTON(actionName, tr("Enable Forced Subtitles"));
9076 }
9077 }
9078 else if (actionName == "DISABLEEXTTEXT")
9079 {
9082 BUTTON(actionName, tr("Disable External Subtitles"));
9083 }
9084 else if (actionName == "ENABLEEXTTEXT")
9085 {
9088 BUTTON(actionName, tr("Enable External Subtitles"));
9089 }
9090 else if (actionName == "TOGGLETTM")
9091 {
9092 if (!m_tvmTracks[kTrackTypeTeletextMenu].empty())
9093 BUTTON(actionName, tr("Toggle Teletext Menu"));
9094 }
9095 else if (actionName == "TOGGLESUBZOOM")
9096 {
9098 BUTTON(actionName, tr("Adjust Subtitle Zoom"));
9099 }
9100 else if (actionName == "TOGGLESUBDELAY")
9101 {
9105 {
9106 BUTTON(actionName, tr("Adjust Subtitle Delay"));
9107 }
9108 }
9109 else if (actionName == "PAUSE")
9110 {
9111 active = m_tvmIsPaused;
9112 BUTTON2(actionName, tr("Play"), tr("Pause"));
9113 }
9114 else if (actionName == "TOGGLESTRETCH")
9115 {
9116 BUTTON(actionName, tr("Toggle"));
9117 }
9118 else if (actionName == "TOGGLEBROWSE")
9119 {
9121 BUTTON(actionName, tr("Toggle Browse Mode"));
9122 }
9123 else if (actionName == "CANCELPLAYLIST")
9124 {
9125 if (m_inPlaylist)
9126 BUTTON(actionName, tr("Cancel Playlist"));
9127 }
9128 else if (actionName == "DEBUGOSD")
9129 {
9130 BUTTON(actionName, tr("Playback Data"));
9131 }
9132 else if (actionName == "JUMPFFWD")
9133 {
9134 if (m_tvmJump)
9135 BUTTON(actionName, tr("Jump Ahead"));
9136 }
9137 else if (actionName == "JUMPRWND")
9138 {
9139 if (m_tvmJump)
9140 BUTTON(actionName, tr("Jump Back"));
9141 }
9142 else if (actionName == "JUMPTODVDROOTMENU")
9143 {
9144 if (m_tvmIsBd || m_tvmIsDvd)
9145 {
9146 active = m_tvmIsDvd;
9147 BUTTON2(actionName, tr("DVD Root Menu"), tr("Top menu"));
9148 }
9149 }
9150 else if (actionName == "JUMPTOPOPUPMENU")
9151 {
9152 if (m_tvmIsBd)
9153 BUTTON(actionName, tr("Popup menu"));
9154 }
9155 else if (actionName == "JUMPTODVDTITLEMENU")
9156 {
9157 if (m_tvmIsDvd)
9158 BUTTON(actionName, tr("DVD Title Menu"));
9159 }
9160 else if (actionName == "JUMPTODVDCHAPTERMENU")
9161 {
9162 if (m_tvmIsDvd)
9163 BUTTON(actionName, tr("DVD Chapter Menu"));
9164 }
9165 else if (actionName == "PREVCHAN")
9166 {
9168 BUTTON(actionName, tr("Previous Channel"));
9169 }
9170 else if (actionName == "GUIDE")
9171 {
9172 BUTTON(actionName, tr("Program Guide"));
9173 }
9174 else if (actionName == "FINDER")
9175 {
9176 BUTTON(actionName, tr("Program Finder"));
9177 }
9178 else if (actionName == "VIEWSCHEDULED")
9179 {
9180 BUTTON(actionName, tr("Upcoming Recordings"));
9181 }
9182 else if (actionName == "SCHEDULE")
9183 {
9184 BUTTON(actionName, tr("Edit Recording Schedule"));
9185 }
9186 else if (actionName == "DIALOG_JUMPREC_X_0")
9187 {
9188 BUTTON3(actionName, tr("Recorded Program"), "", true);
9189 QVariant v;
9190 v.setValue(MythTVMenuNodeTuple(Context.m_menu.m_id,
9193 }
9194 else if (actionName == "JUMPPREV")
9195 {
9196 if (m_lastProgram != nullptr)
9197 {
9198 if (m_lastProgram->GetSubtitle().isEmpty())
9199 {
9200 BUTTON(actionName, m_lastProgram->GetTitle());
9201 }
9202 else
9203 {
9204 BUTTON(actionName,
9205 QString("%1: %2")
9206 .arg(m_lastProgram->GetTitle(),
9208 }
9209 }
9210 }
9211 else if (actionName == "EDIT")
9212 {
9215 {
9216 active = m_tvmIsLiveTv;
9217 BUTTON2(actionName, tr("Edit Channel"), tr("Edit Recording"));
9218 }
9219 }
9220 else if (actionName == "TOGGLEAUTOEXPIRE")
9221 {
9223 {
9224 active = m_tvmIsOn;
9225 BUTTON2(actionName,
9226 tr("Turn Auto-Expire OFF"), tr("Turn Auto-Expire ON"));
9227 }
9228 }
9229 else if (actionName == "QUEUETRANSCODE")
9230 {
9231 if (m_tvmIsRecorded)
9232 {
9233 active = m_tvmTranscoding;
9234 BUTTON2(actionName, tr("Stop Transcoding"), tr("Default"));
9235 }
9236 }
9237 else if (actionName == "QUEUETRANSCODE_AUTO")
9238 {
9239 if (m_tvmIsRecorded)
9240 {
9241 active = m_tvmTranscoding;
9242 BUTTON(actionName, tr("Autodetect"));
9243 }
9244 }
9245 else if (actionName == "QUEUETRANSCODE_HIGH")
9246 {
9247 if (m_tvmIsRecorded)
9248 {
9249 active = m_tvmTranscoding;
9250 BUTTON(actionName, tr("High Quality"));
9251 }
9252 }
9253 else if (actionName == "QUEUETRANSCODE_MEDIUM")
9254 {
9255 if (m_tvmIsRecorded)
9256 {
9257 active = m_tvmTranscoding;
9258 BUTTON(actionName, tr("Medium Quality"));
9259 }
9260 }
9261 else if (actionName == "QUEUETRANSCODE_LOW")
9262 {
9263 if (m_tvmIsRecorded)
9264 {
9265 active = m_tvmTranscoding;
9266 BUTTON(actionName, tr("Low Quality"));
9267 }
9268 }
9269 else if (actionName == ACTION_CAST)
9270 {
9271 if (!m_actors.isEmpty() || !m_guestStars.isEmpty() ||
9272 !m_guests.isEmpty())
9273 BUTTON(actionName, tr("Cast"));
9274 }
9275 else
9276 {
9277 // Allow an arbitrary action if it has a translated
9278 // description available to be used as the button text.
9279 // Look in the specified keybinding context as well as the
9280 // Global context.
9281 QString text = m_mainWindow->GetActionText(Context.m_menu.GetKeyBindingContext(), actionName);
9282 if (text.isEmpty())
9283 text = m_mainWindow->GetActionText("Global", actionName);
9284 if (!text.isEmpty())
9285 BUTTON(actionName, text);
9286 }
9287 }
9288
9289 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
9290 return result;
9291}
9292
9293void TV::MenuLazyInit(void *Field)
9294{
9295 if (Field == &m_tvmFreeRecorderCount)
9296 if (m_tvmFreeRecorderCount < 0)
9298}
9299
9301{
9303 if (&Menu != &m_playbackMenu && &Menu != &m_playbackCompactMenu)
9304 return;
9305
9306 m_tvmAvsync = true;
9307
9308 m_tvmFillAutoDetect = false;
9309
9310 m_tvmSpeedX100 = std::lroundf(m_playerContext.m_tsNormal * 100);
9316 m_tvmIsPaused = false;
9321 m_tvmJump = ((m_tvmNumChapters == 0) && !m_tvmIsDvd &&
9325 m_tvmPreviousChan = false;
9326
9333 m_tvmChapterTimes.clear();
9335
9336 m_tvmSubsForcedOn = true;
9337 m_tvmSubsHaveSubs = false;
9338
9339 for (int i = kTrackTypeUnknown ; i < kTrackTypeCount ; ++i)
9340 m_tvmCurtrack[i] = -1;
9341
9342 if (m_tvmIsLiveTv)
9343 {
9344 QString prev_channum = m_playerContext.GetPreviousChannel();
9345 QString cur_channum = QString();
9347 cur_channum = m_playerContext.m_tvchain->GetChannelName(-1);
9348 if (!prev_channum.isEmpty() && prev_channum != cur_channum)
9349 m_tvmPreviousChan = true;
9350 }
9351
9352 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
9353
9355 {
9356 for (uint i = kTrackTypeUnknown ; i < kTrackTypeCount ; ++i)
9357 {
9359 if (!m_tvmTracks[i].empty())
9361 }
9363 !m_tvmTracks[kTrackTypeSubtitle].empty() ||
9365 !m_tvmTracks[kTrackTypeCC708].empty() ||
9366 !m_tvmTracks[kTrackTypeCC608].empty() ||
9370 !m_tvmTracks[kTrackTypeAudio].empty();
9375 if (vo)
9376 {
9378 }
9379 }
9380 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
9385 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
9386
9387 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
9388}
9389
9391{
9393}
9394
9395void TV::PlaybackMenuShow(const MythTVMenu &Menu, const QDomNode &Node, const QDomNode &Selected)
9396{
9397 PlaybackMenuInit(Menu);
9398 bool isPlayback = (&Menu == &m_playbackMenu || &Menu == &m_playbackCompactMenu);
9399 bool isCutlist = (&Menu == &m_cutlistMenu || &Menu == &m_cutlistCompactMenu);
9400 QString text = Menu.Translate(Node.toElement().attribute("text", Menu.GetName()));
9401 const char* windowtitle { "???" };
9402 if (isPlayback)
9403 windowtitle = OSD_DLG_MENU;
9404 else if (isCutlist)
9405 windowtitle = OSD_DLG_CUTPOINT;
9406 MythOSDDialogData menu {.m_dialogName=windowtitle, .m_message=text };
9407 Menu.Show(Node, Selected, *this, &menu);
9408 QDomNode parent = Node.parentNode();
9409 if (!parent.parentNode().isNull())
9410 {
9411 QVariant v;
9412 v.setValue(MythTVMenuNodeTuple(Menu.m_id, MythTVMenu::GetPathFromNode(Node)));
9413 menu.m_back = { .m_text="", .m_data=v };
9414 }
9415
9416 emit ChangeOSDDialog(menu);
9417
9418 if (isCutlist)
9419 {
9420 // hack to unhide the editbar
9421 InfoMap map;
9422 map.insert("title", tr("Edit"));
9424 }
9425 PlaybackMenuDeinit(Menu);
9426}
9427
9429{
9430 // Playback menu
9431 (void)tr("Playback Menu");
9432 (void)tr("Playback Compact Menu");
9433 (void)tr("Audio");
9434 (void)tr("Select Audio Track");
9435 (void)tr("Visualisation");
9436 (void)tr("Video");
9437 (void)tr("Change Aspect Ratio");
9438 (void)tr("Adjust Fill");
9439 (void)tr("Adjust Picture");
9440 (void)tr("3D");
9441 (void)tr("Advanced");
9442 (void)tr("Video Scan");
9443 (void)tr("Deinterlacer");
9444 (void)tr("Subtitles");
9445 (void)tr("Select Subtitle");
9446 (void)tr("Text Subtitles");
9447 (void)tr("Select ATSC CC");
9448 (void)tr("Select VBI CC");
9449 (void)tr("Select Teletext CC");
9450 (void)tr("Playback");
9451 (void)tr("Adjust Time Stretch");
9452 (void)tr("Picture-in-Picture");
9453 (void)tr("Sleep");
9454 (void)tr("Channel Groups");
9455 (void)tr("Navigate");
9456 (void)tr("Commercial Auto-Skip");
9457 (void)tr("Chapter");
9458 (void)tr("Angle");
9459 (void)tr("Title");
9460 (void)tr("Schedule");
9461 (void)tr("Source");
9462 (void)tr("Jump to Program");
9463 (void)tr("Switch Input");
9464 (void)tr("Switch Source");
9465 (void)tr("Jobs");
9466 (void)tr("Begin Transcoding");
9467 (void)tr("Cast");
9468 (void)tr("Recorded");
9469 (void)tr("Upcoming");
9470
9471 // Cutlist editor menu
9472 (void)tr("Edit Cut Points");
9473 (void)tr("Edit Cut Points (Compact)");
9474 (void)tr("Cut List Options");
9475}
9476
9477void TV::ShowOSDMenu(bool isCompact)
9478{
9479 if (!m_playbackMenu.IsLoaded())
9480 {
9482 "menu_playback.xml", tr("Playback Menu"),
9483 metaObject()->className(), "TV Playback");
9485 "menu_playback_compact.xml", tr("Playback Compact Menu"),
9486 metaObject()->className(), "TV Playback");
9487 }
9488
9489 if (isCompact && m_playbackCompactMenu.IsLoaded())
9491 else if (m_playbackMenu.IsLoaded())
9493}
9494
9495void TV::FillOSDMenuJumpRec(const QString &Category, int Level, const QString &Selected)
9496{
9497 // bool in_recgroup = !category.isEmpty() && level > 0;
9498 if (Level < 0 || Level > 1)
9499 {
9500 Level = 0;
9501 // in_recgroup = false;
9502 }
9503
9504 MythOSDDialogData dialog { .m_dialogName="osd_jumprec",
9505 .m_message=tr("Recorded Program") };
9506
9507 QMutexLocker locker(&m_progListsLock);
9508 m_progLists.clear();
9509 std::vector<ProgramInfo*> *infoList = RemoteGetRecordedList(0);
9510 bool LiveTVInAllPrograms = gCoreContext->GetBoolSetting("LiveTVInAllPrograms",false);
9511 if (infoList)
9512 {
9513 QList<QString> titles_seen;
9514
9515 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
9516 QString currecgroup = m_playerContext.m_playingInfo->GetRecordingGroup();
9517 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
9518
9519 for (auto *pi : *infoList)
9520 {
9521 if (pi->GetRecordingGroup() != "LiveTV" || LiveTVInAllPrograms ||
9522 pi->GetRecordingGroup() == currecgroup)
9523 {
9524 m_progLists[pi->GetRecordingGroup()].push_front(
9525 new ProgramInfo(*pi));
9526 }
9527 }
9528
9529 ProgramInfo *lastprog = GetLastProgram();
9530 QMap<QString,ProgramList>::const_iterator Iprog;
9531 for (Iprog = m_progLists.cbegin(); Iprog != m_progLists.cend(); ++Iprog)
9532 {
9533 const ProgramList &plist = *Iprog;
9534 auto progIndex = static_cast<uint>(plist.size());
9535 const QString& group = Iprog.key();
9536
9537 if (plist[0] && (plist[0]->GetRecordingGroup() != currecgroup))
9538 SetLastProgram(plist[0]);
9539
9540 if (progIndex == 1 && Level == 0)
9541 {
9542 dialog.m_buttons.push_back( {Iprog.key(), QString("JUMPPROG %1 0").arg(group) });
9543 }
9544 else if (progIndex > 1 && Level == 0)
9545 {
9546 QString act = QString("DIALOG_%1_%2_1")
9547 .arg(ACTION_JUMPREC, group);
9548 dialog.m_buttons.push_back( {group, act, true, Selected == group });
9549 }
9550 else if (Level == 1 && Iprog.key() == Category)
9551 {
9552 for (auto pit = plist.begin(); pit != plist.end(); ++pit)
9553 {
9554 const ProgramInfo *p = *pit;
9555
9556 if (titles_seen.contains(p->GetTitle()))
9557 continue;
9558
9559 titles_seen.push_back(p->GetTitle());
9560
9561 int j = -1;
9562 for (auto *q : plist)
9563 {
9564 j++;
9565
9566 if (q->GetTitle() != p->GetTitle())
9567 continue;
9568
9569 dialog.m_buttons.push_back( { q->GetSubtitle().isEmpty() ?
9570 q->GetTitle() : q->GetSubtitle(),
9571 QString("JUMPPROG %1 %2").arg(Iprog.key()).arg(j) });
9572 }
9573 }
9574 }
9575 }
9576 SetLastProgram(lastprog);
9577 delete lastprog;
9578
9579 while (!infoList->empty())
9580 {
9581 delete infoList->back();
9582 infoList->pop_back();
9583 }
9584 delete infoList;
9585 }
9586
9587 if (!Category.isEmpty())
9588 {
9589 if (Level == 1)
9590 {
9591 dialog.m_back = { .m_text=Category,
9592 .m_data="DIALOG_" + ACTION_JUMPREC + "_X_0" };
9593 }
9594 else if (Level == 0)
9595 {
9596 if (m_tvmJumprecBackHack.isValid())
9597 dialog.m_back = { .m_text="",
9598 .m_data=m_tvmJumprecBackHack };
9599 else
9600 dialog.m_back = { .m_text=ACTION_JUMPREC,
9601 .m_data="DIALOG_MENU_" + ACTION_JUMPREC +"_0" };
9602 }
9603 }
9604
9605 emit ChangeOSDDialog(dialog);
9606}
9607
9609{
9610 bool recorded = (ProgInfo.GetFilesize() > 0);
9611 QString table = recorded ? "recordedcredits" : "credits";
9612
9613 m_actors.clear();
9614 m_guestStars.clear();
9615 m_guests.clear();
9616
9618 query.prepare(QString("SELECT role, people.name,"
9619 " roles.name, people.person FROM %1"
9620 " AS credits"
9621 " LEFT JOIN people ON"
9622 " credits.person = people.person"
9623 " LEFT JOIN roles ON"
9624 " credits.roleid = roles.roleid"
9625 " WHERE credits.chanid = :CHANID"
9626 " AND credits.starttime = :STARTTIME"
9627 " AND role IN ('guest','actor','guest_star')"
9628 " ORDER BY role, priority;").arg(table));
9629
9630 query.bindValue(":CHANID", ProgInfo.GetChanID());
9631 query.bindValue(":STARTTIME", ProgInfo.GetScheduledStartTime());
9632
9633 if (query.exec() && query.size() > 0)
9634 {
9635 QString role;
9636 QString pname;
9637 QString character;
9638
9639 while(query.next())
9640 {
9641 role = query.value(0).toString();
9642 /* The people.name, roles.name columns uses utf8_bin collation.
9643 * Qt-MySQL drivers use QVariant::ByteArray for string-type
9644 * MySQL fields marked with the BINARY attribute (those using a
9645 * *_bin collation) and QVariant::String for all others.
9646 * Since QVariant::toString() uses QString::fromAscii()
9647 * (through QVariant::convert()) when the QVariant's type is
9648 * QVariant::ByteArray, we have to use QString::fromUtf8()
9649 * explicitly to prevent corrupting characters.
9650 * The following code should be changed to use the simpler
9651 * toString() approach, as above, if we do a DB update to
9652 * coalesce the people.name values that differ only in case and
9653 * change the collation to utf8_general_ci, to match the
9654 * majority of other columns, or we'll have the same problem in
9655 * reverse.
9656 */
9657 int pid = query.value(3).toInt();
9658 pname = QString::fromUtf8(query.value(1)
9659 .toByteArray().constData()) +
9660 "|" + QString::number(pid);
9661 character = QString::fromUtf8(query.value(2)
9662 .toByteArray().constData());
9663
9664 if (role == "actor")
9665 m_actors.append(qMakePair(pname, character));
9666 else if (role == "guest_star")
9667 m_guestStars.append(qMakePair(pname, character));
9668 else if (role == "guest")
9669 m_guests.append(qMakePair(pname, character));
9670 }
9671 }
9672
9673}
9674
9676 const QVector<string_pair> & people)
9677{
9678 for (const auto & [actor, role] : std::as_const(people))
9679 {
9680 if (role.isEmpty())
9681 {
9682 dialog.m_buttons.push_back( {actor.split('|')[0],
9683 QString("JUMPCAST|%1").arg(actor), true} );
9684 }
9685 else
9686 {
9687 dialog.m_buttons.push_back( {QString("%1 as %2")
9688 .arg(actor.split('|')[0], role),
9689 QString("JUMPCAST|%1").arg(actor), true} );
9690 }
9691 }
9692}
9693
9695{
9696 MythOSDDialogData dialog { .m_dialogName="osd_cast", .m_message=tr("Cast") };
9698
9702
9703 emit ChangeOSDDialog(dialog);
9704}
9705
9706void TV::FillOSDMenuActorShows(const QString & actor, int person_id,
9707 const QString & category)
9708{
9709 MythOSDDialogData dialog { .m_dialogName=actor, .m_message=actor };
9710
9711 if (category.isEmpty())
9712 {
9713 dialog.m_buttons.push_back( {"Recorded",
9714 QString("JUMPCAST|%1|%2|Recorded").arg(actor).arg(person_id) } );
9715 dialog.m_buttons.push_back( {"Upcoming",
9716 QString("JUMPCAST|%1|%2|Upcoming").arg(actor).arg(person_id) } );
9717 emit ChangeOSDDialog(dialog);
9718 return;
9719 }
9720
9721 if (category == "Upcoming")
9722 {
9724 return;
9725 }
9726
9727 /*
9728 JUMPCAST|Amanda Burton|133897|Recorded
9729 JUMPCAST|Amanda Burton|133897|Upcoming
9730 */
9731 if (!m_progLists.contains(actor))
9732 {
9733 QString table = "recordedcredits";
9735 query.prepare(QString("SELECT chanid, starttime from %1"
9736 " where person = :PERSON"
9737 " ORDER BY starttime;").arg(table));
9738 query.bindValue(":PERSON", person_id);
9739
9740 QDateTime starttime;
9741 if (query.exec() && query.size() > 0)
9742 {
9743 while(query.next())
9744 {
9745 int chanid = query.value(0).toInt();
9746 starttime = MythDate::fromString(query.value(1).toString());
9747 auto *pi = new ProgramInfo(chanid, starttime.toUTC());
9748 if (!pi->GetTitle().isEmpty() &&
9749 pi->GetRecordingGroup() != "LiveTV" &&
9750 pi->GetRecordingGroup() != "Deleted")
9751 m_progLists[actor].push_back(pi);
9752 }
9753
9754 std::stable_sort(m_progLists[actor].begin(),
9755 m_progLists[actor].end(), comp_title);
9756 }
9757 }
9758
9759 QString show;
9760 int idx = -1;
9761 for (auto & pi : m_progLists[actor])
9762 {
9763 show = pi->GetTitle();
9764 if (show.isEmpty())
9765 continue;
9766 if (!pi->GetSubtitle().isEmpty())
9767 {
9768 show += QString(" %1x%2 %3").arg(pi->GetSeason())
9769 .arg(pi->GetEpisode())
9770 .arg(pi->GetSubtitle());
9771 }
9772
9773 dialog.m_buttons.push_back( {show,
9774 QString("JUMPPROG %1 %2").arg(actor).arg(++idx) });
9775 }
9776 emit ChangeOSDDialog(dialog);
9777}
9778
9780{
9781 QString message;
9782 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
9783 if (m_player)
9784 {
9786 message = ScanTypeToUserString(Scan == kScan_Detect ? kScan_Detect :
9788 }
9789 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
9790
9791 if (!message.isEmpty())
9792 emit ChangeOSDMessage(message);
9793}
9794
9796{
9797 QString desc;
9798
9799 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
9800
9802 {
9804 desc = tr("Auto-Expire OFF");
9805 }
9806 else
9807 {
9809 desc = tr("Auto-Expire ON");
9810 }
9811
9812 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
9813
9814 if (!desc.isEmpty())
9816}
9817
9819{
9820 QString desc;
9821
9822 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
9823 if (m_player)
9824 {
9827 }
9828 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
9829
9830 if (!desc.isEmpty())
9832}
9833
9834void TV::SetManualZoom(bool ZoomON, const QString& Desc)
9835{
9836 m_zoomMode = ZoomON;
9837 if (ZoomON)
9838 ClearOSD();
9839 if (!Desc.isEmpty())
9841}
9842
9843bool TV::HandleJumpToProgramAction(const QStringList &Actions)
9844{
9845 TVState state = GetState();
9846 if (IsActionable({ ACTION_JUMPPREV, "PREVCHAN" }, Actions) &&
9847 !StateIsLiveTV(state))
9848 {
9849 PrepareToExitPlayer(__LINE__);
9850 m_jumpToProgram = true;
9851 SetExitPlayer(true, true);
9852 return true;
9853 }
9854
9855 for (const auto& action : std::as_const(Actions))
9856 {
9857 if (!action.startsWith("JUMPPROG"))
9858 continue;
9859
9860 bool ok = false;
9861 QString key = action.section(" ",1,-2);
9862 uint index = action.section(" ",-1,-1).toUInt(&ok);
9863 ProgramInfo* proginfo = nullptr;
9864
9865 if (ok)
9866 {
9867 QMutexLocker locker(&m_progListsLock);
9868 auto pit = m_progLists.find(key);
9869 if (pit != m_progLists.end())
9870 {
9871 const ProgramInfo* tmp = (*pit)[index];
9872 if (tmp)
9873 proginfo = new ProgramInfo(*tmp);
9874 }
9875 }
9876
9877 if (!proginfo)
9878 {
9879 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to locate jump to program '%1' @ %2")
9880 .arg(key, action.section(" ",-1,-1)));
9881 return true;
9882 }
9883
9885
9886 delete proginfo;
9887 return true;
9888 }
9889
9890 if (!IsActionable(ACTION_JUMPREC, Actions))
9891 return false;
9892
9893 if (m_dbJumpPreferOsd && (StateIsPlaying(state) || StateIsLiveTV(state)))
9894 {
9895 // TODO I'm not sure this really needs to be asyncronous
9896 auto Jump = [&]()
9897 {
9901 };
9902 QTimer::singleShot(0, this, Jump);
9903 }
9904 else if (RunPlaybackBoxPtr)
9905 {
9907 }
9908 else
9909 {
9910 LOG(VB_GENERAL, LOG_ERR, "Failed to open jump to program GUI");
9911 }
9912
9913 return true;
9914}
9915
9916void TV::ToggleSleepTimer(const QString& Time)
9917{
9918 std::chrono::minutes mins { 0min };
9919
9920 if (Time == ACTION_TOGGLESLEEP + "ON")
9921 {
9922 if (m_sleepTimerId)
9923 {
9925 m_sleepTimerId = 0;
9926 }
9927 else
9928 {
9929 m_sleepTimerTimeout = mins = 60min;
9931 }
9932 }
9933 else
9934 {
9935 if (m_sleepTimerId)
9936 {
9938 m_sleepTimerId = 0;
9939 }
9940
9941 if (Time.length() > 11)
9942 {
9943 bool intRead = false;
9944 mins = std::chrono::minutes(Time.right(Time.length() - 11).toUInt(&intRead));
9945
9946 if (intRead)
9947 {
9948 // catch 120 -> 240 mins
9949 if (mins < 30min)
9950 {
9951 mins *= 10;
9952 }
9953 }
9954 else
9955 {
9956 mins = 0min;
9957 LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid time " + Time);
9958 }
9959 }
9960 else
9961 {
9962 LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid time string " + Time);
9963 }
9964
9965 if (mins > 0min)
9966 {
9967 m_sleepTimerTimeout = mins;
9969 }
9970 }
9971
9972 QString out;
9973 if (mins != 0min)
9974 out = tr("Sleep") + " " + QString::number(mins.count());
9975 else
9976 out = tr("Sleep") + " " + kSleepTimes[0].dispString;
9977 emit ChangeOSDMessage(out);
9978}
9979
9981{
9982 QString errorText;
9983
9984 switch (MsgType)
9985 {
9986 case kNoRecorders:
9987 errorText = tr("MythTV is already using all available "
9988 "inputs for the channel you selected. "
9989 "If you want to watch an in-progress recording, "
9990 "select one from the playback menu. If you "
9991 "want to watch Live TV, cancel one of the "
9992 "in-progress recordings from the delete "
9993 "menu.");
9994 break;
9995 case kNoCurrRec:
9996 errorText = tr("Error: MythTV is using all inputs, "
9997 "but there are no active recordings?");
9998 break;
9999 case kNoTuners:
10000 errorText = tr("MythTV has no capture cards defined. "
10001 "Please run the mythtv-setup program.");
10002 break;
10003 }
10004
10005 emit ChangeOSDDialog(
10006 { .m_dialogName=OSD_DLG_INFO,
10007 .m_message=errorText,
10008 .m_timeout=0ms,
10009 .m_buttons={{ tr("OK"), "DIALOG_INFO_X_X" }}});
10010}
10011
10016{
10017 m_lockTimerOn = false;
10018
10019 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
10021 {
10026 }
10027 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
10028
10029 // XXX: Get rid of this?
10031
10034
10035 m_lockTimerOn = false;
10036
10037 QString input = m_playerContext.m_recorder->GetInput();
10039
10040 if (timeout < 0xffffffff)
10041 {
10042 m_lockTimer.start();
10043 m_lockTimerOn = true;
10044 }
10045
10046 SetSpeedChangeTimer(0ms, __LINE__);
10047}
10048
10052void TV::UnpauseLiveTV(bool Quietly)
10053{
10055 {
10058 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
10059 if (m_player)
10060 m_player->Play(m_playerContext.m_tsNormal, true, false);
10061 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
10064 SetSpeedChangeTimer(0ms, __LINE__);
10065 }
10066
10067 ITVRestart(true);
10068
10069 if (m_playerContext.HasPlayer() && !Quietly)
10070 {
10072 UpdateLCD();
10074 }
10075}
10076
10080void TV::ITVRestart(bool IsLive)
10081{
10082 int chanid = -1;
10083 int sourceid = -1;
10084
10085 if (ContextIsPaused(__FILE__, __LINE__))
10086 return;
10087
10088 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
10090 {
10091 chanid = static_cast<int>(m_playerContext.m_playingInfo->GetChanID());
10092 sourceid = static_cast<int>(ChannelUtil::GetSourceIDForChannel(static_cast<uint>(chanid)));
10093 }
10094 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10095
10096 emit RestartITV(static_cast<uint>(chanid), static_cast<uint>(sourceid), IsLive);
10097}
10098
10100{
10103 else if (GetNumChapters() > 0)
10104 DoJumpChapter(9999);
10105 else
10106 DoSeek(m_playerContext.m_jumptime, tr("Jump Ahead"), /*timeIsOffset*/true, /*honorCutlist*/true);
10107}
10108
10110{
10111 DoSeek(m_playerContext.m_fftime, tr("Skip Ahead"), /*timeIsOffset*/true, /*honorCutlist*/true);
10112}
10113
10115{
10117 DVDJumpBack();
10118 else if (GetNumChapters() > 0)
10119 DoJumpChapter(-1);
10120 else
10121 DoSeek(-m_playerContext.m_jumptime, tr("Jump Back"), /*timeIsOffset*/true, /*honorCutlist*/true);
10122}
10123
10125{
10126 DoSeek(-m_playerContext.m_rewtime, tr("Jump Back"), /*timeIsOffset*/true, /*honorCutlist*/true);
10127}
10128
10129/* \fn TV::DVDJumpBack(PlayerContext*)
10130 \brief jump to the previous dvd title or chapter
10131*/
10133{
10134 auto *dvd = dynamic_cast<MythDVDBuffer*>(m_playerContext.m_buffer);
10135 if (!m_playerContext.HasPlayer() || !dvd)
10136 return;
10137
10139 {
10140 UpdateOSDSeekMessage(tr("Skip Back Not Allowed"), kOSDTimeout_Med);
10141 }
10142 else if (!dvd->StartOfTitle())
10143 {
10144 DoJumpChapter(-1);
10145 }
10146 else
10147 {
10148 std::chrono::seconds titleLength = dvd->GetTotalTimeOfTitle();
10149 std::chrono::seconds chapterLength = dvd->GetChapterLength();
10150 if ((titleLength == chapterLength) && chapterLength > 5min)
10151 {
10152 DoSeek(-m_playerContext.m_jumptime, tr("Jump Back"), /*timeIsOffset*/true, /*honorCutlist*/true);
10153 }
10154 else
10155 {
10156 emit GoToDVDProgram(false);
10157 UpdateOSDSeekMessage(tr("Previous Title"), kOSDTimeout_Med);
10158 }
10159 }
10160}
10161
10162/* \fn TV::DVDJumpForward(PlayerContext*)
10163 * \brief jump to the next dvd title or chapter
10164 */
10166{
10167 auto *dvd = dynamic_cast<MythDVDBuffer*>(m_playerContext.m_buffer);
10168 if (!m_playerContext.HasPlayer() || !dvd)
10169 return;
10170
10171 bool in_still = dvd->IsInStillFrame();
10172 bool in_menu = dvd->IsInMenu();
10173 if (in_still && !dvd->NumMenuButtons())
10174 {
10175 dvd->SkipStillFrame();
10176 UpdateOSDSeekMessage(tr("Skip Still Frame"), kOSDTimeout_Med);
10177 }
10178 else if (!dvd->EndOfTitle() && !in_still && !in_menu)
10179 {
10180 DoJumpChapter(9999);
10181 }
10182 else if (!in_still && !in_menu)
10183 {
10184 std::chrono::seconds titleLength = dvd->GetTotalTimeOfTitle();
10185 std::chrono::seconds chapterLength = dvd->GetChapterLength();
10186 std::chrono::seconds currentTime = dvd->GetCurrentTime();
10187 if ((titleLength == chapterLength) && (chapterLength > 5min) &&
10188 (currentTime < (chapterLength - (duration_cast<std::chrono::seconds>(m_playerContext.m_jumptime)))))
10189 {
10190 DoSeek(m_playerContext.m_jumptime, tr("Jump Ahead"), /*timeIsOffset*/true, /*honorCutlist*/true);
10191 }
10192 else
10193 {
10194 emit GoToDVDProgram(true);
10195 UpdateOSDSeekMessage(tr("Next Title"), kOSDTimeout_Med);
10196 }
10197 }
10198}
10199
10200/* \fn TV::IsBookmarkAllowed(const PlayerContext*) const
10201 * \brief Returns true if bookmarks are allowed for the current player.
10202 */
10204{
10205 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
10206
10207 // Allow bookmark of "Record current LiveTV program"
10210 {
10211 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10212 return false;
10213 }
10214
10216 {
10217 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10218 return false;
10219 }
10220
10221 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10222
10224}
10225
10226/* \fn TV::IsDeleteAllowed() const
10227 * \brief Returns true if the delete menu option should be offered.
10228 */
10230{
10231 bool allowed = false;
10232
10233 if (!StateIsLiveTV(GetState()))
10234 {
10235 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
10237 allowed = curProgram && curProgram->QueryIsDeleteCandidate(true);
10238 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10239 }
10240
10241 return allowed;
10242}
10243
10245{
10246 ClearOSD();
10247
10248 if (!ContextIsPaused(__FILE__, __LINE__))
10249 DoTogglePause(false);
10250
10251 QString videotype;
10252
10253 if (StateIsLiveTV(GetState()))
10254 videotype = tr("Live TV");
10256 videotype = tr("this DVD");
10257
10258 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
10259 if (videotype.isEmpty() && m_playerContext.m_playingInfo->IsVideo())
10260 videotype = tr("this Video");
10261 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10262
10263 if (videotype.isEmpty())
10264 videotype = tr("this recording");
10265
10267 .m_message=tr("You are exiting %1").arg(videotype) };
10268
10269 dialog.m_buttons.push_back({tr("Exit %1").arg(videotype), ACTION_STOP});
10270
10271 dialog.m_buttons.push_back({tr("Exit Without Saving"), "DIALOG_VIDEOEXIT_CLEARLASTPLAYEDPOSITION_0"});
10272
10273 if (IsDeleteAllowed())
10274 dialog.m_buttons.push_back({tr("Delete this recording"), "DIALOG_VIDEOEXIT_CONFIRMDELETE_0"});
10275
10276 dialog.m_buttons.push_back({tr("Keep watching"), "DIALOG_VIDEOEXIT_KEEPWATCHING_0"});
10277 dialog.m_back = { .m_text="",
10278 .m_data="DIALOG_VIDEOEXIT_KEEPWATCHING_0",
10279 .m_exit=true };
10280 emit ChangeOSDDialog(dialog);
10281
10282 if (m_videoExitDialogTimerId)
10283 KillTimer(m_videoExitDialogTimerId);
10284 m_videoExitDialogTimerId = StartTimer(kVideoExitDialogTimeout, __LINE__);
10285}
10286
10287void TV::ShowOSDPromptDeleteRecording(const QString& Title, bool Force)
10288{
10289 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
10290
10292 {
10293 // this should only occur when the cat walks on the keyboard.
10294 LOG(VB_GENERAL, LOG_ERR, "It is unsafe to delete at the moment");
10295 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10296 return;
10297 }
10298
10299 bool paused = ContextIsPaused(__FILE__, __LINE__);
10301 {
10302 LOG(VB_GENERAL, LOG_ERR, "This program cannot be deleted at this time.");
10304 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10305
10306 OSD *osd = GetOSDL();
10307 if (osd && !osd->DialogVisible())
10308 {
10309 QString message = tr("Cannot delete program ") + QString("%1 ").arg(pginfo.GetTitle());
10310
10311 if (!pginfo.GetSubtitle().isEmpty())
10312 message += QString("\"%1\" ").arg(pginfo.GetSubtitle());
10313
10314 if (!pginfo.IsRecording())
10315 {
10316 message += tr("because it is not a recording.");
10317 }
10318 else
10319 {
10320 message += tr("because it is in use by");
10321 QStringList byWho;
10322 pginfo.QueryIsInUse(byWho);
10323 for (int i = 0; (i + 2) < byWho.size(); i += 3)
10324 {
10325 if (byWho[i + 1] == gCoreContext->GetHostName() && byWho[i].contains(kPlayerInUseID))
10326 continue;
10327 if (byWho[i].contains(kRecorderInUseID))
10328 continue;
10329 message += " " + byWho[i+2];
10330 }
10331 }
10332 emit ChangeOSDDialog(
10333 {.m_dialogName=OSD_DLG_DELETE,
10334 .m_message=message,
10335 .m_timeout=0ms,
10336 .m_buttons={{ tr("OK"), "DIALOG_DELETE_OK_0" }},
10337 .m_back={ .m_text="", .m_data="DIALOG_DELETE_OK_0", .m_exit=true }});
10338 }
10339 ReturnOSDLock();
10340 // If the delete prompt is to be displayed at the end of a
10341 // recording that ends in a final cut region, it will get into
10342 // a loop of popping up the OK button while the cut region
10343 // plays. Avoid this.
10344 if (m_player->IsNearEnd() && !paused)
10345 SetExitPlayer(true, true);
10346
10347 return;
10348 }
10349 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10350
10351 ClearOSD();
10352
10353 if (!paused)
10354 DoTogglePause(false);
10355
10356 InfoMap infoMap;
10358 if (m_player)
10359 m_player->GetCodecDescription(infoMap);
10360 QString message = QString("%1\n%2\n%3")
10361 .arg(Title, infoMap["title"], infoMap["timedate"]);
10362
10363 OSD *osd = GetOSDL();
10364 if (osd && (!osd->DialogVisible() || Force))
10365 {
10367 .m_message=message };
10368 if (Title == "End Of Recording")
10369 {
10370 dialog.m_buttons.push_back({tr("Delete it, but allow it to re-record"), "DIALOG_VIDEOEXIT_DELETEANDRERECORD_0"});
10371 dialog.m_buttons.push_back({tr("Delete it"), "DIALOG_VIDEOEXIT_JUSTDELETE_0"});
10372 dialog.m_buttons.push_back({tr("Save it so I can watch it again"), ACTION_STOP, false, true});
10373 }
10374 else
10375 {
10376 dialog.m_buttons.push_back({tr("Yes, and allow re-record"), "DIALOG_VIDEOEXIT_DELETEANDRERECORD_0"});
10377 dialog.m_buttons.push_back({tr("Yes, delete it"), "DIALOG_VIDEOEXIT_JUSTDELETE_0"});
10378 dialog.m_buttons.push_back({tr("No, keep it"), ACTION_STOP, false, true});
10379 if (!paused)
10380 dialog.m_back = { .m_text="",
10381 .m_data="DIALOG_PLAY_0_0",
10382 .m_exit=true };
10383 }
10384
10385 emit ChangeOSDDialog(dialog);
10386
10390 }
10391 ReturnOSDLock();
10392}
10393
10394bool TV::HandleOSDVideoExit(const QString& Action)
10395{
10397 return false;
10398
10399 bool hide = true;
10400 bool delete_ok = IsDeleteAllowed();
10401
10402 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
10403 bool near_end = m_player && m_player->IsNearEnd();
10404 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
10405
10406 if (Action == "DELETEANDRERECORD" && delete_ok)
10407 {
10408 m_allowRerecord = true;
10409 m_requestDelete = true;
10410 PrepareToExitPlayer(__LINE__);
10411 SetExitPlayer(true, true);
10412 }
10413 else if (Action == "JUSTDELETE" && delete_ok)
10414 {
10415 m_requestDelete = true;
10416 PrepareToExitPlayer(__LINE__);
10417 SetExitPlayer(true, true);
10418 }
10419 else if (Action == "CONFIRMDELETE")
10420 {
10421 hide = false;
10422 ShowOSDPromptDeleteRecording(tr("Are you sure you want to delete:"), true);
10423 }
10424 else if (Action == "KEEPWATCHING" && !near_end)
10425 {
10426 DoTogglePause(true);
10427 }
10428 else if (Action == "CLEARLASTPLAYEDPOSITION")
10429 {
10430 m_clearPosOnExit = true;
10431 PrepareToExitPlayer(__LINE__);
10432 SetExitPlayer(true, true);
10433 }
10434
10435 return hide;
10436}
10437
10439{
10441 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
10442 bool playing = m_player && !m_player->IsPaused();
10443 // Don't bother saving lastplaypos while paused
10444 if (playing)
10445 {
10446 uint64_t framesPlayed = m_player->GetFramesPlayed();
10447 auto *savPosThread = new SavePositionThread(m_playerContext.m_playingInfo,
10448 framesPlayed);
10449 GetPosThreadPool()->start(savPosThread, "PositionSaver");
10450 }
10451 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
10453
10454 m_savePosOnExit = true;
10455}
10456
10458{
10459 QMutexLocker locker(&m_lastProgramLock);
10460
10461 delete m_lastProgram;
10462
10463 if (ProgInfo)
10465 else
10466 m_lastProgram = nullptr;
10467}
10468
10470{
10471 QMutexLocker locker(&m_lastProgramLock);
10472 if (m_lastProgram)
10473 return new ProgramInfo(*m_lastProgram);
10474 return nullptr;
10475}
10476
10478{
10479 QString ret;
10480
10482 if (StateIsPlaying(GetState()))
10483 {
10484 m_playerContext.LockPlayingInfo(__FILE__, __LINE__);
10487 m_playerContext.UnlockPlayingInfo(__FILE__, __LINE__);
10488 }
10490 return ret;
10491}
10492
10494{
10495 if (!ProgInfo)
10496 return false;
10497
10498 bool ret = false;
10502 return ret;
10503}
10504
10505bool TV::ContextIsPaused(const char *File, int Location)
10506{
10507 bool paused = false;
10508 m_playerContext.LockDeletePlayer(File, Location);
10509 if (m_player)
10510 paused = m_player->IsPaused();
10511 m_playerContext.UnlockDeletePlayer(File, Location);
10512 return paused;
10513}
10514
10516{
10517 m_playerContext.LockDeletePlayer(__FILE__, __LINE__);
10518 if (m_player)
10519 {
10520 m_player->LockOSD();
10521 OSD *osd = m_player->GetOSD();
10522 if (!osd)
10523 {
10525 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
10526 }
10527 return osd;
10528 }
10529 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
10530 return nullptr;
10531}
10532
10534{
10535 if (m_player)
10537 m_playerContext.UnlockDeletePlayer(__FILE__, __LINE__);
10538}
10539
10541{
10542 m_playerLock.lockForWrite();
10543}
10544
10546{
10547 m_playerLock.lockForRead();
10548}
10549
10551{
10552 m_playerLock.unlock();
10553}
10554
10555void TV::onApplicationStateChange(Qt::ApplicationState State)
10556{
10557 switch (State)
10558 {
10559 case Qt::ApplicationState::ApplicationSuspended:
10560 {
10561 LOG(VB_GENERAL, LOG_NOTICE, "Exiting playback on app suspecnd");
10562 StopPlayback();
10563 break;
10564 }
10565 default:
10566 break;
10567 }
10568}
10569
10571{
10572 return m_posThreadPool;
10573}
10574
10576{
10577 if (m_progInfo)
10578 {
10579 try
10580 {
10582 }
10583 catch (...)
10584 {
10585 LOG(VB_GENERAL, LOG_ERR, "An exception occurred");
10586 }
10587 }
10588}
#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:1802
static std::vector< uint > GetInputGroups(uint inputid)
Definition: cardutil.cpp:2200
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1960
static bool SetStartChannel(uint inputid, const QString &channum)
Definition: cardutil.cpp:1690
static QString GetDisplayName(uint inputid)
Definition: cardutil.cpp:1886
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:931
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:520
static bool IsJobQueuedOrRunning(int jobType, uint chanid, const QDateTime &recstartts)
Definition: jobqueue.cpp:1110
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:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QVariant value(int i) const
Definition: mythdbcon.h:204
int size(void) const
Definition: mythdbcon.h:214
bool isActive(void) const
Definition: mythdbcon.h:215
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
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:74
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:380
QString GetBasename(void) const
Definition: programinfo.h:351
bool HasPathname(void) const
Definition: programinfo.h:365
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:352
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
bool IsVideoDVD(void) const
Definition: programinfo.h:354
void SetIgnoreProgStart(bool ignore)
If "ignore" is true QueryProgStart() will return 0, otherwise QueryProgStart() will return the progst...
Definition: programinfo.h:577
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:570
uint GetEpisode(void) const
Definition: programinfo.h:374
bool IsVideo(void) const
Definition: programinfo.h:497
QString GetProgramID(void) const
Definition: programinfo.h:447
QString GetRecordingGroup(void) const
Definition: programinfo.h:427
void SaveAutoExpire(AutoExpireType autoExpire, bool updateDelete=false)
Set "autoexpire" field in "recorded" table to "autoExpire".
uint GetRecordingID(void) const
Definition: programinfo.h:457
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:585
QString GetHostname(void) const
Definition: programinfo.h:429
bool IsRecording(void) const
Definition: programinfo.h:498
uint GetSourceID(void) const
Definition: programinfo.h:473
QString GetPlaybackGroup(void) const
Definition: programinfo.h:428
QString GetTitle(void) const
Definition: programinfo.h:368
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:412
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:398
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:384
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:346
QString GetSortTitle(void) const
Definition: programinfo.h:369
bool IsVideoBD(void) const
Definition: programinfo.h:356
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:350
QDate GetOriginalAirDate(void) const
Definition: programinfo.h:439
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:370
uint GetSeason(void) const
Definition: programinfo.h:373
QString GetChannelSchedulingID(void) const
This is the unique programming identifier of a channel.
Definition: programinfo.h:391
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:36
void QuickRecord(void)
Create a kSingleRecord if not already scheduled.
void ApplyRecordRecGroupChange(const QString &newrecgroup)
Sets the recording group, both in this RecordingInfo and in the database.
void ApplyTranscoderProfileChange(const QString &profile) const
Sets the transcoder profile for a recording.
Internal representation of a recording rule, mirrors the record table.
Definition: recordingrule.h:30
bool LoadTemplate(const QString &title, const QString &category="Default", const QString &categoryType="Default")
General purpose reference counter.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
QAtomicInt m_referenceCount
virtual int IncrRef(void)
Increments reference count.
std::chrono::milliseconds SetSignalMonitoringRate(std::chrono::milliseconds rate, int notifyFrontend=1)
Sets the signal monitoring rate.
void StopLiveTV(void)
Tells TVRec to stop a "Live TV" recorder.
bool ShouldSwitchToAnotherCard(const QString &channelid)
Checks if named channel exists on current tuner, or another tuner.
void SpawnLiveTV(const QString &chainid, bool pip, const QString &startchan)
Tells TVRec to Spawn a "Live TV" recorder.
void ChangeChannel(int channeldirection)
void SetLiveRecording(bool recording)
int ChangePictureAttribute(PictureAdjustType type, PictureAttribute attr, bool up)
Changes brightness/contrast/colour/hue of a recording.
int GetPictureAttribute(PictureAttribute attr)
bool IsValidRecorder(void) const
void GetChannelInfo(InfoMap &infoMap, uint chanid=0)
bool CheckChannelPrefix(const QString &prefix, uint &complete_valid_channel_on_rec, bool &is_extra_char_useful, QString &needed_spacer)
Checks a prefix against the channels in the DB.
void PauseRecorder(void)
Tells TVRec to pause a recorder, used for channel and input changes.
void SetChannel(const QString &channel)
uint GetSignalLockTimeout(const QString &input)
void FrontendReady(void)
bool SetChannelInfo(const InfoMap &infoMap)
QString GetInput(void)
bool Setup(void)
bool CheckChannel(const QString &channel)
Checks if named channel exists on current tuner.
void CancelNextRecording(bool cancel)
int GetRecorderNumber(void) const
void ToggleChannelFavorite(const QString &changroupname)
bool IsRecording(bool *ok=nullptr)
uint64_t m_framesPlayed
Definition: tv_play.h:798
ProgramInfo * m_progInfo
Definition: tv_play.h:797
void run() override
Definition: tv_play.cpp:10575
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:157
QList< std::chrono::seconds > m_tvmChapterTimes
Definition: tv_play.h:738
void ChannelEditXDSFill(InfoMap &Info)
Definition: tv_play.cpp:8079
void DoQueueTranscode(const QString &Profile)
Definition: tv_play.cpp:5341
QString m_lcdTitle
Definition: tv_play.h:662
void HandleOSDIdle(const QString &Action)
Definition: tv_play.cpp:7229
MythTVMenu m_playbackCompactMenu
Definition: tv_play.h:754
bool SubtitleZoomHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3749
void VolumeChange(bool Up, int NewVolume=-1)
Definition: tv_play.cpp:7028
bool ManualZoomHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3600
PictureAttribute m_adjustingPictureAttribute
Picture attribute to modify (on arrow left or right)
Definition: tv_play.h:576
void DVDJumpForward()
Definition: tv_play.cpp:10165
static bool IsTVRunning()
Check whether media is currently playing.
Definition: tv_play.cpp:176
OSD * GetOSDL()
Definition: tv_play.cpp:10515
volatile int m_endOfRecPromptTimerId
Definition: tv_play.h:698
void ShowLCDChannelInfo()
Definition: tv_play.cpp:6702
bool HandleLCDTimerEvent()
Definition: tv_play.cpp:2655
void QuickRecord()
Definition: tv_play.cpp:7688
void GetPlayerWriteLock() const
Definition: tv_play.cpp:10540
void ProcessNetworkControlCommand(const QString &Command)
Definition: tv_play.cpp:4303
InfoMap m_chanEditMap
Channel Editing initial map.
Definition: tv_play.h:590
bool m_requestDelete
User wants last video deleted.
Definition: tv_play.h:567
QList< QKeyEvent * > m_screenPressKeyMapLiveTV
Definition: tv_play.h:630
QString GetTitleName(int Title)
Definition: tv_play.cpp:5494
int Playback(const ProgramInfo &ProgInfo)
Definition: tv_play.cpp:1927
bool GetJumpToProgram() const
This is set if the user asked MythTV to jump to the previous recording in the playlist.
Definition: tv_play.h:325
volatile int m_endOfPlaybackTimerId
Definition: tv_play.h:697
void IdleDialogTimeout()
Definition: tv_play.cpp:7252
static const int kInitFFRWSpeed
Definition: tv_play.h:759
bool TranslateKeyPressOrGesture(const QString &Context, QEvent *Event, QStringList &Actions, bool IsLiveTV, bool AllowJumps=true)
Definition: tv_play.cpp:3306
volatile int m_networkControlTimerId
Definition: tv_play.h:693
QList< QKeyEvent * > m_screenPressKeyMapPlayback
Definition: tv_play.h:629
bool HandleOSDCutpoint(const QString &Action)
Definition: tv_play.cpp:7897
bool DoPlayerSeekToFrame(uint64_t FrameNum)
Definition: tv_play.cpp:4975
void ShowOSDAlreadyEditing()
Definition: tv_play.cpp:7935
static QList< QKeyEvent * > ConvertScreenPressKeyMap(const QString &KeyList)
Definition: tv_play.cpp:3241
void PopPreviousChannel(bool ImmediateChange)
Definition: tv_play.cpp:6275
PlayerContext m_playerContext
Definition: tv_play.h:648
void DoPlay()
Definition: tv_play.cpp:4796
static const std::chrono::milliseconds kSpeedChangeCheckFrequency
Definition: tv_play.h:774
static const std::chrono::milliseconds kEndOfRecPromptCheckFrequency
Definition: tv_play.h:776
void HandleStateChange()
Changes the state to the state on the front of the state change queue.
Definition: tv_play.cpp:2001
static void ToggleChannelFavorite()
Definition: tv_play.cpp:5776
static const std::chrono::milliseconds kErrorRecoveryCheckFrequency
Definition: tv_play.h:775
bool MenuItemDisplayPlayback(const MythTVMenuItemContext &Context, MythOSDDialogData *Menu)
Definition: tv_play.cpp:8726
int StartTimer(std::chrono::milliseconds Interval, int Line)
Definition: tv_play.cpp:2711
uint GetQueuedChanID() const
Definition: tv_play.h:351
bool m_underNetworkControl
initial show started via by the network control interface
Definition: tv_play.h:642
void SleepDialogTimeout()
Definition: tv_play.cpp:7192
std::chrono::milliseconds m_dbIdleTimeout
Definition: tv_play.h:533
bool m_doSmartForward
Definition: tv_play.h:569
QString m_dbChannelFormat
Definition: tv_play.h:532
MythPlayerUI * m_player
Definition: tv_play.h:651
bool m_zoomMode
Definition: tv_play.h:564
QMap< QString, ProgramList > m_progLists
Definition: tv_play.h:583
bool AudioSyncHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3726
void DoEditSchedule(int EditType=kScheduleProgramGuide, const QString &EditArg="")
Definition: tv_play.cpp:6893
bool MenuItemDisplay(const MythTVMenuItemContext &Context, MythOSDDialogData *Menu) override
Definition: tv_play.cpp:8582
int m_tvmCurrentChapter
Definition: tv_play.h:737
bool m_ignoreKeyPresses
should we ignore keypresses
Definition: tv_play.h:676
volatile int m_lcdVolumeTimerId
Definition: tv_play.h:692
void ShowOSDStopWatchingRecording()
Definition: tv_play.cpp:10244
static const std::chrono::milliseconds kSleepTimerDialogTimeout
Definition: tv_play.h:769
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:6414
uint m_queuedChanID
Queued ChanID (from EPG channel selector)
Definition: tv_play.h:619
int m_sleepDialogTimerId
Timer for sleep dialog.
Definition: tv_play.h:597
bool m_subtitleDelayAdjustment
True if subtitle delay is turned on.
Definition: tv_play.h:563
MythDeque< QString > m_networkControlCommands
Definition: tv_play.h:688
void RetrieveCast(const ProgramInfo &ProgInfo)
Definition: tv_play.cpp:9608
void HandleSaveLastPlayPosEvent()
Definition: tv_play.cpp:10438
MythTVMenu m_cutlistCompactMenu
Definition: tv_play.h:756
uint m_initialChanID
Initial chanid override for Live TV.
Definition: tv_play.h:621
int GetNumTitles()
Definition: tv_play.cpp:5433
void PlaybackLoop()
The main playback loop.
Definition: tv_play.cpp:1314
static EMBEDRETURNVOIDSCHEDIT RunScheduleEditorPtr
Definition: tv_play.h:207
bool m_sigMonMode
Are we in signal monitoring mode?
Definition: tv_play.h:565
int m_tvmNumTitles
Definition: tv_play.h:741
bool HasQueuedChannel() const
Definition: tv_play.h:345
void ClearInputQueues(bool Hideosd)
Clear channel key buffer of input keys.
Definition: tv_play.cpp:5823
static bool IsPaused()
Check whether playback is paused.
Definition: tv_play.cpp:4894
bool m_tvmIsRecorded
Definition: tv_play.h:724
bool m_savedPause
saved pause state before embedding
Definition: tv_play.h:677
bool GetAllowRerecord() const
Returns true if the user told Mythtv to allow re-recording of the show.
Definition: tv_play.h:319
QVector< string_pair > m_guests
Definition: tv_play.h:587
bool m_allowRerecord
User wants to rerecord the last video if deleted.
Definition: tv_play.h:568
ChannelGroupList m_dbChannelGroups
Definition: tv_play.h:547
QString GetRecordingGroup() const
Definition: tv_play.cpp:10477
void setUnderNetworkControl(bool setting)
Definition: tv_play.h:299
void HandleEndOfRecordingExitPromptTimerEvent()
Definition: tv_play.cpp:2874
static EMBEDRETURNVOIDEPG RunProgramGuidePtr
Definition: tv_play.h:205
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:322
QString GetAngleName(int Angle)
Definition: tv_play.cpp:5473
bool ProcessKeypressOrGesture(QEvent *Event)
Definition: tv_play.cpp:3319
void ShowOSDSleep()
Definition: tv_play.cpp:7152
uint m_vbimode
Definition: tv_play.h:554
float DoTogglePauseStart()
Definition: tv_play.cpp:4830
MThreadPool * GetPosThreadPool()
Definition: tv_play.cpp:10570
void DoSwitchAngle(int Angle)
Definition: tv_play.cpp:5520
QVariant m_tvmJumprecBackHack
Definition: tv_play.h:750
bool RequestNextRecorder(bool ShowDialogs, const ChannelInfoList &Selection=ChannelInfoList())
Definition: tv_play.cpp:1562
bool HandleJumpToProgramAction(const QStringList &Actions)
Definition: tv_play.cpp:9843
bool CommitQueuedInput()
Definition: tv_play.cpp:5955
void SetFFRew(int Index)
Definition: tv_play.cpp:5296
void FillOSDMenuJumpRec(const QString &Category="", int Level=0, const QString &Selected="")
Definition: tv_play.cpp:9495
static const std::chrono::milliseconds kVideoExitDialogTimeout
Definition: tv_play.h:771
bool m_tvmIsVideo
Definition: tv_play.h:725
MThreadPool * m_posThreadPool
Definition: tv_play.h:529
void SwitchInputs(uint ChanID=0, QString ChanNum="", uint InputID=0)
Definition: tv_play.cpp:5617
QMap< QString, AskProgramInfo > m_askAllowPrograms
Definition: tv_play.h:579
QString m_queuedChanNum
Input key presses queued up so far to form a valid ChanNum.
Definition: tv_play.h:617
bool DoPlayerSeek(float Time)
Definition: tv_play.cpp:4939
bool m_stretchAdjustment
True if time stretch is turned on.
Definition: tv_play.h:560
void FillOSDMenuActorShows(const QString &actor, int person_id, const QString &category="")
Definition: tv_play.cpp:9706
bool m_asInputMode
Are we in Arbitrary seek input mode?
Definition: tv_play.h:611
void ReloadKeys()
Definition: tv_play.cpp:971
bool StartPlayer(TVState desiredState)
Definition: tv_play.cpp:4773
void ToggleTimeStretch()
Definition: tv_play.cpp:7060
bool m_ffRewReverse
Definition: tv_play.h:551
int GetNumAngles()
Definition: tv_play.cpp:5453
PictureAdjustType m_adjustingPicture
Picture attribute type to modify.
Definition: tv_play.h:574
QString GetQueuedInput() const
Definition: tv_play.cpp:5789
static const uint kNextSource
Definition: tv_play.h:761
void PlaybackMenuDeinit(const MythTVMenu &Menu)
Definition: tv_play.cpp:9390
bool LiveTV(bool ShowDialogs, const ChannelInfoList &Selection)
Starts LiveTV.
Definition: tv_play.cpp:1534
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:562
volatile int m_videoExitDialogTimerId
Definition: tv_play.h:699
std::chrono::milliseconds m_sleepTimerTimeout
Current sleep timeout in msec.
Definition: tv_play.h:595
void ShowOSDPromptDeleteRecording(const QString &Title, bool Force=false)
Definition: tv_play.cpp:10287
bool m_tvmIsLiveTv
Definition: tv_play.h:732
void DoTogglePauseFinish(float Time, bool ShowOSD)
Definition: tv_play.cpp:4859
void DoJumpChapter(int Chapter)
Definition: tv_play.cpp:5418
void HandleEndOfPlaybackTimerEvent()
Definition: tv_play.cpp:2843
void UpdateOSDSignal(const QStringList &List)
Updates Signal portion of OSD...
Definition: tv_play.cpp:6453
bool MenuItemDisplayCutlist(const MythTVMenuItemContext &Context, MythOSDDialogData *Menu)
Definition: tv_play.cpp:8591
bool m_tvmFillAutoDetect
Definition: tv_play.h:718
bool m_dbUseGuiSizeForTv
Definition: tv_play.h:539
bool HandleOSDVideoExit(const QString &Action)
Definition: tv_play.cpp:10394
bool m_dbRememberLastChannelGroup
Definition: tv_play.h:546
static const std::vector< SleepTimerInfo > kSleepTimes
Definition: tv_play.h:593
void HandleOSDAskAllow(const QString &Action)
Definition: tv_play.cpp:1889
volatile int m_speedChangeTimerId
Definition: tv_play.h:701
bool m_ccInputMode
Are we in CC/Teletext page/stream selection mode?
Definition: tv_play.h:607
void UpdateOSDTimeoutMessage()
Definition: tv_play.cpp:6611
void ForceNextStateNone()
Definition: tv_play.cpp:2724
void PauseLiveTV()
Used in ChangeChannel() to temporarily stop video output.
Definition: tv_play.cpp:10015
static EMBEDRETURNVOID RunPlaybackBoxPtr
Definition: tv_play.h:203
float m_ffRewRepos
Definition: tv_play.h:550
void ShowOSDIdle()
After idleTimer has expired, display a dialogue warning the user that we will exit LiveTV unless they...
Definition: tv_play.cpp:7210
void GetPlayerReadLock() const
Definition: tv_play.cpp:10545
void ToggleSleepTimer()
Definition: tv_play.cpp:7126
MythTimer m_keyRepeatTimer
Queue of unprocessed key presses.
Definition: tv_play.h:603
void StartOsdNavigation()
Definition: tv_play.cpp:8018
static const std::chrono::milliseconds kEndOfPlaybackFirstCheckTimer
Definition: tv_play.h:781
static const uint kPreviousSource
Definition: tv_play.h:762
void HandleOSDAlreadyEditing(const QString &Action, bool WasPaused)
Definition: tv_play.cpp:7952
static EMBEDRETURNVOIDFINDER RunProgramFinderPtr
Definition: tv_play.h:206
static int GetActiveChannelGroupId()
Definition: tv_play.h:523
void GetStatus()
Definition: tv_play.cpp:1382
volatile int m_lcdTimerId
Definition: tv_play.h:691
volatile int m_channelGroupId
Definition: tv_play.h:684
int GetCurrentAngle()
Definition: tv_play.cpp:5463
bool m_tvmIsDvd
Definition: tv_play.h:729
~TV() override
Definition: tv_play.cpp:1234
QRect m_playerBounds
Prior GUI window bounds, for DoEditSchedule() and player exit().
Definition: tv_play.h:669
QReadWriteLock m_playerLock
lock on player and playerActive changes
Definition: tv_play.h:653
bool m_tvmIsPaused
Definition: tv_play.h:727
bool m_wantsToQuit
True if the user told MythTV to stop playback.
Definition: tv_play.h:559
QRecursiveMutex m_askAllowLock
Definition: tv_play.h:580
void PlaybackExiting(TV *Player)
void onApplicationStateChange(Qt::ApplicationState State)
Definition: tv_play.cpp:10555
bool DialogIsVisible(const QString &Dialog)
Definition: tv_play.cpp:8554
QVector< string_pair > m_guestStars
Definition: tv_play.h:586
void UpdateOSDProgInfo(const char *WhichInfo)
Update and display the passed OSD set with programinfo.
Definition: tv_play.cpp:6388
static const uint kInputKeysMax
Definition: tv_play.h:760
static bool StateIsLiveTV(TVState State)
Definition: tv_play.cpp:1973
bool StartRecorder(std::chrono::milliseconds MaxWait=-1ms)
Starts recorder, must be called before StartPlayer().
Definition: tv_play.cpp:2351
bool CreatePlayer(TVState State, bool Muted=false)
Definition: tv_play.cpp:201
void ToggleAutoExpire()
Definition: tv_play.cpp:9795
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:664
void MenuLazyInit(void *Field)
Definition: tv_play.cpp:9293
volatile int m_asInputTimerId
Definition: tv_play.h:695
bool event(QEvent *Event) override
This handles all standard events.
Definition: tv_play.cpp:3054
bool DoSetPauseState(bool Pause)
Definition: tv_play.cpp:6882
bool HandleTrackAction(const QString &Action)
Definition: tv_play.cpp:3121
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:628
void HandleLCDVolumeTimerEvent()
Definition: tv_play.cpp:2696
bool m_lockTimerOn
Definition: tv_play.h:634
static bool IsTunable(uint ChanId)
Definition: tv_play.cpp:6772
int GetCurrentTitle()
Definition: tv_play.cpp:5443
void HandleOSDSleep(const QString &Action)
Definition: tv_play.cpp:7171
int m_tvmSpeedX100
Definition: tv_play.h:721
void HandlePseudoLiveTVTimerEvent()
Definition: tv_play.cpp:2929
bool m_audiosyncAdjustment
True if audiosync is turned on.
Definition: tv_play.h:561
volatile int m_exitPlayerTimerId
Definition: tv_play.h:703
std::array< QStringList, kTrackTypeCount > m_tvmTracks
Definition: tv_play.h:711
bool m_tvmIsBd
Definition: tv_play.h:730
bool m_tvmSubsHaveSubs
Definition: tv_play.h:745
bool m_savePosOnExit
False until first timer event.
Definition: tv_play.h:571
void UpdateOSDInput()
Definition: tv_play.cpp:6442
int GetNumChapters()
Definition: tv_play.cpp:5390
QString GetQueuedChanNum() const
Definition: tv_play.cpp:5799
bool m_dbBrowseAllTuners
Definition: tv_play.h:544
bool IsDeleteAllowed()
Definition: tv_play.cpp:10229
bool ProcessSmartChannel(QString &InputStr)
Definition: tv_play.cpp:5885
void StartChannelEditMode()
Starts channel editing mode.
Definition: tv_play.cpp:7989
int GetCurrentChapter()
Definition: tv_play.cpp:5408
void ClearOSD()
Definition: tv_play.cpp:6306
static QStringList lastProgramStringList
Definition: tv_play.h:202
int m_tvmNumAngles
Definition: tv_play.h:739
void GetChapterTimes(QList< std::chrono::seconds > &Times)
Definition: tv_play.cpp:5400
bool TranslateGesture(const QString &Context, MythGestureEvent *Event, QStringList &Actions, bool IsLiveTV)
Definition: tv_play.cpp:3275
static bool StateIsPlaying(TVState State)
Definition: tv_play.cpp:1964
float StopFFRew()
Definition: tv_play.cpp:5236
void StartProgramEditMode()
Starts Program Cut Map Editing mode.
Definition: tv_play.cpp:7920
bool SubtitleDelayHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3772
int GetQueuedInputAsInt(bool *OK=nullptr, int Base=10) const
Definition: tv_play.cpp:5794
int m_dbPlaybackExitPrompt
Definition: tv_play.h:534
void InitFromDB()
Definition: tv_play.cpp:1040
static void MenuStrings()
Definition: tv_play.cpp:9428
bool BrowseHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3556
void SetErrored()
Definition: tv_play.cpp:2770
void StopPlayback()
Definition: tv_play.cpp:271
void DoSkipCommercials(int Direction)
Definition: tv_play.cpp:5536
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:7814
bool m_dbUseVideoModes
Definition: tv_play.h:540
bool ContextIsPaused(const char *File, int Location)
Definition: tv_play.cpp:10505
void ToggleOSD(bool IncludeStatusOSD)
Cycle through the available Info OSDs.
Definition: tv_play.cpp:6322
volatile int m_saveLastPlayPosTimerId
Definition: tv_play.h:704
volatile int m_ccInputTimerId
Definition: tv_play.h:694
bool HandleOSDChannelEdit(const QString &Action)
Processes channel editing key.
Definition: tv_play.cpp:8033
QMutex m_channelGroupLock
Lock necessary when modifying channel group variables.
Definition: tv_play.h:683
void UnpauseLiveTV(bool Quietly=false)
Used in ChangeChannel() to restart video output.
Definition: tv_play.cpp:10052
void ReturnPlayerLock() const
Definition: tv_play.cpp:10550
std::vector< int > m_ffRewSpeeds
Definition: tv_play.h:552
QMutex m_lastProgramLock
Definition: tv_play.h:639
TvPlayWindow * m_myWindow
Our screen, if it exists.
Definition: tv_play.h:667
void HandleOSDClosed(int OSDType)
Definition: tv_play.cpp:7763
bool eventFilter(QObject *Object, QEvent *Event) override
Prevent events from being sent to another object.
Definition: tv_play.cpp:3007
static bool StateIsRecording(TVState State)
Definition: tv_play.cpp:1959
void ShowOSDCutpoint(const QString &Type)
Definition: tv_play.cpp:7850
void DoArbSeek(ArbSeekWhence Whence, bool HonorCutlist)
Definition: tv_play.cpp:5139
void ReturnOSDLock() const
Definition: tv_play.cpp:10533
void SetBookmark(bool Clear=false)
Definition: tv_play.cpp:4177
QMutex m_progListsLock
Definition: tv_play.h:582
PictureAttribute NextPictureAdjustType(PictureAdjustType Type, PictureAttribute Attr)
Definition: tv_play.cpp:7792
bool m_weDisabledGUI
true if this instance disabled MythUI drawing.
Definition: tv_play.h:673
void UpdateOSDSeekMessage(const QString &Msg, enum OSDTimeout Timeout)
Definition: tv_play.cpp:6428
bool SeekHandleAction(const QStringList &Actions, bool IsDVD)
Definition: tv_play.cpp:5004
bool m_tvmSubsForcedOn
Definition: tv_play.h:744
bool m_tvmTranscoding
Definition: tv_play.h:748
volatile int m_errorRecoveryTimerId
Definition: tv_play.h:702
void PlaybackMenuShow(const MythTVMenu &Menu, const QDomNode &Node, const QDomNode &Selected)
Definition: tv_play.cpp:9395
void OSDDialogEvent(int Result, const QString &Text, QString Action)
Definition: tv_play.cpp:8116
void HandleVideoExitDialogTimerEvent()
Definition: tv_play.cpp:2904
void SetAutoCommercialSkip(CommSkipMode SkipMode=kCommSkipOff)
Definition: tv_play.cpp:9818
bool m_queuedTranscode
Definition: tv_play.h:570
static const std::chrono::milliseconds kIdleTimerDialogTimeout
Definition: tv_play.h:770
void HideOSDWindow(const char *window)
Definition: tv_play.cpp:6686
TVState GetState() const
Definition: tv_play.cpp:1373
void KillTimer(int Id)
Definition: tv_play.cpp:2719
static const std::chrono::milliseconds kSaveLastPlayPosTimeout
Definition: tv_play.h:777
bool m_dbEndOfRecExitPrompt
Definition: tv_play.h:537
void AskAllowRecording(const QStringList &Msg, int Timeuntil, bool HasRec, bool HasLater)
Definition: tv_play.cpp:1615
void ShowPreviousChannel()
Definition: tv_play.cpp:6266
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:208
void SetExitPlayer(bool SetIt, bool WantsTo)
Definition: tv_play.cpp:2826
std::array< int, kTrackTypeCount > m_tvmCurtrack
Definition: tv_play.h:712
bool m_tvmPreviousChan
Definition: tv_play.h:733
int m_tvmNumChapters
Definition: tv_play.h:736
bool m_dbContinueEmbedded
Definition: tv_play.h:542
void ChangeTimeStretch(int Dir, bool AllowEdit=true)
Definition: tv_play.cpp:7074
bool TimeStretchHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3701
static const std::chrono::milliseconds kEndOfPlaybackCheckFrequency
Definition: tv_play.h:772
static EMBEDRETURNVOID RunViewScheduledPtr
Definition: tv_play.h:204
MythTVMenu m_cutlistMenu
Definition: tv_play.h:755
bool m_dbJumpPreferOsd
Definition: tv_play.h:538
bool m_jumpToProgram
Definition: tv_play.h:645
bool FFRewHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:4078
bool m_dbRunJobsOnRemote
Definition: tv_play.h:541
QString m_lcdSubtitle
Definition: tv_play.h:663
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:8069
void SwitchSource(uint Direction)
Definition: tv_play.cpp:5559
int m_tvmCurrentTitle
Definition: tv_play.h:742
void SetSpeedChangeTimer(std::chrono::milliseconds When, int Line)
Definition: tv_play.cpp:2963
void ShowLCDDVDInfo()
Definition: tv_play.cpp:6728
void HandleOSDInfo(const QString &Action)
Definition: tv_play.cpp:8564
void AddKeyToInputQueue(char Key)
Definition: tv_play.cpp:5838
bool ActivePostQHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:4195
int m_tvmFreeRecorderCount
Definition: tv_play.h:728
QDateTime m_lastLockSeenTime
Definition: tv_play.h:635
void DoSeekAbsolute(long long Seconds, bool HonorCutlist)
Definition: tv_play.cpp:5125
bool ActiveHandleAction(const QStringList &Actions, bool IsDVD, bool IsDVDStillFrame)
Definition: tv_play.cpp:3811
static const std::chrono::milliseconds kInputModeTimeout
Definition: tv_play.h:764
void timerEvent(QTimerEvent *Event) override
Definition: tv_play.cpp:2436
MythMainWindow * m_mainWindow
Definition: tv_play.h:528
QString m_queuedInput
Input key presses queued up so far...
Definition: tv_play.h:615
ProgramInfo * m_lastProgram
last program played with this player
Definition: tv_play.h:640
static QVector< uint > IsTunableOn(PlayerContext *Context, uint ChanId)
Definition: tv_play.cpp:6805
void DoTogglePause(bool ShowOSD)
Definition: tv_play.cpp:4916
void NormalSpeed()
Definition: tv_play.cpp:5176
bool m_smartForward
Definition: tv_play.h:549
bool IsSameProgram(const ProgramInfo *ProgInfo) const
Definition: tv_play.cpp:10493
MythTVMenu m_playbackMenu
Definition: tv_play.h:753
void ScheduleStateChange()
Definition: tv_play.cpp:2730
volatile int m_signalMonitorTimerId
Definition: tv_play.h:705
void OverrideScan(FrameScanType Scan)
Definition: tv_play.cpp:9779
void DoSeekRWND()
Definition: tv_play.cpp:10124
void DoSwitchTitle(int Title)
Definition: tv_play.cpp:5504
void EditSchedule(int EditType=kScheduleProgramGuide, const QString &arg="")
Definition: tv_play.cpp:7020
int m_sleepTimerId
Timer for turning off playback.
Definition: tv_play.h:596
void ChangeChannel(const ChannelInfoList &Options)
Definition: tv_play.cpp:6245
bool DiscMenuHandleAction(const QStringList &Actions) const
Definition: tv_play.cpp:3795
bool IsTunablePriv(uint ChanId)
Definition: tv_play.cpp:6788
uint m_dbAutoexpireDefault
Definition: tv_play.h:535
uint m_sleepIndex
Index into sleep_times.
Definition: tv_play.h:594
RemoteEncoder * m_switchToRec
Main recorder to use after a successful SwitchCards() call.
Definition: tv_play.h:659
CommSkipMode m_tvmCurSkip
Definition: tv_play.h:726
void DoJumpFFWD()
Definition: tv_play.cpp:10099
void ChangeFFRew(int Direction)
Definition: tv_play.cpp:5261
void ITVRestart(bool IsLive)
Restart the MHEG/MHP engine.
Definition: tv_play.cpp:10080
bool m_tvmIsOn
Definition: tv_play.h:747
void SetManualZoom(bool ZoomON, const QString &Desc)
Definition: tv_play.cpp:9834
QElapsedTimer m_lockTimer
Definition: tv_play.h:633
bool ToggleHandleAction(const QStringList &Actions, bool IsDVD)
Definition: tv_play.cpp:4115
bool m_endOfRecording
!player->IsPlaying() && StateIsPlaying()
Definition: tv_play.h:566
QRect m_savedGuiBounds
Definition: tv_play.h:671
void SetLastProgram(const ProgramInfo *ProgInfo)
Definition: tv_play.cpp:10457
QRecursiveMutex m_chanEditMapLock
Lock for chanEditMap and ddMap.
Definition: tv_play.h:589
bool m_dbAutoSetWatched
Definition: tv_play.h:536
static const std::chrono::milliseconds kKeyRepeatTimeout
Definition: tv_play.h:767
void DoJumpRWND()
Definition: tv_play.cpp:10114
std::chrono::seconds GetTitleDuration(int Title)
Definition: tv_play.cpp:5483
void PrepareToExitPlayer(int Line)
Definition: tv_play.cpp:2787
void ShowNoRecorderDialog(NoRecorderMsg MsgType=kNoRecorders)
Definition: tv_play.cpp:9980
uint m_switchToInputId
Definition: tv_play.h:555
static void FillOSDMenuCastButton(MythOSDDialogData &dialog, const QVector< string_pair > &people)
Definition: tv_play.cpp:9675
bool HasQueuedInput() const
Definition: tv_play.h:344
bool PictureAttributeHandleAction(const QStringList &Actions)
Definition: tv_play.cpp:3672
bool m_dbBrowseAlways
Definition: tv_play.h:543
bool m_tvmAvsync
Definition: tv_play.h:715
bool m_tvmJump
Definition: tv_play.h:731
static void InitKeys()
Definition: tv_play.cpp:497
ProgramInfo * GetLastProgram() const
Definition: tv_play.cpp:10469
QVector< string_pair > m_actors
Definition: tv_play.h:585
int m_idleDialogTimerId
Timer for idle dialog.
Definition: tv_play.h:600
void Embed(bool Embed, QRect Rect={}, const QStringList &Data={})
Definition: tv_play.cpp:6849
void PrepToSwitchToRecordedProgram(const ProgramInfo &ProgInfo)
Definition: tv_play.cpp:2777
bool m_inPlaylist
show is part of a playlist
Definition: tv_play.h:641
bool m_tvmIsRecording
Definition: tv_play.h:723
bool StartPlaying(std::chrono::milliseconds MaxWait=-1ms)
Definition: tv_play.cpp:237
void HandleSpeedChangeTimerEvent()
Definition: tv_play.cpp:2970
void DVDJumpBack()
Definition: tv_play.cpp:10132
void ShowOSDAskAllow()
Definition: tv_play.cpp:1656
void SetInPlayList(bool InPlayList)
Definition: tv_play.h:298
int m_tvmCurrentAngle
Definition: tv_play.h:740
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:2401
volatile int m_pseudoChangeChanTimerId
Definition: tv_play.h:700
void FillOSDMenuCast(void)
Definition: tv_play.cpp:9694
bool m_dbUseChannelGroups
Definition: tv_play.h:545
void customEvent(QEvent *Event) override
This handles all custom events.
Definition: tv_play.cpp:7287
volatile int m_queueInputTimerId
Definition: tv_play.h:696
void ScheduleInputChange()
Definition: tv_play.cpp:2753
int m_idleTimerId
Timer for turning off playback after idle period.
Definition: tv_play.h:599
void DoSeek(float Time, const QString &Msg, bool TimeIsOffset, bool HonorCutlist)
Definition: tv_play.cpp:5088
static const std::chrono::milliseconds kLCDTimeout
Definition: tv_play.h:765
bool m_clearPosOnExit
False unless requested by user on playback exit.
Definition: tv_play.h:572
const MythTVMenu & getMenuFromId(MenuTypeId id)
Definition: tv_play.cpp:7270
void DoSeekFFWD()
Definition: tv_play.cpp:10109
TVState m_tvmState
Definition: tv_play.h:722
bool IsBookmarkAllowed()
Definition: tv_play.cpp:10203
void UpdateLCD()
Definition: tv_play.cpp:6694
void ChangeSpeed(int Direction)
Definition: tv_play.cpp:5191
void ShowOSDMenu(bool isCompact=false)
Definition: tv_play.cpp:9477
ChannelInfoList m_channelGroupChannelList
Definition: tv_play.h:685
void PlaybackMenuInit(const MythTVMenu &Menu)
Definition: tv_play.cpp:9300
bool CalcPlayerSliderPosition(osdInfo &info, bool paddedFields=false) const
Definition: tv_play.cpp:6673
ArbSeekWhence
Definition: tv_play.h:377
@ ARBSEEK_FORWARD
Definition: tv_play.h:377
@ ARBSEEK_END
Definition: tv_play.h:377
@ ARBSEEK_SET
Definition: tv_play.h:377
@ ARBSEEK_REWIND
Definition: tv_play.h:377
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:79
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:17
std::chrono::seconds secsInFuture(const QDateTime &future)
Definition: mythdate.cpp:217
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
MBASE_PUBLIC 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:44
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:5878
#define BUTTON3(action, textActive, textInactive, isMenu)
Definition: tv_play.cpp:8578
#define BUTTON2(action, textActive, textInactive)
Definition: tv_play.cpp:8576
static void insert_map(InfoMap &infoMap, const InfoMap &newMap)
Definition: tv_play.cpp:7980
#define SET_LAST()
Definition: tv_play.cpp:1982
#define SET_NEXT()
Definition: tv_play.cpp:1981
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:1985
static QString toCommaList(const QVector< uint > &list)
Definition: tv_play.cpp:6793
static uint get_chanid(const PlayerContext *ctx, uint cardid, const QString &channum)
Definition: tv_play.cpp:6081
#define BUTTON(action, text)
Definition: tv_play.cpp:8574
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:3225
#define TRANSITION(ASTATE, BSTATE)
Definition: tv_play.cpp:1979
void(*)(const ProgramInfo *, void *) EMBEDRETURNVOIDSCHEDIT
Definition: tv_play.h:64
void(*)(void *, bool) EMBEDRETURNVOID
Definition: tv_play.h:60
void(*)(uint, const QString &, const QDateTime, TV *, bool, bool, int) EMBEDRETURNVOIDEPG
Definition: tv_play.h:61
void(*)(TV *, int, const QString &) EMBEDRETURNVOIDPROGLIST
Definition: tv_play.h:63
@ kStartTVIgnoreLastPlayPos
Definition: tv_play.h:120
@ kStartTVIgnoreProgStart
Definition: tv_play.h:119
@ kStartTVByNetworkCommand
Definition: tv_play.h:117
@ kStartTVInPlayList
Definition: tv_play.h:116
@ kStartTVIgnoreBookmark
Definition: tv_play.h:118
@ kViewSchedule
Definition: tv_play.h:99
@ kScheduleProgramList
Definition: tv_play.h:101
@ kScheduleProgramGuide
Definition: tv_play.h:96
@ kScheduleProgramFinder
Definition: tv_play.h:97
@ kPlaybackBox
Definition: tv_play.h:100
@ kScheduledRecording
Definition: tv_play.h:98
void(*)(TV *, bool, bool) EMBEDRETURNVOIDFINDER
Definition: tv_play.h:62
NoRecorderMsg
Type of message displayed in ShowNoRecorderDialog()
Definition: tv_play.h:108
@ kNoRecorders
No free recorders.
Definition: tv_play.h:109
@ kNoCurrRec
No current recordings.
Definition: tv_play.h:110
@ kNoTuners
No capture cards configured.
Definition: tv_play.h:111
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