2 * Copyright (C) 2012-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "PVRPlaybackState.h"
12 #include "ServiceBroker.h"
13 #include "XBDateTime.h"
14 #include "cores/DataCacheCore.h"
15 #include "messaging/ApplicationMessenger.h"
16 #include "pvr/PVRManager.h"
17 #include "pvr/PVRStreamProperties.h"
18 #include "pvr/addons/PVRClient.h"
19 #include "pvr/channels/PVRChannel.h"
20 #include "pvr/channels/PVRChannelGroup.h"
21 #include "pvr/channels/PVRChannelGroupMember.h"
22 #include "pvr/channels/PVRChannelGroups.h"
23 #include "pvr/channels/PVRChannelGroupsContainer.h"
24 #include "pvr/epg/Epg.h"
25 #include "pvr/epg/EpgContainer.h"
26 #include "pvr/epg/EpgInfoTag.h"
27 #include "pvr/recordings/PVRRecording.h"
28 #include "pvr/recordings/PVRRecordings.h"
29 #include "pvr/timers/PVRTimers.h"
30 #include "settings/Settings.h"
31 #include "settings/SettingsComponent.h"
32 #include "threads/Timer.h"
33 #include "utils/log.h"
41 class CPVRPlaybackState::CLastWatchedUpdateTimer
: public CTimer
, private ITimerCallback
44 CLastWatchedUpdateTimer(CPVRPlaybackState
& state
,
45 const std::shared_ptr
<CPVRChannelGroupMember
>& channel
,
46 const CDateTime
& time
)
47 : CTimer(this), m_state(state
), m_channel(channel
), m_time(time
)
51 // ITimerCallback implementation
52 void OnTimeout() override
{ m_state
.UpdateLastWatched(m_channel
, m_time
); }
55 CLastWatchedUpdateTimer() = delete;
57 CPVRPlaybackState
& m_state
;
58 const std::shared_ptr
<CPVRChannelGroupMember
> m_channel
;
59 const CDateTime m_time
;
62 CPVRPlaybackState::CPVRPlaybackState() = default;
64 CPVRPlaybackState::~CPVRPlaybackState() = default;
66 void CPVRPlaybackState::ReInit()
68 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
72 if (m_playingClientId
!= -1)
74 if (m_playingChannelUniqueId
!= -1)
76 const std::shared_ptr
<const CPVRChannelGroup
> group
=
77 CServiceBroker::GetPVRManager().ChannelGroups()->GetByIdFromAll(m_playingGroupId
);
79 m_playingChannel
= group
->GetByUniqueID({m_playingClientId
, m_playingChannelUniqueId
});
81 CLog::LogFC(LOGERROR
, LOGPVR
, "Failed to obtain group with id '{}'", m_playingGroupId
);
83 else if (!m_strPlayingRecordingUniqueId
.empty())
85 m_playingRecording
= CServiceBroker::GetPVRManager().Recordings()->GetById(
86 m_playingClientId
, m_strPlayingRecordingUniqueId
);
88 else if (m_playingEpgTagChannelUniqueId
!= -1 && m_playingEpgTagUniqueId
!= 0)
90 const std::shared_ptr
<const CPVREpg
> epg
=
91 CServiceBroker::GetPVRManager().EpgContainer().GetByChannelUid(
92 m_playingClientId
, m_playingEpgTagChannelUniqueId
);
94 m_playingEpgTag
= epg
->GetTagByBroadcastId(m_playingEpgTagUniqueId
);
98 const std::shared_ptr
<const CPVRChannelGroupsContainer
> groups
=
99 CServiceBroker::GetPVRManager().ChannelGroups();
100 const CPVRChannelGroups
* groupsTV
= groups
->GetTV();
101 const CPVRChannelGroups
* groupsRadio
= groups
->GetRadio();
103 m_activeGroupTV
= groupsTV
->GetLastOpenedGroup();
104 m_activeGroupRadio
= groupsRadio
->GetLastOpenedGroup();
105 if (!m_activeGroupTV
)
106 m_activeGroupTV
= groupsTV
->GetGroupAll();
107 if (!m_activeGroupRadio
)
108 m_activeGroupRadio
= groupsRadio
->GetGroupAll();
110 GroupMemberPair lastPlayed
= groupsTV
->GetLastAndPreviousToLastPlayedChannelGroupMember();
111 m_lastPlayedChannelTV
= lastPlayed
.first
;
112 m_previousToLastPlayedChannelTV
= lastPlayed
.second
;
114 lastPlayed
= groupsRadio
->GetLastAndPreviousToLastPlayedChannelGroupMember();
115 m_lastPlayedChannelRadio
= lastPlayed
.first
;
116 m_previousToLastPlayedChannelRadio
= lastPlayed
.second
;
119 void CPVRPlaybackState::ClearData()
121 m_playingGroupId
= -1;
122 m_playingChannelUniqueId
= -1;
123 m_strPlayingRecordingUniqueId
.clear();
124 m_playingEpgTagChannelUniqueId
= -1;
125 m_playingEpgTagUniqueId
= 0;
126 m_playingClientId
= -1;
127 m_strPlayingClientName
.clear();
130 void CPVRPlaybackState::Clear()
132 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
134 m_playingChannel
.reset();
135 m_playingRecording
.reset();
136 m_playingEpgTag
.reset();
137 m_lastPlayedChannelTV
.reset();
138 m_lastPlayedChannelRadio
.reset();
139 m_previousToLastPlayedChannelTV
.reset();
140 m_previousToLastPlayedChannelRadio
.reset();
141 m_lastWatchedUpdateTimer
.reset();
142 m_activeGroupTV
.reset();
143 m_activeGroupRadio
.reset();
146 void CPVRPlaybackState::OnPlaybackStarted(const CFileItem
& item
)
148 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
150 m_playingChannel
.reset();
151 m_playingRecording
.reset();
152 m_playingEpgTag
.reset();
155 if (item
.HasPVRChannelGroupMemberInfoTag())
157 const std::shared_ptr
<CPVRChannelGroupMember
> channel
= item
.GetPVRChannelGroupMemberInfoTag();
159 m_playingChannel
= channel
;
160 m_playingGroupId
= m_playingChannel
->GroupID();
161 m_playingClientId
= m_playingChannel
->Channel()->ClientID();
162 m_playingChannelUniqueId
= m_playingChannel
->Channel()->UniqueID();
164 SetActiveChannelGroup(channel
);
166 if (channel
->Channel()->IsRadio())
168 if (m_lastPlayedChannelRadio
!= channel
)
170 m_previousToLastPlayedChannelRadio
= m_lastPlayedChannelRadio
;
171 m_lastPlayedChannelRadio
= channel
;
176 if (m_lastPlayedChannelTV
!= channel
)
178 m_previousToLastPlayedChannelTV
= m_lastPlayedChannelTV
;
179 m_lastPlayedChannelTV
= channel
;
183 int iLastWatchedDelay
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
184 CSettings::SETTING_PVRPLAYBACK_DELAYMARKLASTWATCHED
) *
186 if (iLastWatchedDelay
> 0)
188 // Insert new / replace existing last watched update timer
189 if (m_lastWatchedUpdateTimer
)
190 m_lastWatchedUpdateTimer
->Stop(true);
192 m_lastWatchedUpdateTimer
=
193 std::make_unique
<CLastWatchedUpdateTimer
>(*this, channel
, CDateTime::GetUTCDateTime());
194 m_lastWatchedUpdateTimer
->Start(std::chrono::milliseconds(iLastWatchedDelay
));
198 // Store last watched timestamp immediately
199 UpdateLastWatched(channel
, CDateTime::GetUTCDateTime());
202 else if (item
.HasPVRRecordingInfoTag())
204 m_playingRecording
= item
.GetPVRRecordingInfoTag();
205 m_playingClientId
= m_playingRecording
->ClientID();
206 m_strPlayingRecordingUniqueId
= m_playingRecording
->ClientRecordingID();
208 else if (item
.HasEPGInfoTag())
210 m_playingEpgTag
= item
.GetEPGInfoTag();
211 m_playingClientId
= m_playingEpgTag
->ClientID();
212 m_playingEpgTagChannelUniqueId
= m_playingEpgTag
->UniqueChannelID();
213 m_playingEpgTagUniqueId
= m_playingEpgTag
->UniqueBroadcastID();
215 else if (item
.HasPVRChannelInfoTag())
217 CLog::LogFC(LOGERROR
, LOGPVR
, "Channel item without channel group member!");
220 if (m_playingClientId
!= -1)
222 const std::shared_ptr
<const CPVRClient
> client
=
223 CServiceBroker::GetPVRManager().GetClient(m_playingClientId
);
225 m_strPlayingClientName
= client
->GetFriendlyName();
229 bool CPVRPlaybackState::OnPlaybackStopped(const CFileItem
& item
)
231 // Playback ended due to user interaction
233 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
235 bool bChanged
= false;
237 if (item
.HasPVRChannelGroupMemberInfoTag() &&
238 item
.GetPVRChannelGroupMemberInfoTag()->Channel()->ClientID() == m_playingClientId
&&
239 item
.GetPVRChannelGroupMemberInfoTag()->Channel()->UniqueID() == m_playingChannelUniqueId
)
241 bool bUpdateLastWatched
= true;
243 if (m_lastWatchedUpdateTimer
)
245 if (m_lastWatchedUpdateTimer
->IsRunning())
247 // If last watched timer is still running, cancel it. Channel was not watched long enough to store the value.
248 m_lastWatchedUpdateTimer
->Stop(true);
249 bUpdateLastWatched
= false;
251 m_lastWatchedUpdateTimer
.reset();
254 if (bUpdateLastWatched
)
256 // If last watched timer is not running (any more), channel was watched long enough to store the value.
257 UpdateLastWatched(m_playingChannel
, CDateTime::GetUTCDateTime());
261 m_playingChannel
.reset();
264 else if (item
.HasPVRRecordingInfoTag() &&
265 item
.GetPVRRecordingInfoTag()->ClientID() == m_playingClientId
&&
266 item
.GetPVRRecordingInfoTag()->ClientRecordingID() == m_strPlayingRecordingUniqueId
)
269 m_playingRecording
.reset();
272 else if (item
.HasEPGInfoTag() && item
.GetEPGInfoTag()->ClientID() == m_playingClientId
&&
273 item
.GetEPGInfoTag()->UniqueChannelID() == m_playingEpgTagChannelUniqueId
&&
274 item
.GetEPGInfoTag()->UniqueBroadcastID() == m_playingEpgTagUniqueId
)
277 m_playingEpgTag
.reset();
280 else if (item
.HasPVRChannelInfoTag())
282 CLog::LogFC(LOGERROR
, LOGPVR
, "Channel item without channel group member!");
288 std::unique_ptr
<CFileItem
> CPVRPlaybackState::GetNextAutoplayItem(const CFileItem
& item
)
290 if (!item
.GetProperty("epg_playlist_item").asBoolean(false))
293 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
295 if (item
.HasEPGInfoTag() && item
.GetEPGInfoTag()->ClientID() == m_playingClientId
&&
296 item
.GetEPGInfoTag()->UniqueChannelID() == m_playingEpgTagChannelUniqueId
&&
297 item
.GetEPGInfoTag()->UniqueBroadcastID() == m_playingEpgTagUniqueId
)
299 auto& pvrMgr
{CServiceBroker::GetPVRManager()};
300 const std::shared_ptr
<const CPVREpg
> epg
{
301 pvrMgr
.EpgContainer().GetByChannelUid(m_playingClientId
, m_playingEpgTagChannelUniqueId
)};
302 const std::shared_ptr
<const CPVRClient
> client
{pvrMgr
.GetClient(m_playingClientId
)};
305 const std::vector
<std::shared_ptr
<CPVREpgInfoTag
>> tags
{epg
->GetTags()};
306 bool nextIsMatch
{false};
307 for (const auto& tag
: tags
)
311 // Next to play is successor of given item in channel's timeline.
312 return std::make_unique
<CFileItem
>(tag
);
315 if (tag
!= tags
.back() && tag
->StartAsUTC() == m_playingEpgTag
->StartAsUTC() &&
316 tag
->EndAsUTC() == m_playingEpgTag
->EndAsUTC())
322 // No more non-live epg items in channel's timeline. Next to play is the live channel.
323 const std::shared_ptr
<CPVRChannelGroup
> group
{
324 pvrMgr
.ChannelGroups()->Get(m_playingEpgTag
->IsRadio())->GetGroupAll()};
327 const std::shared_ptr
<CPVRChannelGroupMember
> groupMember
{
328 group
->GetByUniqueID({m_playingClientId
, m_playingEpgTagChannelUniqueId
})};
330 return std::make_unique
<CFileItem
>(groupMember
);
338 bool CPVRPlaybackState::OnPlaybackEnded(const CFileItem
& item
)
340 // Playback ended, but not due to user interaction
342 std::unique_ptr
<CFileItem
> nextToPlay
{GetNextAutoplayItem(item
)};
344 StartPlayback(nextToPlay
.release());
346 return OnPlaybackStopped(item
);
349 void CPVRPlaybackState::StartPlayback(
351 ContentUtils::PlayMode mode
/* = ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM */) const
353 // Obtain dynamic playback url and properties from the respective pvr client
354 const std::shared_ptr
<const CPVRClient
> client
= CServiceBroker::GetPVRManager().GetClient(*item
);
357 CPVRStreamProperties props
;
359 if (item
->IsPVRChannel())
361 client
->GetChannelStreamProperties(item
->GetPVRChannelInfoTag(), props
);
363 else if (item
->IsPVRRecording())
365 client
->GetRecordingStreamProperties(item
->GetPVRRecordingInfoTag(), props
);
367 else if (item
->IsEPG())
369 client
->GetEpgTagStreamProperties(item
->GetEPGInfoTag(), props
);
371 if (mode
== ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM
)
373 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
374 CSettings::SETTING_PVRPLAYBACK_AUTOPLAYNEXTPROGRAMME
))
375 item
->SetProperty("epg_playlist_item", true);
377 else if (mode
== ContentUtils::PlayMode::PLAY_FROM_HERE
)
379 item
->SetProperty("epg_playlist_item", true);
385 const std::string url
= props
.GetStreamURL();
387 item
->SetDynPath(url
);
389 const std::string mime
= props
.GetStreamMimeType();
392 item
->SetMimeType(mime
);
393 item
->SetContentLookup(false);
396 for (const auto& prop
: props
)
397 item
->SetProperty(prop
.first
, prop
.second
);
401 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MEDIA_PLAY
, 0, 0, static_cast<void*>(item
));
404 bool CPVRPlaybackState::IsPlaying() const
406 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
407 return m_playingChannel
!= nullptr || m_playingRecording
!= nullptr || m_playingEpgTag
!= nullptr;
410 bool CPVRPlaybackState::IsPlayingTV() const
412 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
413 return m_playingChannel
&& !m_playingChannel
->Channel()->IsRadio();
416 bool CPVRPlaybackState::IsPlayingRadio() const
418 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
419 return m_playingChannel
&& m_playingChannel
->Channel()->IsRadio();
422 bool CPVRPlaybackState::IsPlayingEncryptedChannel() const
424 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
425 return m_playingChannel
&& m_playingChannel
->Channel()->IsEncrypted();
428 bool CPVRPlaybackState::IsPlayingRecording() const
430 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
431 return m_playingRecording
!= nullptr;
434 bool CPVRPlaybackState::IsPlayingEpgTag() const
436 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
437 return m_playingEpgTag
!= nullptr;
440 bool CPVRPlaybackState::IsPlayingChannel(int iClientID
, int iUniqueChannelID
) const
442 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
443 return m_playingClientId
== iClientID
&& m_playingChannelUniqueId
== iUniqueChannelID
;
446 bool CPVRPlaybackState::IsPlayingChannel(const std::shared_ptr
<const CPVRChannel
>& channel
) const
450 const std::shared_ptr
<const CPVRChannel
> current
= GetPlayingChannel();
451 if (current
&& current
->ClientID() == channel
->ClientID() &&
452 current
->UniqueID() == channel
->UniqueID())
459 bool CPVRPlaybackState::IsPlayingRecording(
460 const std::shared_ptr
<const CPVRRecording
>& recording
) const
464 const std::shared_ptr
<const CPVRRecording
> current
= GetPlayingRecording();
465 if (current
&& current
->ClientID() == recording
->ClientID() &&
466 current
->ClientRecordingID() == recording
->ClientRecordingID())
473 bool CPVRPlaybackState::IsPlayingEpgTag(const std::shared_ptr
<const CPVREpgInfoTag
>& epgTag
) const
477 const std::shared_ptr
<const CPVREpgInfoTag
> current
= GetPlayingEpgTag();
478 if (current
&& current
->ClientID() == epgTag
->ClientID() &&
479 current
->UniqueChannelID() == epgTag
->UniqueChannelID() &&
480 current
->UniqueBroadcastID() == epgTag
->UniqueBroadcastID())
487 std::shared_ptr
<CPVRChannel
> CPVRPlaybackState::GetPlayingChannel() const
489 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
490 return m_playingChannel
? m_playingChannel
->Channel() : std::shared_ptr
<CPVRChannel
>();
493 std::shared_ptr
<CPVRChannelGroupMember
> CPVRPlaybackState::GetPlayingChannelGroupMember() const
495 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
496 return m_playingChannel
;
499 std::shared_ptr
<CPVRRecording
> CPVRPlaybackState::GetPlayingRecording() const
501 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
502 return m_playingRecording
;
505 std::shared_ptr
<CPVREpgInfoTag
> CPVRPlaybackState::GetPlayingEpgTag() const
507 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
508 return m_playingEpgTag
;
511 int CPVRPlaybackState::GetPlayingChannelUniqueID() const
513 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
514 return m_playingChannelUniqueId
;
517 std::string
CPVRPlaybackState::GetPlayingClientName() const
519 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
520 return m_strPlayingClientName
;
523 int CPVRPlaybackState::GetPlayingClientID() const
525 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
526 return m_playingClientId
;
529 bool CPVRPlaybackState::IsRecording() const
531 return CServiceBroker::GetPVRManager().Timers()->IsRecording();
534 bool CPVRPlaybackState::IsRecordingOnPlayingChannel() const
536 const std::shared_ptr
<const CPVRChannel
> currentChannel
= GetPlayingChannel();
537 return currentChannel
&&
538 CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*currentChannel
);
541 bool CPVRPlaybackState::IsPlayingActiveRecording() const
543 const std::shared_ptr
<const CPVRRecording
> currentRecording
= GetPlayingRecording();
544 return currentRecording
&& currentRecording
->IsInProgress();
547 bool CPVRPlaybackState::CanRecordOnPlayingChannel() const
549 const std::shared_ptr
<const CPVRChannel
> currentChannel
= GetPlayingChannel();
550 return currentChannel
&& currentChannel
->CanRecord();
555 std::shared_ptr
<CPVRChannelGroup
> GetFirstNonDeletedAndNonHiddenChannelGroup(
556 const std::shared_ptr
<CPVRChannelGroupMember
>& groupMember
)
558 CPVRChannelGroups
* groups
{
559 CServiceBroker::GetPVRManager().ChannelGroups()->Get(groupMember
->IsRadio())};
562 const std::vector
<std::shared_ptr
<CPVRChannelGroup
>> members
{
563 groups
->GetMembers(true /* exclude hidden */)};
565 for (const auto& member
: members
)
567 if (member
->IsDeleted())
570 if (member
->GetByUniqueID(groupMember
->Channel()->StorageId()))
575 CLog::LogFC(LOGERROR
, LOGPVR
,
576 "Failed to obtain non-deleted and non-hidden group for channel '{}‘",
577 groupMember
->Channel()->ChannelName());
580 } // unnamed namespace
582 void CPVRPlaybackState::SetActiveChannelGroup(const std::shared_ptr
<CPVRChannelGroup
>& group
)
586 if (group
->IsHidden() || group
->IsDeleted())
588 CLog::LogFC(LOGERROR
, LOGPVR
,
589 "Rejecting to make hidden or deleted group '{}‘ the active group.",
594 if (group
->IsRadio())
595 m_activeGroupRadio
= group
;
597 m_activeGroupTV
= group
;
599 auto duration
= std::chrono::system_clock::now().time_since_epoch();
600 uint64_t tsMillis
= std::chrono::duration_cast
<std::chrono::milliseconds
>(duration
).count();
601 group
->SetLastOpened(tsMillis
);
605 void CPVRPlaybackState::SetActiveChannelGroup(
606 const std::shared_ptr
<CPVRChannelGroupMember
>& channel
)
608 const bool bRadio
= channel
->Channel()->IsRadio();
609 std::shared_ptr
<CPVRChannelGroup
> group
{
610 CServiceBroker::GetPVRManager().ChannelGroups()->Get(bRadio
)->GetById(channel
->GroupID())};
612 if (group
&& (group
->IsHidden() || group
->IsDeleted()))
613 group
= GetFirstNonDeletedAndNonHiddenChannelGroup(channel
);
615 SetActiveChannelGroup(group
);
618 std::shared_ptr
<CPVRChannelGroup
> CPVRPlaybackState::GetActiveChannelGroup(bool bRadio
) const
621 return m_activeGroupRadio
;
623 return m_activeGroupTV
;
626 CDateTime
CPVRPlaybackState::GetPlaybackTime(int iClientID
, int iUniqueChannelID
) const
628 const std::shared_ptr
<const CPVREpgInfoTag
> epgTag
= GetPlayingEpgTag();
629 if (epgTag
&& iClientID
== epgTag
->ClientID() && iUniqueChannelID
== epgTag
->UniqueChannelID())
631 // playing an epg tag on requested channel
632 return epgTag
->StartAsUTC() +
633 CDateTimeSpan(0, 0, 0, CServiceBroker::GetDataCacheCore().GetPlayTime() / 1000);
636 // not playing / playing live / playing timeshifted
637 return GetChannelPlaybackTime(iClientID
, iUniqueChannelID
);
640 CDateTime
CPVRPlaybackState::GetChannelPlaybackTime(int iClientID
, int iUniqueChannelID
) const
642 if (IsPlayingChannel(iClientID
, iUniqueChannelID
))
645 time_t startTime
= CServiceBroker::GetDataCacheCore().GetStartTime();
647 return CDateTime(startTime
+ CServiceBroker::GetDataCacheCore().GetPlayTime() / 1000);
650 // not playing / playing live
651 return CDateTime::GetUTCDateTime();
654 void CPVRPlaybackState::UpdateLastWatched(const std::shared_ptr
<CPVRChannelGroupMember
>& channel
,
655 const CDateTime
& time
)
658 time
.GetAsTime(iTime
);
660 channel
->Channel()->SetLastWatched(iTime
, channel
->GroupID());
662 // update last watched timestamp for group
663 const bool bRadio
= channel
->Channel()->IsRadio();
664 const std::shared_ptr
<CPVRChannelGroup
> group
=
665 CServiceBroker::GetPVRManager().ChannelGroups()->Get(bRadio
)->GetById(channel
->GroupID());
667 group
->SetLastWatched(iTime
);
670 std::shared_ptr
<CPVRChannelGroupMember
> CPVRPlaybackState::GetLastPlayedChannelGroupMember(
673 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
674 return bRadio
? m_lastPlayedChannelRadio
: m_lastPlayedChannelTV
;
677 std::shared_ptr
<CPVRChannelGroupMember
> CPVRPlaybackState::
678 GetPreviousToLastPlayedChannelGroupMember(bool bRadio
) const
680 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
681 return bRadio
? m_previousToLastPlayedChannelRadio
: m_previousToLastPlayedChannelTV
;