[video] fix selection after changing video or extra art
[xbmc.git] / xbmc / pvr / PVRPlaybackState.cpp
blob34f0ff5b97d3ca0c0377246846fef3b6099a7574
1 /*
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.
7 */
9 #include "PVRPlaybackState.h"
11 #include "FileItem.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"
35 #include <algorithm>
36 #include <memory>
37 #include <mutex>
39 using namespace PVR;
41 class CPVRPlaybackState::CLastWatchedUpdateTimer : public CTimer, private ITimerCallback
43 public:
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); }
54 private:
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);
70 Clear();
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);
78 if (group)
79 m_playingChannel = group->GetByUniqueID({m_playingClientId, m_playingChannelUniqueId});
80 else
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);
93 if (epg)
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();
153 ClearData();
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;
174 else
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) *
185 1000;
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));
196 else
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);
224 if (client)
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());
260 bChanged = true;
261 m_playingChannel.reset();
262 ClearData();
264 else if (item.HasPVRRecordingInfoTag() &&
265 item.GetPVRRecordingInfoTag()->ClientID() == m_playingClientId &&
266 item.GetPVRRecordingInfoTag()->ClientRecordingID() == m_strPlayingRecordingUniqueId)
268 bChanged = true;
269 m_playingRecording.reset();
270 ClearData();
272 else if (item.HasEPGInfoTag() && item.GetEPGInfoTag()->ClientID() == m_playingClientId &&
273 item.GetEPGInfoTag()->UniqueChannelID() == m_playingEpgTagChannelUniqueId &&
274 item.GetEPGInfoTag()->UniqueBroadcastID() == m_playingEpgTagUniqueId)
276 bChanged = true;
277 m_playingEpgTag.reset();
278 ClearData();
280 else if (item.HasPVRChannelInfoTag())
282 CLog::LogFC(LOGERROR, LOGPVR, "Channel item without channel group member!");
285 return bChanged;
288 std::unique_ptr<CFileItem> CPVRPlaybackState::GetNextAutoplayItem(const CFileItem& item)
290 if (!item.GetProperty("epg_playlist_item").asBoolean(false))
291 return {};
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)};
303 if (epg && client)
305 const std::vector<std::shared_ptr<CPVREpgInfoTag>> tags{epg->GetTags()};
306 bool nextIsMatch{false};
307 for (const auto& tag : tags)
309 if (nextIsMatch)
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())
317 nextIsMatch = true;
320 if (!nextIsMatch)
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()};
325 if (group)
327 const std::shared_ptr<CPVRChannelGroupMember> groupMember{
328 group->GetByUniqueID({m_playingClientId, m_playingEpgTagChannelUniqueId})};
329 if (groupMember)
330 return std::make_unique<CFileItem>(groupMember);
335 return {};
338 bool CPVRPlaybackState::OnPlaybackEnded(const CFileItem& item)
340 // Playback ended, but not due to user interaction
342 std::unique_ptr<CFileItem> nextToPlay{GetNextAutoplayItem(item)};
343 if (nextToPlay)
344 StartPlayback(nextToPlay.release());
346 return OnPlaybackStopped(item);
349 void CPVRPlaybackState::StartPlayback(
350 CFileItem* item,
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);
355 if (client)
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);
383 if (props.size())
385 const std::string url = props.GetStreamURL();
386 if (!url.empty())
387 item->SetDynPath(url);
389 const std::string mime = props.GetStreamMimeType();
390 if (!mime.empty())
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
448 if (channel)
450 const std::shared_ptr<const CPVRChannel> current = GetPlayingChannel();
451 if (current && current->ClientID() == channel->ClientID() &&
452 current->UniqueID() == channel->UniqueID())
453 return true;
456 return false;
459 bool CPVRPlaybackState::IsPlayingRecording(
460 const std::shared_ptr<const CPVRRecording>& recording) const
462 if (recording)
464 const std::shared_ptr<const CPVRRecording> current = GetPlayingRecording();
465 if (current && current->ClientID() == recording->ClientID() &&
466 current->ClientRecordingID() == recording->ClientRecordingID())
467 return true;
470 return false;
473 bool CPVRPlaybackState::IsPlayingEpgTag(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
475 if (epgTag)
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())
481 return true;
484 return false;
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();
553 namespace
555 std::shared_ptr<CPVRChannelGroup> GetFirstNonDeletedAndNonHiddenChannelGroup(
556 const std::shared_ptr<CPVRChannelGroupMember>& groupMember)
558 CPVRChannelGroups* groups{
559 CServiceBroker::GetPVRManager().ChannelGroups()->Get(groupMember->IsRadio())};
560 if (groups)
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())
568 continue;
570 if (member->GetByUniqueID(groupMember->Channel()->StorageId()))
571 return member;
575 CLog::LogFC(LOGERROR, LOGPVR,
576 "Failed to obtain non-deleted and non-hidden group for channel '{}‘",
577 groupMember->Channel()->ChannelName());
578 return {};
580 } // unnamed namespace
582 void CPVRPlaybackState::SetActiveChannelGroup(const std::shared_ptr<CPVRChannelGroup>& group)
584 if (group)
586 if (group->IsHidden() || group->IsDeleted())
588 CLog::LogFC(LOGERROR, LOGPVR,
589 "Rejecting to make hidden or deleted group '{}‘ the active group.",
590 group->GroupName());
591 return;
594 if (group->IsRadio())
595 m_activeGroupRadio = group;
596 else
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
620 if (bRadio)
621 return m_activeGroupRadio;
622 else
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))
644 // start time valid?
645 time_t startTime = CServiceBroker::GetDataCacheCore().GetStartTime();
646 if (startTime > 0)
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)
657 time_t iTime;
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());
666 if (group)
667 group->SetLastWatched(iTime);
670 std::shared_ptr<CPVRChannelGroupMember> CPVRPlaybackState::GetLastPlayedChannelGroupMember(
671 bool bRadio) const
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;