Merge pull request #26076 from notspiff/seekhandler_seektype_enum_class
[xbmc.git] / xbmc / pvr / addons / PVRClient.cpp
blob05f87afbe108ccc6e38428ce26c0e54a8c9dbdc4
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 "PVRClient.h"
11 #include "ServiceBroker.h"
12 #include "addons/AddonManager.h"
13 #include "addons/binary-addons/AddonDll.h"
14 #include "cores/EdlEdit.h"
15 #include "cores/VideoPlayer/DVDDemuxers/DVDDemuxUtils.h"
16 #include "dialogs/GUIDialogKaiToast.h" //! @todo get rid of GUI in core
17 #include "events/EventLog.h"
18 #include "events/NotificationEvent.h"
19 #include "filesystem/SpecialProtocol.h"
20 #include "guilib/LocalizeStrings.h"
21 #include "pvr/PVRConstants.h" // PVR_CLIENT_INVALID_UID
22 #include "pvr/PVRDatabase.h"
23 #include "pvr/PVRDescrambleInfo.h"
24 #include "pvr/PVRManager.h"
25 #include "pvr/PVRSignalStatus.h"
26 #include "pvr/PVRStreamProperties.h"
27 #include "pvr/addons/PVRClientMenuHooks.h"
28 #include "pvr/addons/PVRClients.h"
29 #include "pvr/channels/PVRChannel.h"
30 #include "pvr/channels/PVRChannelGroup.h"
31 #include "pvr/channels/PVRChannelGroupFactory.h"
32 #include "pvr/channels/PVRChannelGroupMember.h"
33 #include "pvr/channels/PVRChannelGroups.h"
34 #include "pvr/channels/PVRChannelGroupsContainer.h"
35 #include "pvr/epg/Epg.h"
36 #include "pvr/epg/EpgContainer.h"
37 #include "pvr/epg/EpgInfoTag.h"
38 #include "pvr/providers/PVRProvider.h"
39 #include "pvr/providers/PVRProviders.h"
40 #include "pvr/recordings/PVRRecording.h"
41 #include "pvr/recordings/PVRRecordings.h"
42 #include "pvr/timers/PVRTimerInfoTag.h"
43 #include "pvr/timers/PVRTimerType.h"
44 #include "pvr/timers/PVRTimers.h"
45 #include "settings/AdvancedSettings.h"
46 #include "settings/SettingsComponent.h"
47 #include "utils/StringUtils.h"
48 #include "utils/log.h"
50 #include <chrono>
51 #include <map>
52 #include <memory>
53 #include <mutex>
54 #include <string>
55 #include <utility>
57 extern "C"
59 #include <libavcodec/avcodec.h>
62 using namespace ADDON;
63 using namespace PVR;
65 namespace
67 class CAddonChannelGroup : public PVR_CHANNEL_GROUP
69 public:
70 explicit CAddonChannelGroup(const CPVRChannelGroup& group) : m_groupName(group.ClientGroupName())
72 // zero-init base struct members
73 PVR_CHANNEL_GROUP* base = static_cast<PVR_CHANNEL_GROUP*>(this);
74 *base = {};
76 bIsRadio = group.IsRadio();
77 strGroupName = m_groupName.c_str();
78 iPosition = group.GetClientPosition();
80 virtual ~CAddonChannelGroup() = default;
82 private:
83 const std::string m_groupName;
86 class CAddonChannel : public PVR_CHANNEL
88 public:
89 explicit CAddonChannel(const CPVRChannel& channel, const std::string& newChannelName = "")
90 : m_channelName(newChannelName.empty() ? channel.ClientChannelName() : newChannelName),
91 m_mimeType(channel.MimeType()),
92 m_iconPath(channel.ClientIconPath())
94 // zero-init base struct members
95 PVR_CHANNEL* base = static_cast<PVR_CHANNEL*>(this);
96 *base = {};
98 iUniqueId = channel.UniqueID();
99 iChannelNumber = channel.ClientChannelNumber().GetChannelNumber();
100 iSubChannelNumber = channel.ClientChannelNumber().GetSubChannelNumber();
101 strChannelName = m_channelName.c_str();
102 strIconPath = m_iconPath.c_str();
103 iEncryptionSystem = channel.EncryptionSystem();
104 bIsRadio = channel.IsRadio();
105 bIsHidden = channel.IsHidden();
106 strMimeType = m_mimeType.c_str();
107 iClientProviderUid = channel.ClientProviderUid();
108 bHasArchive = channel.HasArchive();
110 virtual ~CAddonChannel() = default;
112 private:
113 const std::string m_channelName;
114 const std::string m_mimeType;
115 const std::string m_iconPath;
118 class CAddonRecording : public PVR_RECORDING
120 public:
121 explicit CAddonRecording(const CPVRRecording& recording)
122 : m_recordingId(recording.ClientRecordingID()),
123 m_title(recording.m_strTitle),
124 m_titleExtraInfo(recording.TitleExtraInfo()),
125 m_episodeName(recording.m_strShowTitle),
126 m_directory(recording.Directory()),
127 m_plotOutline(recording.m_strPlotOutline),
128 m_plot(recording.m_strPlot),
129 m_genreDescription(recording.GetGenresLabel()),
130 m_channelName(recording.ChannelName()),
131 m_iconPath(recording.ClientIconPath()),
132 m_thumbnailPath(recording.ClientThumbnailPath()),
133 m_fanartPath(recording.ClientFanartPath()),
134 m_firstAired(recording.FirstAired().IsValid() ? recording.FirstAired().GetAsW3CDate() : ""),
135 m_providerName(recording.ProviderName()),
136 m_parentalRatingCode(recording.GetParentalRatingCode()),
137 m_parentalRatingIcon(recording.ClientParentalRatingIconPath()),
138 m_parentalRatingSource(recording.GetParentalRatingSource())
140 // zero-init base struct members
141 PVR_RECORDING* base = static_cast<PVR_RECORDING*>(this);
142 *base = {};
144 time_t recTime;
145 recording.RecordingTimeAsUTC().GetAsTime(recTime);
147 strRecordingId = m_recordingId.c_str();
148 strTitle = m_title.c_str();
149 strTitleExtraInfo = m_titleExtraInfo.c_str();
150 strEpisodeName = m_episodeName.c_str();
151 iSeriesNumber = recording.m_iSeason;
152 iEpisodeNumber = recording.m_iEpisode;
153 iEpisodePartNumber = recording.EpisodePart();
154 iYear = recording.GetYear();
155 strDirectory = m_directory.c_str();
156 strPlotOutline = m_plotOutline.c_str();
157 strPlot = m_plot.c_str();
158 strGenreDescription = m_genreDescription.c_str();
159 strChannelName = m_channelName.c_str();
160 strIconPath = m_iconPath.c_str();
161 strThumbnailPath = m_thumbnailPath.c_str();
162 strFanartPath = m_fanartPath.c_str();
163 recordingTime =
164 recTime -
165 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection;
166 iDuration = recording.GetDuration();
167 iPriority = recording.Priority();
168 iLifetime = recording.LifeTime();
169 iGenreType = recording.GenreType();
170 iGenreSubType = recording.GenreSubType();
171 iPlayCount = recording.GetLocalPlayCount();
172 iLastPlayedPosition =
173 static_cast<int>(std::lrint(recording.GetLocalResumePoint().timeInSeconds));
174 bIsDeleted = recording.IsDeleted();
175 iEpgEventId = recording.BroadcastUid();
176 iChannelUid = recording.ChannelUid();
177 channelType =
178 recording.IsRadio() ? PVR_RECORDING_CHANNEL_TYPE_RADIO : PVR_RECORDING_CHANNEL_TYPE_TV;
179 strFirstAired = m_firstAired.c_str();
180 iFlags = recording.Flags();
181 sizeInBytes = recording.GetSizeInBytes();
182 strProviderName = m_providerName.c_str();
183 iClientProviderUid = recording.ClientProviderUid();
184 strParentalRatingCode = m_parentalRatingCode.c_str();
185 strParentalRatingIcon = m_parentalRatingIcon.c_str();
186 strParentalRatingSource = m_parentalRatingSource.c_str();
188 virtual ~CAddonRecording() = default;
190 private:
191 const std::string m_recordingId;
192 const std::string m_title;
193 const std::string m_titleExtraInfo;
194 const std::string m_episodeName;
195 const std::string m_directory;
196 const std::string m_plotOutline;
197 const std::string m_plot;
198 const std::string m_genreDescription;
199 const std::string m_channelName;
200 const std::string m_iconPath;
201 const std::string m_thumbnailPath;
202 const std::string m_fanartPath;
203 const std::string m_firstAired;
204 const std::string m_providerName;
205 const std::string m_parentalRatingCode;
206 const std::string m_parentalRatingIcon;
207 const std::string m_parentalRatingSource;
210 class CAddonTimer : public PVR_TIMER
212 public:
213 explicit CAddonTimer(const CPVRTimerInfoTag& timer)
214 : m_title(timer.Title()),
215 m_epgSearchString(timer.EpgSearchString()),
216 m_directory(timer.Directory()),
217 m_summary(timer.Summary()),
218 m_seriesLink(timer.SeriesLink())
220 // zero-init base struct members
221 PVR_TIMER* base = static_cast<PVR_TIMER*>(this);
222 *base = {};
224 time_t start;
225 timer.StartAsUTC().GetAsTime(start);
226 time_t end;
227 timer.EndAsUTC().GetAsTime(end);
228 time_t first;
229 timer.FirstDayAsUTC().GetAsTime(first);
230 const std::shared_ptr<const CPVREpgInfoTag> epgTag{timer.GetEpgInfoTag()};
231 const int timeCorrection{
232 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection};
234 iClientIndex = timer.ClientIndex();
235 iParentClientIndex = timer.ParentClientIndex();
236 state = timer.State();
237 iTimerType = timer.GetTimerType()->GetTypeId();
238 iClientChannelUid = timer.ClientChannelUID();
239 strTitle = m_title.c_str();
240 strEpgSearchString = m_epgSearchString.c_str();
241 bFullTextEpgSearch = timer.IsFullTextEpgSearch();
242 strDirectory = m_directory.c_str();
243 iPriority = timer.Priority();
244 iLifetime = timer.Lifetime();
245 iMaxRecordings = timer.MaxRecordings();
246 iPreventDuplicateEpisodes = timer.PreventDupEpisodesPolicy();
247 iRecordingGroup = timer.RecordingGroup();
248 iWeekdays = timer.WeekDays();
249 startTime = start - timeCorrection;
250 endTime = end - timeCorrection;
251 bStartAnyTime = timer.IsStartAnyTime();
252 bEndAnyTime = timer.IsEndAnyTime();
253 firstDay = first - timeCorrection;
254 iEpgUid = epgTag ? epgTag->UniqueBroadcastID() : PVR_TIMER_NO_EPG_UID;
255 strSummary = m_summary.c_str();
256 iMarginStart = timer.MarginStart();
257 iMarginEnd = timer.MarginEnd();
258 iGenreType = epgTag ? epgTag->GenreType() : 0;
259 iGenreSubType = epgTag ? epgTag->GenreSubType() : 0;
260 strSeriesLink = m_seriesLink.c_str();
262 const auto& props{timer.GetCustomProperties()};
263 iCustomPropsSize = static_cast<unsigned int>(props.size());
264 if (iCustomPropsSize)
266 m_customProps = std::make_unique<PVR_SETTING_KEY_VALUE_PAIR[]>(iCustomPropsSize);
267 int idx{0};
268 for (const auto& entry : props)
270 PVR_SETTING_KEY_VALUE_PAIR& prop{m_customProps[idx]};
271 prop.iKey = entry.first;
272 prop.eType = entry.second.type;
273 prop.iValue = entry.second.value.asInteger32();
274 m_customPropStringValues.emplace_back(entry.second.value.asString());
275 prop.strValue = m_customPropStringValues.back().c_str();
276 ++idx;
278 customProps = m_customProps.get();
281 virtual ~CAddonTimer() = default;
283 private:
284 const std::string m_title;
285 const std::string m_epgSearchString;
286 const std::string m_directory;
287 const std::string m_summary;
288 const std::string m_seriesLink;
289 std::vector<std::string> m_customPropStringValues;
290 std::unique_ptr<PVR_SETTING_KEY_VALUE_PAIR[]> m_customProps;
293 class CAddonEpgTag : public EPG_TAG
295 public:
296 explicit CAddonEpgTag(const CPVREpgInfoTag& tag)
297 : m_title(tag.Title()),
298 m_titleExtraInfo(tag.TitleExtraInfo()),
299 m_plotOutline(tag.PlotOutline()),
300 m_plot(tag.Plot()),
301 m_originalTitle(tag.OriginalTitle()),
302 m_cast(tag.DeTokenize(tag.Cast())),
303 m_director(tag.DeTokenize(tag.Directors())),
304 m_writer(tag.DeTokenize(tag.Writers())),
305 m_IMDBNumber(tag.IMDBNumber()),
306 m_episodeName(tag.EpisodeName()),
307 m_iconPath(tag.ClientIconPath()),
308 m_seriesLink(tag.SeriesLink()),
309 m_genreDescription(tag.GenreDescription()),
310 m_firstAired(GetFirstAired(tag)),
311 m_parentalRatingCode(tag.ParentalRatingCode()),
312 m_parentalRatingIcon(tag.ClientParentalRatingIconPath()),
313 m_parentalRatingSource(tag.ParentalRatingSource())
315 // zero-init base struct members
316 EPG_TAG* base = static_cast<EPG_TAG*>(this);
317 *base = {};
319 time_t t;
320 tag.StartAsUTC().GetAsTime(t);
321 startTime = t;
322 tag.EndAsUTC().GetAsTime(t);
323 endTime = t;
325 iUniqueBroadcastId = tag.UniqueBroadcastID();
326 iUniqueChannelId = tag.UniqueChannelID();
327 iParentalRating = tag.ParentalRating();
328 iSeriesNumber = tag.SeriesNumber();
329 iEpisodeNumber = tag.EpisodeNumber();
330 iEpisodePartNumber = tag.EpisodePart();
331 iStarRating = tag.StarRating();
332 iYear = tag.Year();
333 iFlags = tag.Flags();
334 iGenreType = tag.GenreType();
335 iGenreSubType = tag.GenreSubType();
336 strTitle = m_title.c_str();
337 strTitleExtraInfo = m_titleExtraInfo.c_str();
338 strPlotOutline = m_plotOutline.c_str();
339 strPlot = m_plot.c_str();
340 strOriginalTitle = m_originalTitle.c_str();
341 strCast = m_cast.c_str();
342 strDirector = m_director.c_str();
343 strWriter = m_writer.c_str();
344 strIMDBNumber = m_IMDBNumber.c_str();
345 strEpisodeName = m_episodeName.c_str();
346 strIconPath = m_iconPath.c_str();
347 strSeriesLink = m_seriesLink.c_str();
348 strGenreDescription = m_genreDescription.c_str();
349 strFirstAired = m_firstAired.c_str();
350 strParentalRatingCode = m_parentalRatingCode.c_str();
351 strParentalRatingIcon = m_parentalRatingIcon.c_str();
352 strParentalRatingSource = m_parentalRatingSource.c_str();
355 virtual ~CAddonEpgTag() = default;
357 private:
358 static std::string GetFirstAired(const CPVREpgInfoTag& tag)
360 const CDateTime firstAired{tag.FirstAired()};
361 if (firstAired.IsValid())
362 return firstAired.GetAsW3CDate();
363 return {};
366 const std::string m_title;
367 const std::string m_titleExtraInfo;
368 const std::string m_plotOutline;
369 const std::string m_plot;
370 const std::string m_originalTitle;
371 const std::string m_cast;
372 const std::string m_director;
373 const std::string m_writer;
374 const std::string m_IMDBNumber;
375 const std::string m_episodeName;
376 const std::string m_iconPath;
377 const std::string m_seriesLink;
378 const std::string m_genreDescription;
379 const std::string m_firstAired;
380 const std::string m_parentalRatingCode;
381 const std::string m_parentalRatingIcon;
382 const std::string m_parentalRatingSource;
385 EDL::Edit ConvertAddonEdl(const PVR_EDL_ENTRY& entry)
387 EDL::Edit edit;
388 edit.start = std::chrono::milliseconds(entry.start);
389 edit.end = std::chrono::milliseconds(entry.end);
391 switch (entry.type)
393 case PVR_EDL_TYPE_CUT:
394 edit.action = EDL::Action::CUT;
395 break;
396 case PVR_EDL_TYPE_MUTE:
397 edit.action = EDL::Action::MUTE;
398 break;
399 case PVR_EDL_TYPE_SCENE:
400 edit.action = EDL::Action::SCENE;
401 break;
402 case PVR_EDL_TYPE_COMBREAK:
403 edit.action = EDL::Action::COMM_BREAK;
404 break;
405 default:
406 CLog::LogF(LOGWARNING, "Ignoring entry of unknown EDL type: {}", entry.type);
407 break;
410 return edit;
412 } // unnamed namespace
414 namespace PVR
417 #define DEFAULT_INFO_STRING_VALUE "unknown"
419 CPVRClient::CPVRClient(const ADDON::AddonInfoPtr& addonInfo,
420 ADDON::AddonInstanceId instanceId,
421 int clientId)
422 : IAddonInstanceHandler(ADDON_INSTANCE_PVR, addonInfo, instanceId), m_iClientId(clientId)
424 // Create all interface parts independent to make API changes easier if
425 // something is added
426 m_ifc.pvr = new AddonInstance_PVR;
427 m_ifc.pvr->props = new AddonProperties_PVR();
428 m_ifc.pvr->toKodi = new AddonToKodiFuncTable_PVR();
429 m_ifc.pvr->toAddon = new KodiToAddonFuncTable_PVR();
431 ResetProperties();
434 CPVRClient::~CPVRClient()
436 Destroy();
438 if (m_ifc.pvr)
440 delete m_ifc.pvr->props;
441 delete m_ifc.pvr->toKodi;
442 delete m_ifc.pvr->toAddon;
444 delete m_ifc.pvr;
447 void CPVRClient::StopRunningInstance()
449 // stop the pvr manager and stop and unload the running pvr addon. pvr manager will be restarted on demand.
450 CServiceBroker::GetPVRManager().Stop();
451 CServiceBroker::GetPVRManager().Clients()->StopClient(m_iClientId, false);
454 void CPVRClient::OnPreInstall()
456 // note: this method is also called on update; thus stop and unload possibly running instance
457 StopRunningInstance();
460 void CPVRClient::OnPreUnInstall()
462 StopRunningInstance();
465 void CPVRClient::ResetProperties()
467 std::unique_lock<CCriticalSection> lock(m_critSection);
469 /* initialise members */
470 m_strUserPath = CSpecialProtocol::TranslatePath(Profile());
471 m_strClientPath = CSpecialProtocol::TranslatePath(Path());
472 m_bReadyToUse = false;
473 m_bBlockAddonCalls = false;
474 m_iAddonCalls = 0;
475 m_allAddonCallsFinished.Set();
476 m_connectionState = PVR_CONNECTION_STATE_UNKNOWN;
477 m_prevConnectionState = PVR_CONNECTION_STATE_UNKNOWN;
478 m_ignoreClient = false;
479 m_priority.reset();
480 m_strBackendVersion = DEFAULT_INFO_STRING_VALUE;
481 m_strConnectionString = DEFAULT_INFO_STRING_VALUE;
482 m_strBackendName = DEFAULT_INFO_STRING_VALUE;
483 m_strBackendHostname.clear();
484 m_menuhooks.reset();
485 m_timertypes.clear();
486 m_clientCapabilities.clear();
488 m_ifc.pvr->props->strUserPath = m_strUserPath.c_str();
489 m_ifc.pvr->props->strClientPath = m_strClientPath.c_str();
490 m_ifc.pvr->props->iEpgMaxPastDays =
491 CServiceBroker::GetPVRManager().EpgContainer().GetPastDaysToDisplay();
492 m_ifc.pvr->props->iEpgMaxFutureDays =
493 CServiceBroker::GetPVRManager().EpgContainer().GetFutureDaysToDisplay();
495 m_ifc.pvr->toKodi->kodiInstance = this;
496 m_ifc.pvr->toKodi->TransferEpgEntry = cb_transfer_epg_entry;
497 m_ifc.pvr->toKodi->TransferChannelEntry = cb_transfer_channel_entry;
498 m_ifc.pvr->toKodi->TransferProviderEntry = cb_transfer_provider_entry;
499 m_ifc.pvr->toKodi->TransferTimerEntry = cb_transfer_timer_entry;
500 m_ifc.pvr->toKodi->TransferRecordingEntry = cb_transfer_recording_entry;
501 m_ifc.pvr->toKodi->AddMenuHook = cb_add_menu_hook;
502 m_ifc.pvr->toKodi->RecordingNotification = cb_recording_notification;
503 m_ifc.pvr->toKodi->TriggerChannelUpdate = cb_trigger_channel_update;
504 m_ifc.pvr->toKodi->TriggerProvidersUpdate = cb_trigger_provider_update;
505 m_ifc.pvr->toKodi->TriggerChannelGroupsUpdate = cb_trigger_channel_groups_update;
506 m_ifc.pvr->toKodi->TriggerTimerUpdate = cb_trigger_timer_update;
507 m_ifc.pvr->toKodi->TriggerRecordingUpdate = cb_trigger_recording_update;
508 m_ifc.pvr->toKodi->TriggerEpgUpdate = cb_trigger_epg_update;
509 m_ifc.pvr->toKodi->FreeDemuxPacket = cb_free_demux_packet;
510 m_ifc.pvr->toKodi->AllocateDemuxPacket = cb_allocate_demux_packet;
511 m_ifc.pvr->toKodi->TransferChannelGroup = cb_transfer_channel_group;
512 m_ifc.pvr->toKodi->TransferChannelGroupMember = cb_transfer_channel_group_member;
513 m_ifc.pvr->toKodi->ConnectionStateChange = cb_connection_state_change;
514 m_ifc.pvr->toKodi->EpgEventStateChange = cb_epg_event_state_change;
515 m_ifc.pvr->toKodi->GetCodecByName = cb_get_codec_by_name;
517 // Clear function addresses to have NULL if not set by addon
518 memset(m_ifc.pvr->toAddon, 0, sizeof(KodiToAddonFuncTable_PVR));
521 ADDON_STATUS CPVRClient::Create()
523 ResetProperties();
525 CLog::LogFC(LOGDEBUG, LOGPVR, "Creating PVR add-on instance [{},{},{}]", ID(), InstanceId(),
526 m_iClientId);
528 const ADDON_STATUS status = CreateInstance();
530 if (status == ADDON_STATUS_OK)
532 m_bReadyToUse = GetAddonProperties();
534 CLog::LogFC(LOGDEBUG, LOGPVR,
535 "Created PVR add-on instance {}. readytouse={}, ignoreclient={}, "
536 "connectionstate={}",
537 GetID(), m_bReadyToUse, IgnoreClient(), GetConnectionState());
539 else
541 m_bReadyToUse = false;
543 CLog::LogF(LOGERROR, "Failed to create PVR add-on instance {}. status={}", GetID(), status);
546 return status;
549 void CPVRClient::Destroy()
551 if (!m_bReadyToUse)
552 return;
554 m_bReadyToUse = false;
556 CLog::LogFC(LOGDEBUG, LOGPVR, "Destroying PVR add-on instance {}", GetID());
558 m_bBlockAddonCalls = true;
559 m_allAddonCallsFinished.Wait();
561 DestroyInstance();
563 CLog::LogFC(LOGDEBUG, LOGPVR, "Destroyed PVR add-on instance {}", GetID());
565 if (m_menuhooks)
566 m_menuhooks->Clear();
568 ResetProperties();
571 void CPVRClient::Stop()
573 m_bBlockAddonCalls = true;
574 m_priority.reset();
577 void CPVRClient::Continue()
579 m_bBlockAddonCalls = false;
582 void CPVRClient::ReCreate()
584 Destroy();
585 Create();
588 bool CPVRClient::ReadyToUse() const
590 return m_bReadyToUse;
593 PVR_CONNECTION_STATE CPVRClient::GetConnectionState() const
595 std::unique_lock<CCriticalSection> lock(m_critSection);
596 return m_connectionState;
599 void CPVRClient::SetConnectionState(PVR_CONNECTION_STATE state)
601 if (state == PVR_CONNECTION_STATE_CONNECTED)
603 // update properties - some will only be available after add-on is connected to backend
604 if (!GetAddonProperties())
605 CLog::LogF(LOGERROR, "Error reading PVR client properties");
607 else
609 if (!GetAddonNameStringProperties())
610 CLog::LogF(LOGERROR, "Cannot read PVR client name string properties");
613 std::unique_lock<CCriticalSection> lock(m_critSection);
615 m_prevConnectionState = m_connectionState;
616 m_connectionState = state;
618 if (m_connectionState == PVR_CONNECTION_STATE_CONNECTED)
619 m_ignoreClient = false;
620 else if (m_connectionState == PVR_CONNECTION_STATE_CONNECTING &&
621 m_prevConnectionState == PVR_CONNECTION_STATE_UNKNOWN)
622 m_ignoreClient = true; // ignore until connected
625 PVR_CONNECTION_STATE CPVRClient::GetPreviousConnectionState() const
627 std::unique_lock<CCriticalSection> lock(m_critSection);
628 return m_prevConnectionState;
631 bool CPVRClient::IgnoreClient() const
633 std::unique_lock<CCriticalSection> lock(m_critSection);
634 return m_ignoreClient;
637 bool CPVRClient::IsEnabled() const
639 if (InstanceId() == ADDON_SINGLETON_INSTANCE_ID)
641 return !CServiceBroker::GetAddonMgr().IsAddonDisabled(ID());
643 else
645 bool instanceEnabled{false};
646 Addon()->ReloadSettings(InstanceId());
647 Addon()->GetSettingBool(ADDON_SETTING_INSTANCE_ENABLED_VALUE, instanceEnabled, InstanceId());
648 return instanceEnabled;
652 int CPVRClient::GetID() const
654 return m_iClientId;
657 bool CPVRClient::GetAddonProperties()
659 if (!GetAddonNameStringProperties())
660 return false;
662 PVR_ADDON_CAPABILITIES addonCapabilities = {};
664 /* get the capabilities */
665 PVR_ERROR retVal = DoAddonCall(
666 __func__,
667 [&addonCapabilities](const AddonInstance* addon)
668 { return addon->toAddon->GetCapabilities(addon, &addonCapabilities); },
669 true, false);
671 if (retVal == PVR_ERROR_NO_ERROR)
673 std::unique_lock<CCriticalSection> lock(m_critSection);
674 m_clientCapabilities = addonCapabilities;
677 /* free the resources of the capabilities instance */
678 DoAddonCall(
679 __func__,
680 [&addonCapabilities](const AddonInstance* addon)
681 { return addon->toAddon->FreeCapabilities(addon, &addonCapabilities); },
682 true, false);
684 return (retVal == PVR_ERROR_NO_ERROR);
687 bool CPVRClient::GetAddonNameStringProperties()
689 std::string backendName;
690 std::string connectionString;
691 std::string backendVersion;
692 std::string backendHostname;
694 /* get the name of the backend */
695 PVR_ERROR retVal = DoAddonCall(
696 __func__,
697 [&backendName](const AddonInstance* addon)
699 char* strBackendName{nullptr};
700 const PVR_ERROR error{addon->toAddon->GetBackendName(addon, &strBackendName)};
701 if (error == PVR_ERROR_NO_ERROR && strBackendName != nullptr)
702 backendName = strBackendName;
703 addon->toAddon->FreeString(addon, strBackendName);
704 return error;
706 true, false);
708 if (retVal != PVR_ERROR_NO_ERROR)
709 return false;
711 /* get the connection string */
712 retVal = DoAddonCall(
713 __func__,
714 [&connectionString](const AddonInstance* addon)
716 char* strConnectionString{nullptr};
717 const PVR_ERROR error{addon->toAddon->GetConnectionString(addon, &strConnectionString)};
718 if (error == PVR_ERROR_NO_ERROR && strConnectionString != nullptr)
719 connectionString = strConnectionString;
720 addon->toAddon->FreeString(addon, strConnectionString);
721 return error;
723 true, false);
725 if (retVal != PVR_ERROR_NO_ERROR && retVal != PVR_ERROR_NOT_IMPLEMENTED)
726 return false;
728 /* backend version number */
729 retVal = DoAddonCall(
730 __func__,
731 [&backendVersion](const AddonInstance* addon)
733 char* strBackendVersion{nullptr};
734 const PVR_ERROR error{addon->toAddon->GetBackendVersion(addon, &strBackendVersion)};
735 if (error == PVR_ERROR_NO_ERROR && strBackendVersion != nullptr)
736 backendVersion = strBackendVersion;
737 addon->toAddon->FreeString(addon, strBackendVersion);
738 return error;
740 true, false);
742 if (retVal != PVR_ERROR_NO_ERROR)
743 return false;
745 /* backend hostname */
746 retVal = DoAddonCall(
747 __func__,
748 [&backendHostname](const AddonInstance* addon)
750 char* strBackendHostname{nullptr};
751 const PVR_ERROR error{addon->toAddon->GetBackendHostname(addon, &strBackendHostname)};
752 if (error == PVR_ERROR_NO_ERROR && strBackendHostname != nullptr)
753 backendHostname = strBackendHostname;
754 addon->toAddon->FreeString(addon, strBackendHostname);
755 return error;
757 true, false);
759 if (retVal != PVR_ERROR_NO_ERROR && retVal != PVR_ERROR_NOT_IMPLEMENTED)
760 return false;
762 /* update the members */
763 std::unique_lock<CCriticalSection> lock(m_critSection);
764 m_strBackendName = backendName;
765 m_strConnectionString = connectionString;
766 m_strBackendVersion = backendVersion;
767 m_strBackendHostname = backendHostname;
769 return true;
772 const std::string& CPVRClient::GetBackendName() const
774 return m_strBackendName;
777 const std::string& CPVRClient::GetBackendVersion() const
779 return m_strBackendVersion;
782 const std::string& CPVRClient::GetBackendHostname() const
784 return m_strBackendHostname;
787 const std::string& CPVRClient::GetConnectionString() const
789 return m_strConnectionString;
792 std::string CPVRClient::GetClientName() const
794 return Name();
797 std::string CPVRClient::GetInstanceName() const
799 std::string instanceName;
800 if (Addon()->SupportsInstanceSettings())
801 Addon()->GetSettingString(ADDON_SETTING_INSTANCE_NAME_VALUE, instanceName, InstanceId());
803 return instanceName;
806 std::string CPVRClient::GetFullClientName() const
808 if (Addon()->SupportsInstanceSettings())
810 std::string instanceName;
811 Addon()->GetSettingString(ADDON_SETTING_INSTANCE_NAME_VALUE, instanceName, InstanceId());
812 if (!instanceName.empty())
813 return StringUtils::Format("{} ({})", Name(), instanceName);
815 return Name();
818 PVR_ERROR CPVRClient::GetDriveSpace(uint64_t& iTotal, uint64_t& iUsed) const
820 /* default to 0 in case of error */
821 iTotal = 0;
822 iUsed = 0;
824 return DoAddonCall(__func__,
825 [&iTotal, &iUsed](const AddonInstance* addon)
827 uint64_t iTotalSpace = 0;
828 uint64_t iUsedSpace = 0;
829 PVR_ERROR error =
830 addon->toAddon->GetDriveSpace(addon, &iTotalSpace, &iUsedSpace);
831 if (error == PVR_ERROR_NO_ERROR)
833 iTotal = iTotalSpace;
834 iUsed = iUsedSpace;
836 return error;
840 PVR_ERROR CPVRClient::StartChannelScan()
842 return DoAddonCall(
843 __func__,
844 [](const AddonInstance* addon) { return addon->toAddon->OpenDialogChannelScan(addon); },
845 m_clientCapabilities.SupportsChannelScan());
848 PVR_ERROR CPVRClient::OpenDialogChannelAdd(const std::shared_ptr<const CPVRChannel>& channel)
850 return DoAddonCall(
851 __func__,
852 [channel](const AddonInstance* addon)
854 const CAddonChannel addonChannel{*channel};
855 return addon->toAddon->OpenDialogChannelAdd(addon, &addonChannel);
857 m_clientCapabilities.SupportsChannelSettings());
860 PVR_ERROR CPVRClient::OpenDialogChannelSettings(const std::shared_ptr<const CPVRChannel>& channel)
862 return DoAddonCall(
863 __func__,
864 [channel](const AddonInstance* addon)
866 const CAddonChannel addonChannel{*channel};
867 return addon->toAddon->OpenDialogChannelSettings(addon, &addonChannel);
869 m_clientCapabilities.SupportsChannelSettings());
872 PVR_ERROR CPVRClient::DeleteChannel(const std::shared_ptr<const CPVRChannel>& channel)
874 return DoAddonCall(
875 __func__,
876 [channel](const AddonInstance* addon)
878 const CAddonChannel addonChannel{*channel};
879 return addon->toAddon->DeleteChannel(addon, &addonChannel);
881 m_clientCapabilities.SupportsChannelSettings());
884 PVR_ERROR CPVRClient::RenameChannel(const std::shared_ptr<const CPVRChannel>& channel)
886 return DoAddonCall(
887 __func__,
888 [channel](const AddonInstance* addon)
890 const CAddonChannel addonChannel{*channel, channel->ChannelName()};
891 return addon->toAddon->RenameChannel(addon, &addonChannel);
893 m_clientCapabilities.SupportsChannelSettings());
896 PVR_ERROR CPVRClient::GetEPGForChannel(int iChannelUid,
897 CPVREpg* epg,
898 time_t start,
899 time_t end) const
901 return DoAddonCall(
902 __func__,
903 [this, iChannelUid, epg, start, end](const AddonInstance* addon)
905 PVR_HANDLE_STRUCT handle = {};
906 handle.callerAddress = this;
907 handle.dataAddress = epg;
909 int iPVRTimeCorrection =
910 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection;
912 return addon->toAddon->GetEPGForChannel(addon, &handle, iChannelUid,
913 start ? start - iPVRTimeCorrection : 0,
914 end ? end - iPVRTimeCorrection : 0);
916 m_clientCapabilities.SupportsEPG());
919 PVR_ERROR CPVRClient::SetEPGMaxPastDays(int iPastDays)
921 return DoAddonCall(
922 __func__,
923 [iPastDays](const AddonInstance* addon)
924 { return addon->toAddon->SetEPGMaxPastDays(addon, iPastDays); },
925 m_clientCapabilities.SupportsEPG());
928 PVR_ERROR CPVRClient::SetEPGMaxFutureDays(int iFutureDays)
930 return DoAddonCall(
931 __func__,
932 [iFutureDays](const AddonInstance* addon)
933 { return addon->toAddon->SetEPGMaxFutureDays(addon, iFutureDays); },
934 m_clientCapabilities.SupportsEPG());
937 PVR_ERROR CPVRClient::IsRecordable(const std::shared_ptr<const CPVREpgInfoTag>& tag,
938 bool& bIsRecordable) const
940 return DoAddonCall(
941 __func__,
942 [tag, &bIsRecordable](const AddonInstance* addon)
944 CAddonEpgTag addonTag(*tag);
945 return addon->toAddon->IsEPGTagRecordable(addon, &addonTag, &bIsRecordable);
947 m_clientCapabilities.SupportsRecordings() && m_clientCapabilities.SupportsEPG());
950 PVR_ERROR CPVRClient::IsPlayable(const std::shared_ptr<const CPVREpgInfoTag>& tag,
951 bool& bIsPlayable) const
953 return DoAddonCall(
954 __func__,
955 [tag, &bIsPlayable](const AddonInstance* addon)
957 CAddonEpgTag addonTag(*tag);
958 return addon->toAddon->IsEPGTagPlayable(addon, &addonTag, &bIsPlayable);
960 m_clientCapabilities.SupportsEPG());
963 void CPVRClient::WriteStreamProperties(PVR_NAMED_VALUE** properties,
964 unsigned int iPropertyCount,
965 CPVRStreamProperties& props)
967 for (unsigned int i = 0; i < iPropertyCount; ++i)
969 const PVR_NAMED_VALUE* prop{properties[i]};
970 props.emplace_back(std::make_pair(prop->strName, prop->strValue));
974 PVR_ERROR CPVRClient::GetEpgTagStreamProperties(const std::shared_ptr<const CPVREpgInfoTag>& tag,
975 CPVRStreamProperties& props) const
977 return DoAddonCall(__func__,
978 [&tag, &props](const AddonInstance* addon)
980 CAddonEpgTag addonTag(*tag);
982 PVR_NAMED_VALUE** property_array{nullptr};
983 unsigned int size{0};
984 const PVR_ERROR error{addon->toAddon->GetEPGTagStreamProperties(
985 addon, &addonTag, &property_array, &size)};
986 if (error == PVR_ERROR_NO_ERROR)
987 WriteStreamProperties(property_array, size, props);
989 addon->toAddon->FreeProperties(addon, property_array, size);
990 return error;
994 PVR_ERROR CPVRClient::GetEpgTagEdl(const std::shared_ptr<const CPVREpgInfoTag>& epgTag,
995 std::vector<EDL::Edit>& edls) const
997 edls.clear();
998 return DoAddonCall(
999 __func__,
1000 [&epgTag, &edls](const AddonInstance* addon)
1002 CAddonEpgTag addonTag(*epgTag);
1004 PVR_EDL_ENTRY** edl_array{nullptr};
1005 unsigned int size{0};
1006 const PVR_ERROR error{addon->toAddon->GetEPGTagEdl(addon, &addonTag, &edl_array, &size)};
1007 if (error == PVR_ERROR_NO_ERROR)
1009 edls.reserve(size);
1010 for (unsigned int i = 0; i < size; ++i)
1011 edls.emplace_back(ConvertAddonEdl(*edl_array[i]));
1013 addon->toAddon->FreeEdlEntries(addon, edl_array, size);
1014 return error;
1016 m_clientCapabilities.SupportsEpgTagEdl());
1019 PVR_ERROR CPVRClient::GetChannelGroupsAmount(int& iGroups) const
1021 iGroups = -1;
1022 return DoAddonCall(
1023 __func__,
1024 [&iGroups](const AddonInstance* addon)
1025 { return addon->toAddon->GetChannelGroupsAmount(addon, &iGroups); },
1026 m_clientCapabilities.SupportsChannelGroups());
1029 PVR_ERROR CPVRClient::GetChannelGroups(CPVRChannelGroups* groups) const
1031 const bool radio{groups->IsRadio()};
1032 return DoAddonCall(
1033 __func__,
1034 [this, groups](const AddonInstance* addon)
1036 PVR_HANDLE_STRUCT handle = {};
1037 handle.callerAddress = this;
1038 handle.dataAddress = groups;
1039 return addon->toAddon->GetChannelGroups(addon, &handle, groups->IsRadio());
1041 m_clientCapabilities.SupportsChannelGroups() &&
1042 ((radio && m_clientCapabilities.SupportsRadio()) ||
1043 (!radio && m_clientCapabilities.SupportsTV())));
1046 PVR_ERROR CPVRClient::GetChannelGroupMembers(
1047 CPVRChannelGroup* group,
1048 std::vector<std::shared_ptr<CPVRChannelGroupMember>>& groupMembers) const
1050 const bool radio{group->IsRadio()};
1051 return DoAddonCall(
1052 __func__,
1053 [this, group, &groupMembers](const AddonInstance* addon)
1055 PVR_HANDLE_STRUCT handle = {};
1056 handle.callerAddress = this;
1057 handle.dataAddress = &groupMembers;
1059 const CAddonChannelGroup addonGroup{*group};
1060 return addon->toAddon->GetChannelGroupMembers(addon, &handle, &addonGroup);
1062 m_clientCapabilities.SupportsChannelGroups() &&
1063 ((radio && m_clientCapabilities.SupportsRadio()) ||
1064 (!radio && m_clientCapabilities.SupportsTV())));
1067 PVR_ERROR CPVRClient::GetProvidersAmount(int& iProviders) const
1069 iProviders = -1;
1070 return DoAddonCall(__func__, [&iProviders](const AddonInstance* addon)
1071 { return addon->toAddon->GetProvidersAmount(addon, &iProviders); });
1074 PVR_ERROR CPVRClient::GetProviders(CPVRProvidersContainer& providers) const
1076 return DoAddonCall(
1077 __func__,
1078 [this, &providers](const AddonInstance* addon)
1080 PVR_HANDLE_STRUCT handle = {};
1081 handle.callerAddress = this;
1082 handle.dataAddress = &providers;
1083 return addon->toAddon->GetProviders(addon, &handle);
1085 m_clientCapabilities.SupportsProviders());
1088 PVR_ERROR CPVRClient::GetChannelsAmount(int& iChannels) const
1090 iChannels = -1;
1091 return DoAddonCall(__func__, [&iChannels](const AddonInstance* addon)
1092 { return addon->toAddon->GetChannelsAmount(addon, &iChannels); });
1095 PVR_ERROR CPVRClient::GetChannels(bool radio,
1096 std::vector<std::shared_ptr<CPVRChannel>>& channels) const
1098 return DoAddonCall(
1099 __func__,
1100 [this, radio, &channels](const AddonInstance* addon)
1102 PVR_HANDLE_STRUCT handle = {};
1103 handle.callerAddress = this;
1104 handle.dataAddress = &channels;
1105 const PVR_ERROR error{addon->toAddon->GetChannels(addon, &handle, radio)};
1107 if (error == PVR_ERROR_NO_ERROR)
1109 const CDateTime& dateTime{GetDateTimeFirstChannelsAdded()};
1110 if (!dateTime.IsValid())
1112 // Remember when first channels were added for this client.
1113 const_cast<CPVRClient*>(this)->SetDateTimeFirstChannelsAdded(
1114 CDateTime::GetUTCDateTime());
1118 return error;
1120 (radio && m_clientCapabilities.SupportsRadio()) ||
1121 (!radio && m_clientCapabilities.SupportsTV()));
1124 PVR_ERROR CPVRClient::GetRecordingsAmount(bool deleted, int& iRecordings) const
1126 iRecordings = -1;
1127 return DoAddonCall(
1128 __func__,
1129 [deleted, &iRecordings](const AddonInstance* addon)
1130 { return addon->toAddon->GetRecordingsAmount(addon, deleted, &iRecordings); },
1131 m_clientCapabilities.SupportsRecordings() &&
1132 (!deleted || m_clientCapabilities.SupportsRecordingsUndelete()));
1135 PVR_ERROR CPVRClient::GetRecordings(CPVRRecordings* results, bool deleted) const
1137 return DoAddonCall(
1138 __func__,
1139 [this, results, deleted](const AddonInstance* addon)
1141 PVR_HANDLE_STRUCT handle = {};
1142 handle.callerAddress = this;
1143 handle.dataAddress = results;
1144 return addon->toAddon->GetRecordings(addon, &handle, deleted);
1146 m_clientCapabilities.SupportsRecordings() &&
1147 (!deleted || m_clientCapabilities.SupportsRecordingsUndelete()));
1150 PVR_ERROR CPVRClient::DeleteRecording(const CPVRRecording& recording)
1152 return DoAddonCall(
1153 __func__,
1154 [&recording](const AddonInstance* addon)
1156 const CAddonRecording tag{recording};
1157 return addon->toAddon->DeleteRecording(addon, &tag);
1159 m_clientCapabilities.SupportsRecordings() && m_clientCapabilities.SupportsRecordingsDelete());
1162 PVR_ERROR CPVRClient::UndeleteRecording(const CPVRRecording& recording)
1164 return DoAddonCall(
1165 __func__,
1166 [&recording](const AddonInstance* addon)
1168 const CAddonRecording tag{recording};
1169 return addon->toAddon->UndeleteRecording(addon, &tag);
1171 m_clientCapabilities.SupportsRecordingsUndelete());
1174 PVR_ERROR CPVRClient::DeleteAllRecordingsFromTrash()
1176 return DoAddonCall(
1177 __func__,
1178 [](const AddonInstance* addon)
1179 { return addon->toAddon->DeleteAllRecordingsFromTrash(addon); },
1180 m_clientCapabilities.SupportsRecordingsUndelete());
1183 PVR_ERROR CPVRClient::RenameRecording(const CPVRRecording& recording)
1185 return DoAddonCall(
1186 __func__,
1187 [&recording](const AddonInstance* addon)
1189 const CAddonRecording tag{recording};
1190 return addon->toAddon->RenameRecording(addon, &tag);
1192 m_clientCapabilities.SupportsRecordings());
1195 PVR_ERROR CPVRClient::SetRecordingLifetime(const CPVRRecording& recording)
1197 return DoAddonCall(
1198 __func__,
1199 [&recording](const AddonInstance* addon)
1201 const CAddonRecording tag{recording};
1202 return addon->toAddon->SetRecordingLifetime(addon, &tag);
1204 m_clientCapabilities.SupportsRecordingsLifetimeChange());
1207 PVR_ERROR CPVRClient::SetRecordingPlayCount(const CPVRRecording& recording, int count)
1209 return DoAddonCall(
1210 __func__,
1211 [&recording, count](const AddonInstance* addon)
1213 const CAddonRecording tag{recording};
1214 return addon->toAddon->SetRecordingPlayCount(addon, &tag, count);
1216 m_clientCapabilities.SupportsRecordingsPlayCount());
1219 PVR_ERROR CPVRClient::SetRecordingLastPlayedPosition(const CPVRRecording& recording,
1220 int lastplayedposition)
1222 return DoAddonCall(
1223 __func__,
1224 [&recording, lastplayedposition](const AddonInstance* addon)
1226 const CAddonRecording tag{recording};
1227 return addon->toAddon->SetRecordingLastPlayedPosition(addon, &tag, lastplayedposition);
1229 m_clientCapabilities.SupportsRecordingsLastPlayedPosition());
1232 PVR_ERROR CPVRClient::GetRecordingLastPlayedPosition(const CPVRRecording& recording,
1233 int& iPosition) const
1235 iPosition = -1;
1236 return DoAddonCall(
1237 __func__,
1238 [&recording, &iPosition](const AddonInstance* addon)
1240 const CAddonRecording tag{recording};
1241 return addon->toAddon->GetRecordingLastPlayedPosition(addon, &tag, &iPosition);
1243 m_clientCapabilities.SupportsRecordingsLastPlayedPosition());
1246 PVR_ERROR CPVRClient::GetRecordingEdl(const CPVRRecording& recording,
1247 std::vector<EDL::Edit>& edls) const
1249 edls.clear();
1250 return DoAddonCall(
1251 __func__,
1252 [&recording, &edls](const AddonInstance* addon)
1254 const CAddonRecording tag{recording};
1256 PVR_EDL_ENTRY** edl_array{nullptr};
1257 unsigned int size{0};
1258 const PVR_ERROR error{addon->toAddon->GetRecordingEdl(addon, &tag, &edl_array, &size)};
1259 if (error == PVR_ERROR_NO_ERROR)
1261 edls.reserve(size);
1262 for (unsigned int i = 0; i < size; ++i)
1263 edls.emplace_back(ConvertAddonEdl(*edl_array[i]));
1265 addon->toAddon->FreeEdlEntries(addon, edl_array, size);
1266 return error;
1268 m_clientCapabilities.SupportsRecordingsEdl());
1271 PVR_ERROR CPVRClient::GetRecordingSize(const CPVRRecording& recording, int64_t& sizeInBytes) const
1273 return DoAddonCall(
1274 __func__,
1275 [&recording, &sizeInBytes](const AddonInstance* addon)
1277 const CAddonRecording tag{recording};
1278 return addon->toAddon->GetRecordingSize(addon, &tag, &sizeInBytes);
1280 m_clientCapabilities.SupportsRecordingsSize());
1283 PVR_ERROR CPVRClient::GetTimersAmount(int& iTimers) const
1285 iTimers = -1;
1286 return DoAddonCall(
1287 __func__,
1288 [&iTimers](const AddonInstance* addon)
1289 { return addon->toAddon->GetTimersAmount(addon, &iTimers); },
1290 m_clientCapabilities.SupportsTimers());
1293 PVR_ERROR CPVRClient::GetTimers(CPVRTimersContainer* results) const
1295 return DoAddonCall(
1296 __func__,
1297 [this, results](const AddonInstance* addon)
1299 PVR_HANDLE_STRUCT handle = {};
1300 handle.callerAddress = this;
1301 handle.dataAddress = results;
1302 return addon->toAddon->GetTimers(addon, &handle);
1304 m_clientCapabilities.SupportsTimers());
1307 PVR_ERROR CPVRClient::AddTimer(const CPVRTimerInfoTag& timer)
1309 return DoAddonCall(
1310 __func__,
1311 [&timer](const AddonInstance* addon)
1313 const CAddonTimer tag{timer};
1314 return addon->toAddon->AddTimer(addon, &tag);
1316 m_clientCapabilities.SupportsTimers());
1319 PVR_ERROR CPVRClient::DeleteTimer(const CPVRTimerInfoTag& timer, bool bForce /* = false */)
1321 return DoAddonCall(
1322 __func__,
1323 [&timer, bForce](const AddonInstance* addon)
1325 const CAddonTimer tag{timer};
1326 return addon->toAddon->DeleteTimer(addon, &tag, bForce);
1328 m_clientCapabilities.SupportsTimers());
1331 PVR_ERROR CPVRClient::UpdateTimer(const CPVRTimerInfoTag& timer)
1333 return DoAddonCall(
1334 __func__,
1335 [&timer](const AddonInstance* addon)
1337 const CAddonTimer tag{timer};
1338 return addon->toAddon->UpdateTimer(addon, &tag);
1340 m_clientCapabilities.SupportsTimers());
1343 const std::vector<std::shared_ptr<CPVRTimerType>>& CPVRClient::GetTimerTypes() const
1345 std::unique_lock<CCriticalSection> lock(m_critSection);
1346 return m_timertypes;
1349 PVR_ERROR CPVRClient::UpdateTimerTypes()
1351 std::vector<std::shared_ptr<CPVRTimerType>> timerTypes;
1353 PVR_ERROR retVal = DoAddonCall(
1354 __func__,
1355 [this, &timerTypes](const AddonInstance* addon)
1357 PVR_TIMER_TYPE** types_array{nullptr};
1358 unsigned int size{0};
1359 PVR_ERROR retval{addon->toAddon->GetTimerTypes(addon, &types_array, &size)};
1361 const bool array_owner{retval == PVR_ERROR_NOT_IMPLEMENTED};
1362 if (array_owner)
1364 // begin compat section
1365 CLog::LogF(LOGWARNING,
1366 "Add-on {} does not support timer types. It will work, but not benefit from "
1367 "the timer features introduced with PVR Addon API 2.0.0",
1368 Name());
1370 // Create standard timer types (mostly) matching the timer functionality available in Isengard.
1371 // This is for migration only and does not make changes to the addons obsolete. Addons should
1372 // work and benefit from some UI changes (e.g. some of the timer settings dialog enhancements),
1373 // but all old problems/bugs due to static attributes and values will remain the same as in
1374 // Isengard. Also, new features (like epg search) are not available to addons automatically.
1375 // This code can be removed once all addons actually support the respective PVR Addon API version.
1377 size = 2;
1378 if (m_clientCapabilities.SupportsEPG())
1379 size++;
1381 types_array = new PVR_TIMER_TYPE*[size];
1383 // manual one time
1384 types_array[0] = new PVR_TIMER_TYPE{};
1385 types_array[0]->iId = 1;
1386 types_array[0]->iAttributes =
1387 PVR_TIMER_TYPE_IS_MANUAL | PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE |
1388 PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_TIME |
1389 PVR_TIMER_TYPE_SUPPORTS_END_TIME | PVR_TIMER_TYPE_SUPPORTS_PRIORITY |
1390 PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS;
1392 // manual timer rule
1393 types_array[1] = new PVR_TIMER_TYPE{};
1394 types_array[1]->iId = 2;
1395 types_array[1]->iAttributes =
1396 PVR_TIMER_TYPE_IS_MANUAL | PVR_TIMER_TYPE_IS_REPEATING |
1397 PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_SUPPORTS_CHANNELS |
1398 PVR_TIMER_TYPE_SUPPORTS_START_TIME | PVR_TIMER_TYPE_SUPPORTS_END_TIME |
1399 PVR_TIMER_TYPE_SUPPORTS_PRIORITY | PVR_TIMER_TYPE_SUPPORTS_LIFETIME |
1400 PVR_TIMER_TYPE_SUPPORTS_FIRST_DAY | PVR_TIMER_TYPE_SUPPORTS_WEEKDAYS |
1401 PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS;
1403 if (m_clientCapabilities.SupportsEPG())
1405 // One-shot epg-based
1406 types_array[2] = new PVR_TIMER_TYPE{};
1407 types_array[2]->iId = 3;
1408 types_array[2]->iAttributes =
1409 PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_REQUIRES_EPG_TAG_ON_CREATE |
1410 PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_TIME |
1411 PVR_TIMER_TYPE_SUPPORTS_END_TIME | PVR_TIMER_TYPE_SUPPORTS_PRIORITY |
1412 PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS;
1415 retval = PVR_ERROR_NO_ERROR;
1416 // end compat section
1419 if (retval == PVR_ERROR_NO_ERROR)
1421 timerTypes.reserve(size);
1422 for (unsigned int i = 0; i < size; ++i)
1424 if (types_array[i]->iId == PVR_TIMER_TYPE_NONE)
1426 CLog::LogF(LOGERROR, "Invalid timer type supplied by add-on {}.", GetID());
1427 continue;
1429 timerTypes.emplace_back(
1430 std::make_shared<CPVRTimerType>(*(types_array[i]), m_iClientId));
1434 /* free the resources of the timer types array */
1435 if (array_owner)
1437 // begin compat section
1438 for (unsigned int i = 0; i < size; ++i)
1439 delete types_array[i];
1441 delete[] types_array;
1442 // end compat section
1444 else
1446 addon->toAddon->FreeTimerTypes(addon, types_array, size);
1448 types_array = nullptr;
1450 return retval;
1452 m_clientCapabilities.SupportsTimers(), false);
1454 if (retVal == PVR_ERROR_NO_ERROR)
1456 std::vector<std::shared_ptr<CPVRTimerType>> newTimerTypes;
1457 newTimerTypes.reserve(timerTypes.size());
1459 std::unique_lock<CCriticalSection> lock(m_critSection);
1461 for (const auto& type : timerTypes)
1463 const auto it = std::find_if(m_timertypes.cbegin(), m_timertypes.cend(),
1464 [&type](const std::shared_ptr<const CPVRTimerType>& entry)
1465 { return entry->GetTypeId() == type->GetTypeId(); });
1466 if (it == m_timertypes.cend())
1468 newTimerTypes.emplace_back(type);
1470 else
1472 (*it)->Update(*type);
1473 newTimerTypes.emplace_back(*it);
1477 m_timertypes = newTimerTypes;
1479 return retVal;
1482 PVR_ERROR CPVRClient::GetStreamReadChunkSize(int& iChunkSize) const
1484 return DoAddonCall(
1485 __func__,
1486 [&iChunkSize](const AddonInstance* addon)
1487 { return addon->toAddon->GetStreamReadChunkSize(addon, &iChunkSize); },
1488 m_clientCapabilities.SupportsRecordings() || m_clientCapabilities.HandlesInputStream());
1491 PVR_ERROR CPVRClient::ReadLiveStream(void* lpBuf, int64_t uiBufSize, int& iRead)
1493 iRead = -1;
1494 return DoAddonCall(__func__,
1495 [&lpBuf, uiBufSize, &iRead](const AddonInstance* addon)
1497 iRead = addon->toAddon->ReadLiveStream(
1498 addon, static_cast<unsigned char*>(lpBuf), static_cast<int>(uiBufSize));
1499 return (iRead == -1) ? PVR_ERROR_NOT_IMPLEMENTED : PVR_ERROR_NO_ERROR;
1503 PVR_ERROR CPVRClient::ReadRecordedStream(int64_t streamId,
1504 void* lpBuf,
1505 int64_t uiBufSize,
1506 int& iRead)
1508 iRead = -1;
1509 return DoAddonCall(__func__,
1510 [streamId, &lpBuf, uiBufSize, &iRead](const AddonInstance* addon)
1512 iRead = addon->toAddon->ReadRecordedStream(
1513 addon, streamId, static_cast<unsigned char*>(lpBuf),
1514 static_cast<int>(uiBufSize));
1515 return (iRead == -1) ? PVR_ERROR_NOT_IMPLEMENTED : PVR_ERROR_NO_ERROR;
1519 PVR_ERROR CPVRClient::SeekLiveStream(int64_t iFilePosition, int iWhence, int64_t& iPosition)
1521 iPosition = -1;
1522 return DoAddonCall(__func__,
1523 [iFilePosition, iWhence, &iPosition](const AddonInstance* addon)
1525 iPosition = addon->toAddon->SeekLiveStream(addon, iFilePosition, iWhence);
1526 return (iPosition == -1) ? PVR_ERROR_NOT_IMPLEMENTED : PVR_ERROR_NO_ERROR;
1530 PVR_ERROR CPVRClient::SeekRecordedStream(int64_t streamId,
1531 int64_t iFilePosition,
1532 int iWhence,
1533 int64_t& iPosition)
1535 iPosition = -1;
1536 return DoAddonCall(__func__,
1537 [streamId, iFilePosition, iWhence, &iPosition](const AddonInstance* addon)
1539 iPosition = addon->toAddon->SeekRecordedStream(addon, streamId,
1540 iFilePosition, iWhence);
1541 return (iPosition == -1) ? PVR_ERROR_NOT_IMPLEMENTED : PVR_ERROR_NO_ERROR;
1545 PVR_ERROR CPVRClient::SeekTime(double time, bool backwards, double* startpts)
1547 return DoAddonCall(__func__,
1548 [time, backwards, &startpts](const AddonInstance* addon)
1550 return addon->toAddon->SeekTime(addon, time, backwards, startpts)
1551 ? PVR_ERROR_NO_ERROR
1552 : PVR_ERROR_NOT_IMPLEMENTED;
1556 PVR_ERROR CPVRClient::GetLiveStreamLength(int64_t& iLength) const
1558 iLength = -1;
1559 return DoAddonCall(__func__,
1560 [&iLength](const AddonInstance* addon)
1562 iLength = addon->toAddon->LengthLiveStream(addon);
1563 return (iLength == -1) ? PVR_ERROR_NOT_IMPLEMENTED : PVR_ERROR_NO_ERROR;
1567 PVR_ERROR CPVRClient::GetRecordedStreamLength(int64_t streamId, int64_t& iLength) const
1569 iLength = -1;
1570 return DoAddonCall(__func__,
1571 [streamId, &iLength](const AddonInstance* addon)
1573 iLength = addon->toAddon->LengthRecordedStream(addon, streamId);
1574 return (iLength == -1) ? PVR_ERROR_NOT_IMPLEMENTED : PVR_ERROR_NO_ERROR;
1578 PVR_ERROR CPVRClient::SignalQuality(int channelUid, CPVRSignalStatus& qualityinfo) const
1580 return DoAddonCall(__func__,
1581 [channelUid, &qualityinfo](const AddonInstance* addon)
1583 PVR_SIGNAL_STATUS info{};
1584 const PVR_ERROR error{
1585 addon->toAddon->GetSignalStatus(addon, channelUid, &info)};
1586 if (error == PVR_ERROR_NO_ERROR)
1587 qualityinfo = CPVRSignalStatus{info};
1589 addon->toAddon->FreeSignalStatus(addon, &info);
1590 return error;
1594 PVR_ERROR CPVRClient::GetDescrambleInfo(int channelUid, CPVRDescrambleInfo& descrambleinfo) const
1596 return DoAddonCall(
1597 __func__,
1598 [channelUid, &descrambleinfo](const AddonInstance* addon)
1600 PVR_DESCRAMBLE_INFO info{};
1601 const PVR_ERROR error{addon->toAddon->GetDescrambleInfo(addon, channelUid, &info)};
1602 if (error == PVR_ERROR_NO_ERROR)
1603 descrambleinfo = CPVRDescrambleInfo{info};
1605 addon->toAddon->FreeDescrambleInfo(addon, &info);
1606 return error;
1608 m_clientCapabilities.SupportsDescrambleInfo());
1611 PVR_ERROR CPVRClient::GetChannelStreamProperties(const std::shared_ptr<const CPVRChannel>& channel,
1612 PVR_SOURCE source,
1613 CPVRStreamProperties& props) const
1615 return DoAddonCall(
1616 __func__,
1617 [this, &channel, source, &props](const AddonInstance* addon)
1619 if (!CanPlayChannel(channel))
1620 return PVR_ERROR_NO_ERROR; // no error, but no need to obtain the values from the addon
1622 const CAddonChannel addonChannel{*channel};
1624 PVR_NAMED_VALUE** property_array{nullptr};
1625 unsigned int size{0};
1626 const PVR_ERROR error{addon->toAddon->GetChannelStreamProperties(
1627 addon, &addonChannel, source, &property_array, &size)};
1628 if (error == PVR_ERROR_NO_ERROR)
1629 WriteStreamProperties(property_array, size, props);
1631 addon->toAddon->FreeProperties(addon, property_array, size);
1632 return error;
1636 PVR_ERROR CPVRClient::GetRecordingStreamProperties(
1637 const std::shared_ptr<const CPVRRecording>& recording, CPVRStreamProperties& props) const
1639 return DoAddonCall(
1640 __func__,
1641 [this, &recording, &props](const AddonInstance* addon)
1643 if (!m_clientCapabilities.SupportsRecordings())
1644 return PVR_ERROR_NO_ERROR; // no error, but no need to obtain the values from the addon
1646 const CAddonRecording addonRecording(*recording);
1648 PVR_NAMED_VALUE** property_array{nullptr};
1649 unsigned int size{0};
1650 const PVR_ERROR error{addon->toAddon->GetRecordingStreamProperties(addon, &addonRecording,
1651 &property_array, &size)};
1652 if (error == PVR_ERROR_NO_ERROR)
1653 WriteStreamProperties(property_array, size, props);
1655 addon->toAddon->FreeProperties(addon, property_array, size);
1656 return error;
1660 PVR_ERROR CPVRClient::GetStreamProperties(PVR_STREAM_PROPERTIES* props) const
1662 return DoAddonCall(__func__, [&props](const AddonInstance* addon)
1663 { return addon->toAddon->GetStreamProperties(addon, props); });
1666 PVR_ERROR CPVRClient::StreamClosed() const
1668 return DoAddonCall(__func__, [](const AddonInstance* addon)
1669 { return addon->toAddon->StreamClosed(addon); });
1672 PVR_ERROR CPVRClient::DemuxReset()
1674 return DoAddonCall(
1675 __func__,
1676 [](const AddonInstance* addon)
1678 addon->toAddon->DemuxReset(addon);
1679 return PVR_ERROR_NO_ERROR;
1681 m_clientCapabilities.HandlesDemuxing());
1684 PVR_ERROR CPVRClient::DemuxAbort()
1686 return DoAddonCall(
1687 __func__,
1688 [](const AddonInstance* addon)
1690 addon->toAddon->DemuxAbort(addon);
1691 return PVR_ERROR_NO_ERROR;
1693 m_clientCapabilities.HandlesDemuxing());
1696 PVR_ERROR CPVRClient::DemuxFlush()
1698 return DoAddonCall(
1699 __func__,
1700 [](const AddonInstance* addon)
1702 addon->toAddon->DemuxFlush(addon);
1703 return PVR_ERROR_NO_ERROR;
1705 m_clientCapabilities.HandlesDemuxing());
1708 PVR_ERROR CPVRClient::DemuxRead(DemuxPacket*& packet)
1710 return DoAddonCall(
1711 __func__,
1712 [&packet](const AddonInstance* addon)
1714 packet = static_cast<DemuxPacket*>(addon->toAddon->DemuxRead(addon));
1715 return packet ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_IMPLEMENTED;
1717 m_clientCapabilities.HandlesDemuxing());
1720 const char* CPVRClient::ToString(const PVR_ERROR error)
1722 switch (error)
1724 case PVR_ERROR_NO_ERROR:
1725 return "no error";
1726 case PVR_ERROR_NOT_IMPLEMENTED:
1727 return "not implemented";
1728 case PVR_ERROR_SERVER_ERROR:
1729 return "server error";
1730 case PVR_ERROR_SERVER_TIMEOUT:
1731 return "server timeout";
1732 case PVR_ERROR_RECORDING_RUNNING:
1733 return "recording already running";
1734 case PVR_ERROR_ALREADY_PRESENT:
1735 return "already present";
1736 case PVR_ERROR_REJECTED:
1737 return "rejected by the backend";
1738 case PVR_ERROR_INVALID_PARAMETERS:
1739 return "invalid parameters for this method";
1740 case PVR_ERROR_FAILED:
1741 return "the command failed";
1742 case PVR_ERROR_UNKNOWN:
1743 default:
1744 return "unknown error";
1748 PVR_ERROR CPVRClient::DoAddonCall(const char* strFunctionName,
1749 const std::function<PVR_ERROR(const AddonInstance*)>& function,
1750 bool bIsImplemented /* = true */,
1751 bool bCheckReadyToUse /* = true */) const
1753 // Check preconditions.
1754 if (!bIsImplemented)
1755 return PVR_ERROR_NOT_IMPLEMENTED;
1757 if (m_bBlockAddonCalls)
1759 CLog::Log(LOGWARNING, "{}: Blocking call to add-on {}.", strFunctionName, GetID());
1760 return PVR_ERROR_SERVER_ERROR;
1763 if (bCheckReadyToUse && IgnoreClient())
1765 CLog::Log(LOGWARNING, "{}: Blocking call to add-on {}. Add-on not (yet) connected.",
1766 strFunctionName, GetID());
1767 return PVR_ERROR_SERVER_ERROR;
1770 if (bCheckReadyToUse && !ReadyToUse())
1772 CLog::Log(LOGWARNING, "{}: Blocking call to add-on {}. Add-on not ready to use.",
1773 strFunctionName, GetID());
1774 return PVR_ERROR_SERVER_ERROR;
1777 // Call.
1778 m_allAddonCallsFinished.Reset();
1779 m_iAddonCalls++;
1781 // CLog::LogFC(LOGDEBUG, LOGPVR, "Calling add-on function '{}' on client {}.", strFunctionName,
1782 // GetID());
1784 const PVR_ERROR error = function(m_ifc.pvr);
1786 // CLog::LogFC(LOGDEBUG, LOGPVR, "Called add-on function '{}' on client {}. return={}",
1787 // strFunctionName, GetID(), error);
1789 m_iAddonCalls--;
1790 if (m_iAddonCalls == 0)
1791 m_allAddonCallsFinished.Set();
1793 // Log error, if any.
1794 if (error != PVR_ERROR_NO_ERROR && error != PVR_ERROR_NOT_IMPLEMENTED)
1795 CLog::Log(LOGERROR, "{}: Add-on {} returned an error: {}", strFunctionName, GetID(),
1796 ToString(error));
1798 return error;
1801 bool CPVRClient::CanPlayChannel(const std::shared_ptr<const CPVRChannel>& channel) const
1803 return (m_bReadyToUse && ((m_clientCapabilities.SupportsTV() && !channel->IsRadio()) ||
1804 (m_clientCapabilities.SupportsRadio() && channel->IsRadio())));
1807 PVR_ERROR CPVRClient::OpenLiveStream(const std::shared_ptr<const CPVRChannel>& channel)
1809 if (!channel)
1810 return PVR_ERROR_INVALID_PARAMETERS;
1812 return DoAddonCall(__func__,
1813 [this, channel](const AddonInstance* addon)
1815 CloseLiveStream();
1817 if (!CanPlayChannel(channel))
1819 CLog::LogFC(LOGDEBUG, LOGPVR, "Add-on {} can not play channel '{}'",
1820 GetID(), channel->ChannelName());
1821 return PVR_ERROR_SERVER_ERROR;
1823 else
1825 CLog::LogFC(LOGDEBUG, LOGPVR, "Opening live stream for channel '{}'",
1826 channel->ChannelName());
1827 const CAddonChannel addonChannel{*channel};
1828 return addon->toAddon->OpenLiveStream(addon, &addonChannel)
1829 ? PVR_ERROR_NO_ERROR
1830 : PVR_ERROR_NOT_IMPLEMENTED;
1835 PVR_ERROR CPVRClient::OpenRecordedStream(const std::shared_ptr<const CPVRRecording>& recording,
1836 int64_t& streamId)
1838 if (!recording)
1839 return PVR_ERROR_INVALID_PARAMETERS;
1841 return DoAddonCall(
1842 __func__,
1843 [this, recording, &streamId](const AddonInstance* addon)
1845 if (!m_clientCapabilities.SupportsMultipleRecordedStreams())
1846 CloseRecordedStream(streamId);
1848 const CAddonRecording tag(*recording);
1849 CLog::LogFC(LOGDEBUG, LOGPVR, "Opening stream for recording '{}'", recording->m_strTitle);
1850 return addon->toAddon->OpenRecordedStream(addon, &tag, &streamId)
1851 ? PVR_ERROR_NO_ERROR
1852 : PVR_ERROR_NOT_IMPLEMENTED;
1854 m_clientCapabilities.SupportsRecordings());
1857 PVR_ERROR CPVRClient::CloseLiveStream()
1859 return DoAddonCall(__func__,
1860 [](const AddonInstance* addon)
1862 addon->toAddon->CloseLiveStream(addon);
1863 return PVR_ERROR_NO_ERROR;
1867 PVR_ERROR CPVRClient::CloseRecordedStream(int64_t streamId)
1869 return DoAddonCall(__func__,
1870 [streamId](const AddonInstance* addon)
1872 addon->toAddon->CloseRecordedStream(addon, streamId);
1873 return PVR_ERROR_NO_ERROR;
1877 PVR_ERROR CPVRClient::IsRecordedStreamRealTime(int64_t streamId, bool& isRealTime) const
1879 if (m_clientCapabilities.SupportsMultipleRecordedStreams())
1881 return DoAddonCall(
1882 __func__, [streamId, &isRealTime](const AddonInstance* addon)
1883 { return addon->toAddon->IsRecordedStreamRealTime(addon, streamId, &isRealTime); });
1885 else
1887 return IsRealTimeStream(isRealTime);
1891 PVR_ERROR CPVRClient::PauseRecordedStream(int64_t streamId, bool paused)
1893 if (m_clientCapabilities.SupportsMultipleRecordedStreams())
1895 return DoAddonCall(__func__, [streamId, paused](const AddonInstance* addon)
1896 { return addon->toAddon->PauseRecordedStream(addon, streamId, paused); });
1898 else
1900 return PauseStream(paused);
1904 PVR_ERROR CPVRClient::GetRecordedStreamTimes(int64_t streamId, PVR_STREAM_TIMES* times) const
1906 if (m_clientCapabilities.SupportsMultipleRecordedStreams())
1908 return DoAddonCall(__func__, [streamId, &times](const AddonInstance* addon)
1909 { return addon->toAddon->GetRecordedStreamTimes(addon, streamId, times); });
1911 else
1913 return GetStreamTimes(times);
1917 PVR_ERROR CPVRClient::PauseStream(bool bPaused)
1919 return DoAddonCall(__func__,
1920 [bPaused](const AddonInstance* addon)
1922 addon->toAddon->PauseStream(addon, bPaused);
1923 return PVR_ERROR_NO_ERROR;
1927 PVR_ERROR CPVRClient::SetSpeed(int speed)
1929 return DoAddonCall(__func__,
1930 [speed](const AddonInstance* addon)
1932 addon->toAddon->SetSpeed(addon, speed);
1933 return PVR_ERROR_NO_ERROR;
1937 PVR_ERROR CPVRClient::FillBuffer(bool mode)
1939 return DoAddonCall(__func__,
1940 [mode](const AddonInstance* addon)
1942 addon->toAddon->FillBuffer(addon, mode);
1943 return PVR_ERROR_NO_ERROR;
1947 PVR_ERROR CPVRClient::CanPauseStream(bool& bCanPause) const
1949 bCanPause = false;
1950 return DoAddonCall(__func__,
1951 [&bCanPause](const AddonInstance* addon)
1953 bCanPause = addon->toAddon->CanPauseStream(addon);
1954 return PVR_ERROR_NO_ERROR;
1958 PVR_ERROR CPVRClient::CanSeekStream(bool& bCanSeek) const
1960 bCanSeek = false;
1961 return DoAddonCall(__func__,
1962 [&bCanSeek](const AddonInstance* addon)
1964 bCanSeek = addon->toAddon->CanSeekStream(addon);
1965 return PVR_ERROR_NO_ERROR;
1969 PVR_ERROR CPVRClient::GetStreamTimes(PVR_STREAM_TIMES* times) const
1971 return DoAddonCall(__func__, [&times](const AddonInstance* addon)
1972 { return addon->toAddon->GetStreamTimes(addon, times); });
1975 PVR_ERROR CPVRClient::IsRealTimeStream(bool& bRealTime) const
1977 bRealTime = false;
1978 return DoAddonCall(__func__,
1979 [&bRealTime](const AddonInstance* addon)
1981 bRealTime = addon->toAddon->IsRealTimeStream(addon);
1982 return PVR_ERROR_NO_ERROR;
1986 PVR_ERROR CPVRClient::OnSystemSleep()
1988 return DoAddonCall(__func__, [](const AddonInstance* addon)
1989 { return addon->toAddon->OnSystemSleep(addon); });
1992 PVR_ERROR CPVRClient::OnSystemWake()
1994 return DoAddonCall(__func__, [](const AddonInstance* addon)
1995 { return addon->toAddon->OnSystemWake(addon); });
1998 PVR_ERROR CPVRClient::OnPowerSavingActivated()
2000 return DoAddonCall(__func__, [](const AddonInstance* addon)
2001 { return addon->toAddon->OnPowerSavingActivated(addon); });
2004 PVR_ERROR CPVRClient::OnPowerSavingDeactivated()
2006 return DoAddonCall(__func__, [](const AddonInstance* addon)
2007 { return addon->toAddon->OnPowerSavingDeactivated(addon); });
2010 std::shared_ptr<CPVRClientMenuHooks> CPVRClient::GetMenuHooks() const
2012 if (!m_menuhooks)
2013 m_menuhooks = std::make_shared<CPVRClientMenuHooks>(ID());
2015 return m_menuhooks;
2018 PVR_ERROR CPVRClient::CallEpgTagMenuHook(const CPVRClientMenuHook& hook,
2019 const std::shared_ptr<const CPVREpgInfoTag>& tag)
2021 return DoAddonCall(__func__,
2022 [&hook, &tag](const AddonInstance* addon)
2024 CAddonEpgTag addonTag(*tag);
2026 PVR_MENUHOOK menuHook;
2027 menuHook.category = PVR_MENUHOOK_EPG;
2028 menuHook.iHookId = hook.GetId();
2029 menuHook.iLocalizedStringId = hook.GetLabelId();
2031 return addon->toAddon->CallEPGMenuHook(addon, &menuHook, &addonTag);
2035 PVR_ERROR CPVRClient::CallChannelMenuHook(const CPVRClientMenuHook& hook,
2036 const std::shared_ptr<const CPVRChannel>& channel)
2038 return DoAddonCall(__func__,
2039 [&hook, &channel](const AddonInstance* addon)
2041 const CAddonChannel addonChannel{*channel};
2043 PVR_MENUHOOK menuHook;
2044 menuHook.category = PVR_MENUHOOK_CHANNEL;
2045 menuHook.iHookId = hook.GetId();
2046 menuHook.iLocalizedStringId = hook.GetLabelId();
2048 return addon->toAddon->CallChannelMenuHook(addon, &menuHook, &addonChannel);
2052 PVR_ERROR CPVRClient::CallRecordingMenuHook(const CPVRClientMenuHook& hook,
2053 const std::shared_ptr<const CPVRRecording>& recording,
2054 bool bDeleted)
2056 return DoAddonCall(__func__,
2057 [&hook, &recording, &bDeleted](const AddonInstance* addon)
2059 const CAddonRecording tag(*recording);
2061 PVR_MENUHOOK menuHook;
2062 menuHook.category =
2063 bDeleted ? PVR_MENUHOOK_DELETED_RECORDING : PVR_MENUHOOK_RECORDING;
2064 menuHook.iHookId = hook.GetId();
2065 menuHook.iLocalizedStringId = hook.GetLabelId();
2067 return addon->toAddon->CallRecordingMenuHook(addon, &menuHook, &tag);
2071 PVR_ERROR CPVRClient::CallTimerMenuHook(const CPVRClientMenuHook& hook,
2072 const std::shared_ptr<const CPVRTimerInfoTag>& timer)
2074 return DoAddonCall(__func__,
2075 [&hook, &timer](const AddonInstance* addon)
2077 const CAddonTimer tag(*timer);
2079 PVR_MENUHOOK menuHook;
2080 menuHook.category = PVR_MENUHOOK_TIMER;
2081 menuHook.iHookId = hook.GetId();
2082 menuHook.iLocalizedStringId = hook.GetLabelId();
2084 return addon->toAddon->CallTimerMenuHook(addon, &menuHook, &tag);
2088 PVR_ERROR CPVRClient::CallSettingsMenuHook(const CPVRClientMenuHook& hook)
2090 return DoAddonCall(__func__,
2091 [&hook](const AddonInstance* addon)
2093 PVR_MENUHOOK menuHook;
2094 menuHook.category = PVR_MENUHOOK_SETTING;
2095 menuHook.iHookId = hook.GetId();
2096 menuHook.iLocalizedStringId = hook.GetLabelId();
2098 return addon->toAddon->CallSettingsMenuHook(addon, &menuHook);
2102 void CPVRClient::SetPriority(int iPriority)
2104 std::unique_lock<CCriticalSection> lock(m_critSection);
2105 if (m_priority != iPriority)
2107 m_priority = iPriority;
2108 if (m_iClientId != PVR_CLIENT_INVALID_UID)
2110 CServiceBroker::GetPVRManager().GetTVDatabase()->Persist(*this);
2112 CServiceBroker::GetPVRManager().PublishEvent(PVREvent::ClientsPrioritiesInvalidated);
2116 int CPVRClient::GetPriority() const
2118 std::unique_lock<CCriticalSection> lock(m_critSection);
2119 if (!m_priority.has_value() && m_iClientId != PVR_CLIENT_INVALID_UID)
2121 m_priority = CServiceBroker::GetPVRManager().GetTVDatabase()->GetPriority(*this);
2123 return *m_priority;
2126 const CDateTime& CPVRClient::GetDateTimeFirstChannelsAdded() const
2128 std::unique_lock<CCriticalSection> lock(m_critSection);
2129 if (!m_firstChannelsAdded.has_value() && m_iClientId != PVR_CLIENT_INVALID_UID)
2131 m_firstChannelsAdded =
2132 CServiceBroker::GetPVRManager().GetTVDatabase()->GetDateTimeFirstChannelsAdded(*this);
2134 return *m_firstChannelsAdded;
2137 void CPVRClient::SetDateTimeFirstChannelsAdded(const CDateTime& dateTime)
2139 std::unique_lock<CCriticalSection> lock(m_critSection);
2140 if (m_firstChannelsAdded != dateTime)
2142 m_firstChannelsAdded = dateTime;
2143 if (m_iClientId != PVR_CLIENT_INVALID_UID)
2145 CServiceBroker::GetPVRManager().GetTVDatabase()->Persist(*this);
2150 void CPVRClient::HandleAddonCallback(const char* strFunctionName,
2151 void* kodiInstance,
2152 const std::function<void(CPVRClient* client)>& function,
2153 bool bForceCall /* = false */)
2155 // Check preconditions.
2156 CPVRClient* client = static_cast<CPVRClient*>(kodiInstance);
2157 if (!client)
2159 CLog::Log(LOGERROR, "{}: No instance pointer given!", strFunctionName);
2160 return;
2163 if (!bForceCall && client->m_bBlockAddonCalls && client->m_iAddonCalls == 0)
2165 CLog::Log(LOGWARNING, LOGPVR, "{}: Ignoring callback from PVR client '{}'", strFunctionName,
2166 client->ID());
2167 return;
2170 // Call.
2171 function(client);
2174 void CPVRClient::cb_transfer_channel_group(void* kodiInstance,
2175 const PVR_HANDLE handle,
2176 const PVR_CHANNEL_GROUP* group)
2178 HandleAddonCallback(__func__, kodiInstance,
2179 [&](CPVRClient* client)
2181 if (!handle || !group)
2183 CLog::LogF(LOGERROR, "Invalid callback parameter(s)");
2184 return;
2187 if (strlen(group->strGroupName) == 0)
2189 CLog::LogF(LOGERROR, "Empty group name");
2190 return;
2193 // transfer this entry to the groups container
2194 CPVRChannelGroups* kodiGroups =
2195 static_cast<CPVRChannelGroups*>(handle->dataAddress);
2196 const auto transferGroup = kodiGroups->GetGroupFactory()->CreateClientGroup(
2197 *group, client->GetID(), kodiGroups->GetGroupAll());
2198 kodiGroups->UpdateFromClient(transferGroup);
2202 void CPVRClient::cb_transfer_channel_group_member(void* kodiInstance,
2203 const PVR_HANDLE handle,
2204 const PVR_CHANNEL_GROUP_MEMBER* member)
2206 HandleAddonCallback(__func__, kodiInstance,
2207 [&](CPVRClient* client)
2209 if (!handle || !member)
2211 CLog::LogF(LOGERROR, "Invalid callback parameter(s)");
2212 return;
2215 const std::shared_ptr<CPVRChannel> channel =
2216 CServiceBroker::GetPVRManager().ChannelGroups()->GetByUniqueID(
2217 member->iChannelUniqueId, client->GetID());
2218 if (!channel)
2220 CLog::LogF(LOGERROR, "Cannot find group '{}' or channel '{}'",
2221 member->strGroupName, member->iChannelUniqueId);
2223 else
2225 auto* groupMembers =
2226 static_cast<std::vector<std::shared_ptr<CPVRChannelGroupMember>>*>(
2227 handle->dataAddress);
2228 groupMembers->emplace_back(std::make_shared<CPVRChannelGroupMember>(
2229 member->strGroupName, client->GetID(), member->iOrder, channel));
2234 void CPVRClient::cb_transfer_epg_entry(void* kodiInstance,
2235 const PVR_HANDLE handle,
2236 const EPG_TAG* epgentry)
2238 HandleAddonCallback(__func__, kodiInstance,
2239 [&](CPVRClient* client)
2241 if (!handle || !epgentry)
2243 CLog::LogF(LOGERROR, "Invalid callback parameter(s)");
2244 return;
2247 // transfer this entry to the epg
2248 CPVREpg* epg = static_cast<CPVREpg*>(handle->dataAddress);
2249 epg->UpdateEntry(epgentry, client->GetID());
2253 void CPVRClient::cb_transfer_provider_entry(void* kodiInstance,
2254 const PVR_HANDLE handle,
2255 const PVR_PROVIDER* provider)
2257 if (!handle)
2259 CLog::LogF(LOGERROR, "Invalid handler data");
2260 return;
2263 CPVRClient* client = static_cast<CPVRClient*>(kodiInstance);
2264 CPVRProvidersContainer* kodiProviders = static_cast<CPVRProvidersContainer*>(handle->dataAddress);
2265 if (!provider || !client || !kodiProviders)
2267 CLog::LogF(LOGERROR, "Invalid handler data");
2268 return;
2271 /* transfer this entry to the internal channels group */
2272 std::shared_ptr<CPVRProvider> transferProvider(
2273 std::make_shared<CPVRProvider>(*provider, client->GetID()));
2274 kodiProviders->UpdateFromClient(transferProvider);
2277 void CPVRClient::cb_transfer_channel_entry(void* kodiInstance,
2278 const PVR_HANDLE handle,
2279 const PVR_CHANNEL* channel)
2281 HandleAddonCallback(
2282 __func__, kodiInstance,
2283 [&](CPVRClient* client)
2285 if (!handle || !channel)
2287 CLog::LogF(LOGERROR, "Invalid callback parameter(s)");
2288 return;
2291 auto* channels =
2292 static_cast<std::vector<std::shared_ptr<CPVRChannel>>*>(handle->dataAddress);
2293 channels->emplace_back(std::make_shared<CPVRChannel>(*channel, client->GetID()));
2297 void CPVRClient::cb_transfer_recording_entry(void* kodiInstance,
2298 const PVR_HANDLE handle,
2299 const PVR_RECORDING* recording)
2301 HandleAddonCallback(__func__, kodiInstance,
2302 [&](CPVRClient* client)
2304 if (!handle || !recording)
2306 CLog::LogF(LOGERROR, "Invalid callback parameter(s)");
2307 return;
2310 // transfer this entry to the recordings container
2311 const std::shared_ptr<CPVRRecording> transferRecording =
2312 std::make_shared<CPVRRecording>(*recording, client->GetID());
2313 CPVRRecordings* recordings =
2314 static_cast<CPVRRecordings*>(handle->dataAddress);
2315 recordings->UpdateFromClient(transferRecording, *client);
2319 void CPVRClient::cb_transfer_timer_entry(void* kodiInstance,
2320 const PVR_HANDLE handle,
2321 const PVR_TIMER* timer)
2323 HandleAddonCallback(__func__, kodiInstance,
2324 [&](CPVRClient* client)
2326 if (!handle || !timer)
2328 CLog::LogF(LOGERROR, "Invalid callback parameter(s)");
2329 return;
2332 // Note: channel can be nullptr here, for instance for epg-based timer rules
2333 // ("record on any channel" condition)
2334 const std::shared_ptr<CPVRChannel> channel =
2335 CServiceBroker::GetPVRManager().ChannelGroups()->GetByUniqueID(
2336 timer->iClientChannelUid, client->GetID());
2338 // transfer this entry to the timers container
2339 const std::shared_ptr<CPVRTimerInfoTag> transferTimer =
2340 std::make_shared<CPVRTimerInfoTag>(*timer, channel, client->GetID());
2341 CPVRTimersContainer* timers =
2342 static_cast<CPVRTimersContainer*>(handle->dataAddress);
2343 timers->UpdateFromClient(transferTimer);
2347 void CPVRClient::cb_add_menu_hook(void* kodiInstance, const PVR_MENUHOOK* hook)
2349 HandleAddonCallback(__func__, kodiInstance,
2350 [&](CPVRClient* client)
2352 if (!hook)
2354 CLog::LogF(LOGERROR, "Invalid callback parameter(s)");
2355 return;
2358 client->GetMenuHooks()->AddHook(*hook);
2362 void CPVRClient::cb_recording_notification(void* kodiInstance,
2363 const char* strName,
2364 const char* strFileName,
2365 bool bOnOff)
2367 HandleAddonCallback(
2368 __func__, kodiInstance,
2369 [&](CPVRClient* client)
2371 if (!strFileName)
2373 CLog::LogF(LOGERROR, "Invalid callback parameter(s)");
2374 return;
2377 const std::string strLine1 = StringUtils::Format(
2378 g_localizeStrings.Get(bOnOff ? 19197 : 19198), client->GetFullClientName());
2379 std::string strLine2;
2380 if (strName)
2381 strLine2 = strName;
2382 else
2383 strLine2 = strFileName;
2385 // display a notification for 5 seconds
2386 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, strLine1, strLine2, 5000,
2387 false);
2388 auto eventLog = CServiceBroker::GetEventLog();
2389 if (eventLog)
2390 eventLog->Add(EventPtr(new CNotificationEvent(client->GetFullClientName(), strLine1,
2391 client->Icon(), strLine2)));
2393 CLog::LogFC(LOGDEBUG, LOGPVR, "Recording {} on client {}. name='{}' filename='{}'",
2394 bOnOff ? "started" : "finished", client->GetID(), strName, strFileName);
2398 void CPVRClient::cb_trigger_channel_update(void* kodiInstance)
2400 HandleAddonCallback(__func__, kodiInstance,
2401 [&](CPVRClient* client)
2403 // update channels in the next iteration of the pvrmanager's main loop
2404 CServiceBroker::GetPVRManager().TriggerChannelsUpdate(client->GetID());
2408 void CPVRClient::cb_trigger_provider_update(void* kodiInstance)
2410 HandleAddonCallback(__func__, kodiInstance,
2411 [&](CPVRClient* client)
2413 /* update the providers table in the next iteration of the pvrmanager's main loop */
2414 CServiceBroker::GetPVRManager().TriggerProvidersUpdate(client->GetID());
2418 void CPVRClient::cb_trigger_timer_update(void* kodiInstance)
2420 HandleAddonCallback(__func__, kodiInstance,
2421 [&](CPVRClient* client)
2423 // update timers in the next iteration of the pvrmanager's main loop
2424 CServiceBroker::GetPVRManager().TriggerTimersUpdate(client->GetID());
2428 void CPVRClient::cb_trigger_recording_update(void* kodiInstance)
2430 HandleAddonCallback(__func__, kodiInstance,
2431 [&](CPVRClient* client)
2433 // update recordings in the next iteration of the pvrmanager's main loop
2434 CServiceBroker::GetPVRManager().TriggerRecordingsUpdate(client->GetID());
2438 void CPVRClient::cb_trigger_channel_groups_update(void* kodiInstance)
2440 HandleAddonCallback(__func__, kodiInstance,
2441 [&](CPVRClient* client)
2443 // update all channel groups in the next iteration of the pvrmanager's main loop
2444 CServiceBroker::GetPVRManager().TriggerChannelGroupsUpdate(client->GetID());
2448 void CPVRClient::cb_trigger_epg_update(void* kodiInstance, unsigned int iChannelUid)
2450 HandleAddonCallback(__func__, kodiInstance,
2451 [&](CPVRClient* client) {
2452 CServiceBroker::GetPVRManager().EpgContainer().UpdateRequest(
2453 client->GetID(), iChannelUid);
2457 void CPVRClient::cb_free_demux_packet(void* kodiInstance, DEMUX_PACKET* pPacket)
2459 HandleAddonCallback(
2460 __func__, kodiInstance,
2461 [&](CPVRClient* client)
2462 { CDVDDemuxUtils::FreeDemuxPacket(static_cast<DemuxPacket*>(pPacket)); },
2463 true);
2466 DEMUX_PACKET* CPVRClient::cb_allocate_demux_packet(void* kodiInstance, int iDataSize)
2468 DEMUX_PACKET* result = nullptr;
2470 HandleAddonCallback(
2471 __func__, kodiInstance,
2472 [&](CPVRClient* client) { result = CDVDDemuxUtils::AllocateDemuxPacket(iDataSize); }, true);
2474 return result;
2477 void CPVRClient::cb_connection_state_change(void* kodiInstance,
2478 const char* strConnectionString,
2479 PVR_CONNECTION_STATE newState,
2480 const char* strMessage)
2482 HandleAddonCallback(__func__, kodiInstance,
2483 [&](CPVRClient* client)
2485 if (!strConnectionString)
2487 CLog::LogF(LOGERROR, "Invalid callback parameter(s)");
2488 return;
2491 const PVR_CONNECTION_STATE prevState(client->GetConnectionState());
2492 if (prevState == newState)
2493 return;
2495 CLog::LogFC(LOGDEBUG, LOGPVR,
2496 "Connection state for client {} changed from {} to {}",
2497 client->GetID(), prevState, newState);
2499 client->SetConnectionState(newState);
2501 std::string msg;
2502 if (strMessage)
2503 msg = strMessage;
2505 CServiceBroker::GetPVRManager().ConnectionStateChange(
2506 client, std::string(strConnectionString), newState, msg);
2510 void CPVRClient::cb_epg_event_state_change(void* kodiInstance,
2511 EPG_TAG* tag,
2512 EPG_EVENT_STATE newState)
2514 HandleAddonCallback(__func__, kodiInstance,
2515 [&](CPVRClient* client)
2517 if (!tag)
2519 CLog::LogF(LOGERROR, "Invalid callback parameter(s)");
2520 return;
2523 // Note: channel data and epg id may not yet be available. Tag will be fully initialized later.
2524 const std::shared_ptr<CPVREpgInfoTag> epgTag =
2525 std::make_shared<CPVREpgInfoTag>(*tag, client->GetID(), nullptr, -1);
2526 CServiceBroker::GetPVRManager().EpgContainer().UpdateFromClient(epgTag,
2527 newState);
2531 class CCodecIds
2533 public:
2534 virtual ~CCodecIds() = default;
2536 static CCodecIds& GetInstance()
2538 static CCodecIds _instance;
2539 return _instance;
2542 PVR_CODEC GetCodecByName(const char* strCodecName)
2544 PVR_CODEC retVal = PVR_INVALID_CODEC;
2545 if (strlen(strCodecName) == 0)
2546 return retVal;
2548 std::string strUpperCodecName = strCodecName;
2549 StringUtils::ToUpper(strUpperCodecName);
2551 std::map<std::string, PVR_CODEC>::const_iterator it = m_lookup.find(strUpperCodecName);
2552 if (it != m_lookup.end())
2553 retVal = it->second;
2555 return retVal;
2558 private:
2559 CCodecIds()
2561 // get ids and names
2562 const AVCodec* codec = nullptr;
2563 void* i = nullptr;
2564 PVR_CODEC tmp;
2565 while ((codec = av_codec_iterate(&i)))
2567 if (av_codec_is_decoder(codec))
2569 tmp.codec_type = static_cast<PVR_CODEC_TYPE>(codec->type);
2570 tmp.codec_id = codec->id;
2572 std::string strUpperCodecName = codec->name;
2573 StringUtils::ToUpper(strUpperCodecName);
2575 m_lookup.insert(std::make_pair(strUpperCodecName, tmp));
2579 // teletext is not returned by av_codec_next. we got our own decoder
2580 tmp.codec_type = PVR_CODEC_TYPE_SUBTITLE;
2581 tmp.codec_id = AV_CODEC_ID_DVB_TELETEXT;
2582 m_lookup.insert(std::make_pair("TELETEXT", tmp));
2584 // rds is not returned by av_codec_next. we got our own decoder
2585 tmp.codec_type = PVR_CODEC_TYPE_RDS;
2586 tmp.codec_id = AV_CODEC_ID_NONE;
2587 m_lookup.insert(std::make_pair("RDS", tmp));
2589 // ID3 is not returned by av_codec_next. we got our own decoder
2590 tmp.codec_type = PVR_CODEC_TYPE_ID3;
2591 tmp.codec_id = AV_CODEC_ID_NONE;
2592 m_lookup.insert({"ID3", tmp});
2595 std::map<std::string, PVR_CODEC> m_lookup;
2598 PVR_CODEC CPVRClient::cb_get_codec_by_name(const void* kodiInstance, const char* strCodecName)
2600 PVR_CODEC result = PVR_INVALID_CODEC;
2602 HandleAddonCallback(
2603 __func__, const_cast<void*>(kodiInstance),
2604 [&](CPVRClient* client) { result = CCodecIds::GetInstance().GetCodecByName(strCodecName); },
2605 true);
2607 return result;
2610 } // namespace PVR