MythTV master
playercontext.cpp
Go to the documentation of this file.
1#include <cmath>
2#include <utility>
3
4#include <QPainter>
5
9
10#include "Bluray/mythbdplayer.h"
11#include "DVD/mythdvdplayer.h"
12#include "channelutil.h"
13#include "io/mythmediabuffer.h"
14#include "livetvchain.h"
15#include "metadataimagehelper.h"
16#include "mythplayer.h"
17#include "playercontext.h"
18#include "playgroup.h"
19#include "remoteencoder.h"
20#include "tv_play.h"
21#include "videometadatautil.h"
22#include "videoouttypes.h"
23
24#define LOC QString("playCtx: ")
25
27 m_recUsage(std::move(inUseID))
28{
31}
32
34{
36 m_nextState.clear();
37}
38
40{
41 m_ffRewState = 0;
42 m_ffRewIndex = 0;
43 m_ffRewSpeed = 0;
44 m_tsNormal = 1.0F;
45
46 SetPlayer(nullptr);
47 SetRecorder(nullptr);
48 SetRingBuffer(nullptr);
49 SetTVChain(nullptr);
50 SetPlayingInfo(nullptr);
51}
52
58{
59 TVState newState = kState_None;
60 QString newPlaygroup("Default");
61
62 LockPlayingInfo(__FILE__, __LINE__);
63 if (islivetv)
64 {
66 newState = kState_WatchingLiveTV;
67 }
68 else if (m_playingInfo)
69 {
70 int overrecordseconds = gCoreContext->GetNumSetting("RecordOverTime");
71 QDateTime curtime = MythDate::current();
72 QDateTime recendts = m_playingInfo->GetRecordingEndTime()
73 .addSecs(overrecordseconds);
74
76 {
77 newState = (curtime < recendts) ?
79 }
80 else if (m_playingInfo->IsVideoDVD())
81 {
82 newState = kState_WatchingDVD;
83 }
84 else if (m_playingInfo->IsVideoBD())
85 {
86 newState = kState_WatchingBD;
87 }
88 else
89 {
90 newState = kState_WatchingVideo;
91 }
92
93 newPlaygroup = m_playingInfo->GetPlaybackGroup();
94 }
95 UnlockPlayingInfo(__FILE__, __LINE__);
96
97 ChangeState(newState);
98 SetPlayGroup(newPlaygroup);
99}
100
102{
103 QMutexLocker locker(&m_deletePlayerLock);
104 return m_player;
105}
106
108{
109 QMutexLocker locker(&m_deletePlayerLock);
110 return m_player && m_player->IsErrored();
111}
112
114{
115 QMutexLocker locker(&m_deletePlayerLock);
116 return m_player && m_player->IsPlaying();
117}
118
120{
121 QMutexLocker locker(&m_deletePlayerLock);
123 {
124 m_ffRewSpeed = 0;
125 m_ffRewState = 0;
127 return true;
128 }
129 return false;
130}
131
133{
134 QMutexLocker locker(&m_deletePlayerLock);
137 {
138 // Speed got changed in player since we are close to the end of file
139 m_tsNormal = 1.0F;
140 return true;
141 }
142 return false;
143}
144
146{
148}
149
151{
152 if (m_player)
154}
155
156void PlayerContext::UpdateTVChain(const QStringList &data)
157{
158 QMutexLocker locker(&m_deletePlayerLock);
159 if (m_tvchain && m_player)
160 {
161 m_tvchain->ReloadAll(data);
163 }
164}
165
167{
168 if (!m_tvchain)
169 return false;
170
172 ProgramInfo *pinfo = m_tvchain->GetProgramAt(-1);
173 if (pinfo)
174 {
175 SetPlayingInfo(pinfo);
176 delete pinfo;
177 return true;
178 }
179 return false;
180}
181
186{
187 if (!m_tvchain)
188 return;
189
190 // Don't store more than kMaxChannelHistory channels. Remove the first item
191 if (m_prevChan.size() >= kMaxChannelHistory)
192 m_prevChan.pop_front();
193
194 // This method builds the stack of previous channels
195 QString curChan = m_tvchain->GetChannelName(-1);
196 if (m_prevChan.empty() ||
197 curChan != m_prevChan[m_prevChan.size() - 1])
198 {
199 const QString& chan = curChan;
200 m_prevChan.push_back(chan);
201 }
202}
203
205{
206 if (m_prevChan.empty())
207 return {};
208
209 QString curChan = m_tvchain->GetChannelName(-1);
210 if ((curChan == m_prevChan.back()) && !m_prevChan.empty())
211 m_prevChan.pop_back();
212
213 if (m_prevChan.empty())
214 return {};
215
216 QString chan = m_prevChan.back();
217 m_prevChan.pop_back();
218 // add the current channel back to the list, to allow easy flipping between
219 // two channels using PREVCHAN
221 return chan;
222}
223
225{
226 if (m_prevChan.empty())
227 return {};
228
229 QString curChan = m_tvchain->GetChannelName(-1);
230 QString preChan;
231 if (curChan != m_prevChan.back() || m_prevChan.size() < 2)
232 preChan = m_prevChan.back();
233 else
234 preChan = m_prevChan[m_prevChan.size()-2];
235 return preChan;
236}
237
238void PlayerContext::LockPlayingInfo([[maybe_unused]] const char *file,
239 [[maybe_unused]] int line) const
240{
241#if 0
242 LOG(VB_GENERAL, LOG_DEBUG, QString("LockPlayingInfo(%1,%2)")
243 .arg(file).arg(line));
244#endif
245 m_playingInfoLock.lock();
246}
247
248void PlayerContext::UnlockPlayingInfo([[maybe_unused]] const char *file,
249 [[maybe_unused]] int line) const
250{
251#if 0
252 LOG(VB_GENERAL, LOG_DEBUG, QString("UnlockPlayingInfo(%1,%2)")
253 .arg(file).arg(line));
254#endif
255 m_playingInfoLock.unlock();
256}
257
263void PlayerContext::LockDeletePlayer([[maybe_unused]] const char *file,
264 [[maybe_unused]] int line) const
265{
266#if 0
267 LOG(VB_GENERAL, LOG_DEBUG, QString("LockDeletePlayer(%1,%2)")
268 .arg(file).arg(line));
269#endif
270 m_deletePlayerLock.lock();
271}
272
276void PlayerContext::UnlockDeletePlayer([[maybe_unused]] const char *file,
277 [[maybe_unused]] int line) const
278{
279#if 0
280 LOG(VB_GENERAL, LOG_DEBUG, QString("UnlockDeletePlayer(%1,%2)")
281 .arg(file).arg(line));
282#endif
283 m_deletePlayerLock.unlock();
284}
285
287{
288 m_stateLock.lock();
289}
290
292{
293 m_stateLock.unlock();
294}
295
297{
298 if (!m_stateLock.tryLock())
299 return true;
300 bool inStateChange = !m_nextState.empty();
301 m_stateLock.unlock();
302 return inStateChange;
303}
304
309{
310 QMutexLocker locker(&m_stateLock);
311 m_nextState.enqueue(newState);
312}
313
315{
316 QMutexLocker locker(&m_stateLock);
317 return m_nextState.dequeue();
318}
319
324{
325 QMutexLocker locker(&m_stateLock);
326 m_nextState.clear();
327 m_nextState.push_back(kState_None);
328}
329
331{
332 QMutexLocker locker(&m_stateLock);
333 return m_playingState;
334}
335
337{
338 bool loaded = false;
339 LockPlayingInfo(__FILE__, __LINE__);
340 if (m_playingInfo)
341 {
342 m_playingInfo->ToMap(infoMap);
343 infoMap["tvstate"] = StateToString(m_playingState);
344 infoMap["iconpath"] = ChannelUtil::GetIcon(m_playingInfo->GetChanID());
348 {
349 infoMap["coverartpath"] = VideoMetaDataUtil::GetArtPath(
350 m_playingInfo->GetPathname(), "Coverart");
351 infoMap["fanartpath"] = VideoMetaDataUtil::GetArtPath(
352 m_playingInfo->GetPathname(), "Fanart");
353 infoMap["bannerpath"] = VideoMetaDataUtil::GetArtPath(
354 m_playingInfo->GetPathname(), "Banners");
355 infoMap["screenshotpath"] = VideoMetaDataUtil::GetArtPath(
356 m_playingInfo->GetPathname(), "Screenshots");
357 }
358 else
359 {
362 infoMap["coverartpath"] =
363 artmap.value(kArtworkCoverart).url;
364 infoMap["fanartpath"] =
365 artmap.value(kArtworkFanart).url;
366 infoMap["bannerpath"] =
367 artmap.value(kArtworkBanner).url;
368 infoMap["screenshotpath"] =
369 artmap.value(kArtworkScreenshot).url;
370 }
371 loaded = true;
372 }
373 UnlockPlayingInfo(__FILE__, __LINE__);
374 return loaded;
375}
376
378{
379 bool ret = false;
380 LockPlayingInfo(__FILE__, __LINE__);
381 if (m_playingInfo)
383 UnlockPlayingInfo(__FILE__, __LINE__);
384 return ret;
385}
386
387QString PlayerContext::GetFilters(const QString &baseFilters) const
388{
389 QString filters = baseFilters;
390 QString chanFilters;
391
393 return baseFilters;
394
395 LockPlayingInfo(__FILE__, __LINE__);
396 if (m_playingInfo) // Recordings have this info already.
398 UnlockPlayingInfo(__FILE__, __LINE__);
399
400 if (!chanFilters.isEmpty())
401 {
402 if ((chanFilters[0] != '+'))
403 {
404 filters = chanFilters;
405 }
406 else
407 {
408 if (!filters.isEmpty() && (!filters.endsWith(",")))
409 filters += ",";
410
411 filters += chanFilters.mid(1);
412 }
413 }
414
415 LOG(VB_CHANNEL, LOG_INFO, LOC +
416 QString("Output filters for this channel are: '%1'")
417 .arg(filters));
418
419 return filters;
420}
421
423{
424 QString mesg = QObject::tr("Play");
425 if (m_ffRewState < 0)
426 {
427 mesg = QObject::tr("Rewind");
428 mesg += QString(" %1X").arg(-m_ffRewSpeed);
429 }
430 else if (m_ffRewState > 0)
431 {
432 mesg = QObject::tr("Forward");
433 mesg += QString(" %1X").arg(m_ffRewSpeed);
434 }
435 // Make sure these values for m_ffRewSpeed in TV::ChangeSpeed()
436 // and PlayerContext::GetPlayMessage() stay in sync.
437 else if (m_ffRewSpeed == 0)
438 {
439 mesg += QString(" %1X").arg(m_tsNormal);
440 }
441 else if (m_ffRewSpeed == -1)
442 {
443 mesg += QString(" 1/3X");
444 }
445 else if (m_ffRewSpeed == -2)
446 {
447 mesg += QString(" 1/8X");
448 }
449 else if (m_ffRewSpeed == -3)
450 {
451 mesg += QString(" 1/16X");
452 }
453
454 return mesg;
455}
456
458{
459 QMutexLocker locker(&m_deletePlayerLock);
460 if (m_player)
461 {
462 StopPlaying();
463 delete m_player;
464 }
465 m_player = newplayer;
466}
467
469{
470 if (m_recorder)
471 {
472 delete m_recorder;
473 m_recorder = nullptr;
474 }
475
476 if (rec)
477 {
478 m_recorder = rec;
480 }
481}
482
484{
485 if (m_tvchain)
486 {
489 m_tvchain = nullptr;
490 }
491
492 m_tvchain = chain;
493
494 if (m_tvchain)
496}
497
499{
500 if (m_buffer)
501 {
502 delete m_buffer;
503 m_buffer = nullptr;
504 }
505
507}
508
513{
514 bool ignoreDB = gCoreContext->IsDatabaseIgnored();
515
516 QMutexLocker locker(&m_playingInfoLock);
517
518 if (m_playingInfo)
519 {
520 if (!ignoreDB)
522 delete m_playingInfo;
523 m_playingInfo = nullptr;
524 }
525
526 if (info)
527 {
529 if (!ignoreDB)
533 }
534}
535
536void PlayerContext::SetPlayGroup(const QString &group)
537{
538 m_fftime = PlayGroup::GetDurSetting<std::chrono::seconds>(group, "skipahead", 30s);
539 m_rewtime = PlayGroup::GetDurSetting<std::chrono::seconds>(group, "skipback", 5s);
540 m_jumptime = PlayGroup::GetDurSetting<std::chrono::minutes>(group, "jump", 10min);
541 m_tsNormal = PlayGroup::GetSetting(group, "timestretch", 100) * 0.01F;
542 m_tsAlt = (m_tsNormal == 1.0F) ? 1.5F : 1.0F;
543}
544
546 const ProgramInfo *pi, PseudoState new_state)
547{
549 ProgramInfo *new_rec = nullptr;
550
551 if (pi)
552 {
553 new_rec = new ProgramInfo(*pi);
554 QString msg = QString("Wants to record: %1 %2 %3 %4")
555 .arg(new_rec->GetTitle(), new_rec->GetChanNum(),
558 LOG(VB_PLAYBACK, LOG_INFO, LOC + msg);
559 }
560
561 m_pseudoLiveTVRec = new_rec;
562 m_pseudoLiveTVState = new_state;
563
564 if (old_rec)
565 {
566 QString msg = QString("Done recording: %1 %2 %3 %4")
567 .arg(old_rec->GetTitle(), old_rec->GetChanNum(),
570 LOG(VB_PLAYBACK, LOG_INFO, LOC + msg);
571 delete old_rec;
572 }
573}
static QString GetIcon(uint chanid)
Keeps track of recordings in a current LiveTV instance.
Definition: livetvchain.h:33
void DestroyChain(void)
QString GetChannelName(int pos=-1) const
void ReloadAll(const QStringList &data=QStringList())
ProgramInfo * GetProgramAt(int at) const
Returns program at the desired location.
QString InitializeNewChain(const QString &seed)
Definition: livetvchain.cpp:40
bool IsDatabaseIgnored(void) const
/brief Returns true if database is being ignored.
QString GetHostName(void)
int GetNumSetting(const QString &key, int defaultval=0)
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
float GetNextPlaySpeed(void) const
Definition: mythplayer.h:139
bool AtNormalSpeed(void) const
Definition: mythplayer.h:156
bool IsPlaying(std::chrono::milliseconds wait_in_msec=0ms, bool wait_for=true) const
Definition: mythplayer.cpp:248
bool IsErrored(void) const
virtual void StopPlaying(void)
Definition: mythplayer.cpp:944
void CheckTVChain()
Definition: mythplayer.cpp:930
void addMSecs(std::chrono::milliseconds ms)
Adds an offset to the last call to start() or restart().
Definition: mythtimer.cpp:146
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
static int GetSetting(const QString &name, const QString &field, int defval)
Definition: playgroup.cpp:278
QString m_recUsage
void SetRingBuffer(MythMediaBuffer *Buffer)
void SetPlayingInfo(const ProgramInfo *info)
assign programinfo to the context
QRecursiveMutex m_playingInfoLock
StringDeque m_prevChan
Previous channels.
QString GetPlayMessage(void) const
PlayerContext(QString inUseID=QString("Unknown"))
MythPlayer * m_player
void SetPseudoLiveTV(const ProgramInfo *pi, PseudoState new_state)
PseudoState m_pseudoLiveTVState
bool ReloadTVChain(void)
MythTimer m_lastSignalMsgTime
static constexpr uint kMaxChannelHistory
void LockState(void) const
void LockPlayingInfo(const char *file, int line) const
void SetPlayGroup(const QString &group)
TVState GetState(void) const
QRecursiveMutex m_deletePlayerLock
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
QDateTime m_playingRecStart
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.
QString GetFilters(const QString &baseFilters) const
int m_lastCardid
CardID of current/last recorder.
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
void SetPlayer(MythPlayer *newplayer)
int m_ffRewSpeed
Caches value of m_ffRewSpeeds[m_ffRewIndex].
void SetTVChain(LiveTVChain *chain)
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
QRecursiveMutex m_stateLock
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
bool IsSameProgram(const ProgramInfo &p) const
std::chrono::seconds m_playingLen
Initial CalculateLength()
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 IsVideoFile(void) const
Definition: programinfo.h:352
bool IsVideoDVD(void) const
Definition: programinfo.h:354
QString GetChannelPlaybackFilters(void) const
Definition: programinfo.h:395
QString GetInetRef(void) const
Definition: programinfo.h:448
bool IsSameProgram(const ProgramInfo &other) const
Checks whether this is the same program as "other", which may or may not be a repeat or on another ch...
bool IsRecording(void) const
Definition: programinfo.h:498
QString GetPlaybackGroup(void) const
Definition: programinfo.h:428
QString GetTitle(void) const
Definition: programinfo.h:368
void MarkAsInUse(bool inuse, const QString &usedFor="")
Tracks a recording's in use status, to prevent deletion and to allow the storage scheduler to perform...
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:412
QString GetChanNum(void) const
This is the channel "number", in the form 1, 1_2, 1-2, 1#1, etc.
Definition: programinfo.h:384
bool IsVideoBD(void) const
Definition: programinfo.h:356
std::chrono::seconds GetSecondsInRecording(void) const
Returns length of program/recording in seconds.
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
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:420
uint GetSeason(void) const
Definition: programinfo.h:373
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
int GetRecorderNumber(void) const
bool GetErrorStatus(void)
Definition: remoteencoder.h:81
static const int kInitFFRWSpeed
Definition: tv_play.h:755
static QString GetArtPath(const QString &pathname, const QString &type)
ArtworkMap GetArtwork(const QString &inetref, uint season, bool strict)
QMultiMap< VideoArtworkType, ArtworkInfo > ArtworkMap
@ kArtworkScreenshot
@ kArtworkFanart
@ kArtworkBanner
@ kArtworkCoverart
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
@ ISODate
Default UTC.
Definition: mythdate.h:17
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
dictionary info
Definition: azlyrics.py:7
#define LOC
PseudoState
Definition: playercontext.h:41
QString StateToString(TVState state)
Returns a human readable QString representing a TVState.
Definition: tv.cpp:11
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_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_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_WatchingVideo
Watching Video is the state when we are watching a video and is not a dvd or BD.
Definition: tv.h:74