Merge pull request #3392 from garbear/pvrmenu.searchicons
[vuplus_xbmc] / xbmc / pvr / addons / PVRClients.cpp
1 /*
2  *      Copyright (C) 2012-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "PVRClients.h"
22
23 #include "Application.h"
24 #include "ApplicationMessenger.h"
25 #include "GUIUserMessages.h"
26 #include "dialogs/GUIDialogOK.h"
27 #include "dialogs/GUIDialogSelect.h"
28 #include "pvr/PVRManager.h"
29 #include "pvr/PVRDatabase.h"
30 #include "guilib/GUIWindowManager.h"
31 #include "settings/DisplaySettings.h"
32 #include "settings/MediaSettings.h"
33 #include "settings/Settings.h"
34 #include "pvr/channels/PVRChannelGroups.h"
35 #include "pvr/channels/PVRChannelGroupInternal.h"
36 #include "pvr/recordings/PVRRecordings.h"
37 #include "pvr/timers/PVRTimers.h"
38 #include "cores/IPlayer.h"
39
40 #ifdef HAS_VIDEO_PLAYBACK
41 #include "cores/VideoRenderers/RenderManager.h"
42 #endif
43
44 using namespace std;
45 using namespace ADDON;
46 using namespace PVR;
47 using namespace EPG;
48
49 CPVRClients::CPVRClients(void) :
50     CThread("PVRClient"),
51     m_bChannelScanRunning(false),
52     m_bIsSwitchingChannels(false),
53     m_bIsValidChannelSettings(false),
54     m_playingClientId(-EINVAL),
55     m_bIsPlayingLiveTV(false),
56     m_bIsPlayingRecording(false),
57     m_scanStart(0),
58     m_bNoAddonWarningDisplayed(false)
59 {
60 }
61
62 CPVRClients::~CPVRClients(void)
63 {
64   Unload();
65 }
66
67 bool CPVRClients::IsInUse(const std::string& strAddonId) const
68 {
69   CSingleLock lock(m_critSection);
70
71   for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
72     if (itr->second->Enabled() && itr->second->ID().Equals(strAddonId.c_str()))
73       return true;
74   return false;
75 }
76
77 void CPVRClients::Start(void)
78 {
79   Stop();
80
81   m_addonDb.Open();
82   Create();
83   SetPriority(-1);
84 }
85
86 void CPVRClients::Stop(void)
87 {
88   StopThread();
89   m_addonDb.Close();
90 }
91
92 bool CPVRClients::IsConnectedClient(int iClientId) const
93 {
94   PVR_CLIENT client;
95   return GetConnectedClient(iClientId, client);
96 }
97
98 bool CPVRClients::IsConnectedClient(const AddonPtr addon)
99 {
100   for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
101     if (itr->second->ID() == addon->ID())
102       return itr->second->ReadyToUse();
103   return false;
104 }
105
106 int CPVRClients::GetClientId(const AddonPtr client) const
107 {
108   CSingleLock lock(m_critSection);
109
110   for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
111     if (itr->second->ID() == client->ID())
112       return itr->first;
113
114   return -1;
115 }
116
117 bool CPVRClients::GetClient(int iClientId, PVR_CLIENT &addon) const
118 {
119   bool bReturn(false);
120   if (iClientId <= PVR_INVALID_CLIENT_ID || iClientId == PVR_VIRTUAL_CLIENT_ID)
121     return bReturn;
122
123   CSingleLock lock(m_critSection);
124
125   PVR_CLIENTMAP_CITR itr = m_clientMap.find(iClientId);
126   if (itr != m_clientMap.end())
127   {
128     addon = itr->second;
129     bReturn = true;
130   }
131
132   return bReturn;
133 }
134
135 bool CPVRClients::GetConnectedClient(int iClientId, PVR_CLIENT &addon) const
136 {
137   if (GetClient(iClientId, addon))
138     return addon->ReadyToUse();
139   return false;
140 }
141
142 bool CPVRClients::RequestRestart(AddonPtr addon, bool bDataChanged)
143 {
144   return StopClient(addon, true);
145 }
146
147 bool CPVRClients::RequestRemoval(AddonPtr addon)
148 {
149   return StopClient(addon, false);
150 }
151
152 void CPVRClients::Unload(void)
153 {
154   Stop();
155
156   CSingleLock lock(m_critSection);
157
158   /* destroy all clients */
159   for (PVR_CLIENTMAP_ITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
160     itr->second->Destroy();
161
162   /* reset class properties */
163   m_bChannelScanRunning  = false;
164   m_bIsPlayingLiveTV     = false;
165   m_bIsPlayingRecording  = false;
166   m_strPlayingClientName = "";
167
168   m_clientMap.clear();
169 }
170
171 int CPVRClients::GetFirstConnectedClientID(void)
172 {
173   CSingleLock lock(m_critSection);
174
175   for (PVR_CLIENTMAP_ITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
176     if (itr->second->ReadyToUse())
177       return itr->second->GetID();
178
179   return -1;
180 }
181
182 int CPVRClients::EnabledClientAmount(void) const
183 {
184   int iReturn(0);
185   CSingleLock lock(m_critSection);
186
187   for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
188     if (itr->second->Enabled())
189       ++iReturn;
190
191   return iReturn;
192 }
193
194 bool CPVRClients::HasEnabledClients(void) const
195 {
196   return EnabledClientAmount() > 0;
197 }
198
199 bool CPVRClients::StopClient(AddonPtr client, bool bRestart)
200 {
201   CSingleLock lock(m_critSection);  
202   int iId = GetClientId(client);
203   PVR_CLIENT mappedClient;
204   if (GetConnectedClient(iId, mappedClient))
205   {
206     if (bRestart)
207       mappedClient->ReCreate();
208     else
209       mappedClient->Destroy();
210
211     return true;
212   }
213
214   return false;
215 }
216
217 int CPVRClients::ConnectedClientAmount(void) const
218 {
219   int iReturn(0);
220   CSingleLock lock(m_critSection);
221
222   for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
223     if (itr->second->ReadyToUse())
224       ++iReturn;
225
226   return iReturn;
227 }
228
229 bool CPVRClients::HasConnectedClients(void) const
230 {
231   CSingleLock lock(m_critSection);
232
233   for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
234     if (itr->second->ReadyToUse())
235       return true;
236
237   return false;
238 }
239
240 bool CPVRClients::GetClientName(int iClientId, CStdString &strName) const
241 {
242   bool bReturn(false);
243   PVR_CLIENT client;
244   if ((bReturn = GetConnectedClient(iClientId, client)) == true)
245     strName = client->GetFriendlyName();
246
247   return bReturn;
248 }
249
250 int CPVRClients::GetConnectedClients(PVR_CLIENTMAP &clients) const
251 {
252   int iReturn(0);
253   CSingleLock lock(m_critSection);
254
255   for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
256   {
257     if (itr->second->ReadyToUse())
258     {
259       clients.insert(std::make_pair(itr->second->GetID(), itr->second));
260       ++iReturn;
261     }
262   }
263
264   return iReturn;
265 }
266
267 int CPVRClients::GetPlayingClientID(void) const
268 {
269   CSingleLock lock(m_critSection);
270
271   if (m_bIsPlayingLiveTV || m_bIsPlayingRecording)
272     return m_playingClientId;
273   return -EINVAL;
274 }
275
276 const CStdString CPVRClients::GetPlayingClientName(void) const
277 {
278   CSingleLock lock(m_critSection);
279   return m_strPlayingClientName;
280 }
281
282 CStdString CPVRClients::GetStreamURL(const CPVRChannel &tag)
283 {
284   CStdString strReturn;
285   PVR_CLIENT client;
286   if (GetConnectedClient(tag.ClientID(), client))
287     strReturn = client->GetLiveStreamURL(tag);
288   else
289     CLog::Log(LOGERROR, "PVR - %s - cannot find client %d",__FUNCTION__, tag.ClientID());
290
291   return strReturn;
292 }
293
294 bool CPVRClients::SwitchChannel(const CPVRChannel &channel)
295 {
296   {
297     CSingleLock lock(m_critSection);
298     if (m_bIsSwitchingChannels)
299     {
300       CLog::Log(LOGDEBUG, "PVRClients - %s - can't switch to channel '%s'. waiting for the previous switch to complete", __FUNCTION__, channel.ChannelName().c_str());
301       return false;
302     }
303     m_bIsSwitchingChannels = true;
304   }
305
306   bool bSwitchSuccessful(false);
307   CPVRChannelPtr currentChannel;
308   if (// no channel is currently playing
309       !GetPlayingChannel(currentChannel) ||
310       // different backend
311       currentChannel->ClientID() != channel.ClientID() ||
312       // stream URL should always be opened as a new file
313       !channel.StreamURL().IsEmpty() || !currentChannel->StreamURL().IsEmpty())
314   {
315     if (channel.StreamURL().IsEmpty())
316     {
317       CloseStream();
318       bSwitchSuccessful = OpenStream(channel, true);
319     }
320     else
321     {
322       CFileItem m_currentFile(channel);
323       CApplicationMessenger::Get().PlayFile(m_currentFile, false);
324       bSwitchSuccessful = true;
325     }
326   }
327   // same channel
328   else if (currentChannel.get() && *currentChannel == channel)
329   {
330     bSwitchSuccessful = true;
331   }
332   else
333   {
334     PVR_CLIENT client;
335     if (GetConnectedClient(channel.ClientID(), client))
336       bSwitchSuccessful = client->SwitchChannel(channel);
337   }
338
339   {
340     CSingleLock lock(m_critSection);
341     m_bIsSwitchingChannels = false;
342     if (bSwitchSuccessful)
343       m_bIsValidChannelSettings = false;
344   }
345
346   if (!bSwitchSuccessful)
347     CLog::Log(LOGERROR, "PVR - %s - cannot switch to channel '%s' on client '%d'",__FUNCTION__, channel.ChannelName().c_str(), channel.ClientID());
348
349   return bSwitchSuccessful;
350 }
351
352 bool CPVRClients::GetPlayingChannel(CPVRChannelPtr &channel) const
353 {
354   PVR_CLIENT client;
355   if (GetPlayingClient(client))
356     return client->GetPlayingChannel(channel);
357   return false;
358 }
359
360 bool CPVRClients::GetPlayingRecording(CPVRRecording &recording) const
361 {
362   PVR_CLIENT client;
363   if (GetPlayingClient(client))
364     return client->GetPlayingRecording(recording);
365   return false;
366 }
367
368 bool CPVRClients::HasTimerSupport(int iClientId)
369 {
370   CSingleLock lock(m_critSection);
371
372   return IsConnectedClient(iClientId) && m_clientMap[iClientId]->SupportsTimers();
373 }
374
375 PVR_ERROR CPVRClients::GetTimers(CPVRTimers *timers)
376 {
377   PVR_ERROR error(PVR_ERROR_NO_ERROR);
378   PVR_CLIENTMAP clients;
379   GetConnectedClients(clients);
380
381   /* get the timer list from each client */
382   for (PVR_CLIENTMAP_ITR itrClients = clients.begin(); itrClients != clients.end(); itrClients++)
383   {
384     PVR_ERROR currentError = (*itrClients).second->GetTimers(timers);
385     if (currentError != PVR_ERROR_NOT_IMPLEMENTED &&
386         currentError != PVR_ERROR_NO_ERROR)
387     {
388       CLog::Log(LOGERROR, "PVR - %s - cannot get timers from client '%d': %s",__FUNCTION__, (*itrClients).first, CPVRClient::ToString(currentError));
389       error = currentError;
390     }
391   }
392
393   return error;
394 }
395
396 PVR_ERROR CPVRClients::AddTimer(const CPVRTimerInfoTag &timer)
397 {
398   PVR_ERROR error(PVR_ERROR_UNKNOWN);
399
400   PVR_CLIENT client;
401   if (GetConnectedClient(timer.m_iClientId, client))
402     error = client->AddTimer(timer);
403
404   if (error != PVR_ERROR_NO_ERROR)
405     CLog::Log(LOGERROR, "PVR - %s - cannot add timer to client '%d': %s",__FUNCTION__, timer.m_iClientId, CPVRClient::ToString(error));
406
407   return error;
408 }
409
410 PVR_ERROR CPVRClients::UpdateTimer(const CPVRTimerInfoTag &timer)
411 {
412   PVR_ERROR error(PVR_ERROR_UNKNOWN);
413
414   PVR_CLIENT client;
415   if (GetConnectedClient(timer.m_iClientId, client))
416     error = client->UpdateTimer(timer);
417
418   if (error != PVR_ERROR_NO_ERROR)
419     CLog::Log(LOGERROR, "PVR - %s - cannot update timer on client '%d': %s",__FUNCTION__, timer.m_iClientId, CPVRClient::ToString(error));
420
421   return error;
422 }
423
424 PVR_ERROR CPVRClients::DeleteTimer(const CPVRTimerInfoTag &timer, bool bForce)
425 {
426   PVR_ERROR error(PVR_ERROR_UNKNOWN);
427   PVR_CLIENT client;
428
429   if (GetConnectedClient(timer.m_iClientId, client))
430     error = client->DeleteTimer(timer, bForce);
431
432   return error;
433 }
434
435 PVR_ERROR CPVRClients::RenameTimer(const CPVRTimerInfoTag &timer, const CStdString &strNewName)
436 {
437   PVR_ERROR error(PVR_ERROR_UNKNOWN);
438
439   PVR_CLIENT client;
440   if (GetConnectedClient(timer.m_iClientId, client))
441     error = client->RenameTimer(timer, strNewName);
442
443   if (error != PVR_ERROR_NO_ERROR)
444     CLog::Log(LOGERROR, "PVR - %s - cannot rename timer on client '%d': %s",__FUNCTION__, timer.m_iClientId, CPVRClient::ToString(error));
445
446   return error;
447 }
448
449 PVR_ERROR CPVRClients::GetRecordings(CPVRRecordings *recordings)
450 {
451   PVR_ERROR error(PVR_ERROR_NO_ERROR);
452   PVR_CLIENTMAP clients;
453   GetConnectedClients(clients);
454
455   for (PVR_CLIENTMAP_ITR itrClients = clients.begin(); itrClients != clients.end(); itrClients++)
456   {
457     PVR_ERROR currentError = (*itrClients).second->GetRecordings(recordings);
458     if (currentError != PVR_ERROR_NOT_IMPLEMENTED &&
459         currentError != PVR_ERROR_NO_ERROR)
460     {
461       CLog::Log(LOGERROR, "PVR - %s - cannot get recordings from client '%d': %s",__FUNCTION__, (*itrClients).first, CPVRClient::ToString(currentError));
462       error = currentError;
463     }
464   }
465
466   return error;
467 }
468
469 PVR_ERROR CPVRClients::RenameRecording(const CPVRRecording &recording)
470 {
471   PVR_ERROR error(PVR_ERROR_UNKNOWN);
472
473   PVR_CLIENT client;
474   if (GetConnectedClient(recording.m_iClientId, client))
475     error = client->RenameRecording(recording);
476
477   if (error != PVR_ERROR_NO_ERROR)
478     CLog::Log(LOGERROR, "PVR - %s - cannot rename recording on client '%d': %s",__FUNCTION__, recording.m_iClientId, CPVRClient::ToString(error));
479
480   return error;
481 }
482
483 PVR_ERROR CPVRClients::DeleteRecording(const CPVRRecording &recording)
484 {
485   PVR_ERROR error(PVR_ERROR_UNKNOWN);
486
487   PVR_CLIENT client;
488   if (GetConnectedClient(recording.m_iClientId, client))
489     error = client->DeleteRecording(recording);
490
491   if (error != PVR_ERROR_NO_ERROR)
492     CLog::Log(LOGERROR, "PVR - %s - cannot delete recording from client '%d': %s",__FUNCTION__, recording.m_iClientId, CPVRClient::ToString(error));
493
494   return error;
495 }
496
497 bool CPVRClients::SetRecordingLastPlayedPosition(const CPVRRecording &recording, int lastplayedposition, PVR_ERROR *error)
498 {
499   *error = PVR_ERROR_UNKNOWN;
500   PVR_CLIENT client;
501   if (GetConnectedClient(recording.m_iClientId, client) && client->SupportsRecordings())
502     *error = client->SetRecordingLastPlayedPosition(recording, lastplayedposition);
503   else
504     CLog::Log(LOGERROR, "PVR - %s - client %d does not support recordings",__FUNCTION__, recording.m_iClientId);
505
506   return *error == PVR_ERROR_NO_ERROR;
507 }
508
509 int CPVRClients::GetRecordingLastPlayedPosition(const CPVRRecording &recording)
510 {
511   int rc = 0;
512
513   PVR_CLIENT client;
514   if (GetConnectedClient(recording.m_iClientId, client) && client->SupportsRecordings())
515     rc = client->GetRecordingLastPlayedPosition(recording);
516   else
517     CLog::Log(LOGERROR, "PVR - %s - client %d does not support recordings",__FUNCTION__, recording.m_iClientId);
518
519   return rc;
520 }
521
522 bool CPVRClients::SetRecordingPlayCount(const CPVRRecording &recording, int count, PVR_ERROR *error)
523 {
524   *error = PVR_ERROR_UNKNOWN;
525   PVR_CLIENT client;
526   if (GetConnectedClient(recording.m_iClientId, client) && client->SupportsRecordingPlayCount())
527     *error = client->SetRecordingPlayCount(recording, count);
528   else
529     CLog::Log(LOGERROR, "PVR - %s - client %d does not support setting recording's play count",__FUNCTION__, recording.m_iClientId);
530
531   return *error == PVR_ERROR_NO_ERROR;
532 }
533
534 std::vector<PVR_EDL_ENTRY> CPVRClients::GetRecordingEdl(const CPVRRecording &recording)
535 {
536   PVR_CLIENT client;
537   if (GetConnectedClient(recording.m_iClientId, client) && client->SupportsRecordingEdl())
538     return client->GetRecordingEdl(recording);
539   else
540     CLog::Log(LOGERROR, "PVR - %s - client %d does not support getting Edl", __FUNCTION__, recording.m_iClientId);
541
542   return std::vector<PVR_EDL_ENTRY>();
543 }
544
545 bool CPVRClients::IsRecordingOnPlayingChannel(void) const
546 {
547   CPVRChannelPtr currentChannel;
548   return GetPlayingChannel(currentChannel) &&
549       currentChannel->IsRecording();
550 }
551
552 bool CPVRClients::CanRecordInstantly(void)
553 {
554   CPVRChannelPtr currentChannel;
555   return GetPlayingChannel(currentChannel) &&
556       currentChannel->CanRecord();
557 }
558
559 bool CPVRClients::CanPauseStream(void) const
560 {
561   PVR_CLIENT client;
562
563   if (GetPlayingClient(client))
564   {
565     return m_bIsPlayingRecording || client->CanPauseStream();
566   }
567
568   return false;
569 }
570
571 bool CPVRClients::CanSeekStream(void) const
572 {
573   PVR_CLIENT client;
574
575   if (GetPlayingClient(client))
576   {
577     return m_bIsPlayingRecording || client->CanSeekStream();
578   }
579
580   return false;
581 }
582
583 PVR_ERROR CPVRClients::GetEPGForChannel(const CPVRChannel &channel, CEpg *epg, time_t start, time_t end)
584 {
585   PVR_ERROR error(PVR_ERROR_UNKNOWN);
586   PVR_CLIENT client;
587   if (GetConnectedClient(channel.ClientID(), client))
588     error = client->GetEPGForChannel(channel, epg, start, end);
589
590   if (error != PVR_ERROR_NO_ERROR)
591     CLog::Log(LOGERROR, "PVR - %s - cannot get EPG for channel '%s' from client '%d': %s",__FUNCTION__, channel.ChannelName().c_str(), channel.ClientID(), CPVRClient::ToString(error));
592
593   return error;
594 }
595
596 PVR_ERROR CPVRClients::GetChannels(CPVRChannelGroupInternal *group)
597 {
598   PVR_ERROR error(PVR_ERROR_NO_ERROR);
599   PVR_CLIENTMAP clients;
600   GetConnectedClients(clients);
601
602   /* get the channel list from each client */
603   for (PVR_CLIENTMAP_ITR itrClients = clients.begin(); itrClients != clients.end(); itrClients++)
604   {
605     PVR_ERROR currentError = (*itrClients).second->GetChannels(*group, group->IsRadio());
606     if (currentError != PVR_ERROR_NOT_IMPLEMENTED &&
607         currentError != PVR_ERROR_NO_ERROR)
608     {
609       error = currentError;
610       CLog::Log(LOGERROR, "PVR - %s - cannot get channels from client '%d': %s",__FUNCTION__, (*itrClients).first, CPVRClient::ToString(error));
611     }
612   }
613
614   return error;
615 }
616
617 PVR_ERROR CPVRClients::GetChannelGroups(CPVRChannelGroups *groups)
618 {
619   PVR_ERROR error(PVR_ERROR_NO_ERROR);
620   PVR_CLIENTMAP clients;
621   GetConnectedClients(clients);
622
623   for (PVR_CLIENTMAP_ITR itrClients = clients.begin(); itrClients != clients.end(); itrClients++)
624   {
625     PVR_ERROR currentError = (*itrClients).second->GetChannelGroups(groups);
626     if (currentError != PVR_ERROR_NOT_IMPLEMENTED &&
627         currentError != PVR_ERROR_NO_ERROR)
628     {
629       error = currentError;
630       CLog::Log(LOGERROR, "PVR - %s - cannot get groups from client '%d': %s",__FUNCTION__, (*itrClients).first, CPVRClient::ToString(error));
631     }
632   }
633
634   return error;
635 }
636
637 PVR_ERROR CPVRClients::GetChannelGroupMembers(CPVRChannelGroup *group)
638 {
639   PVR_ERROR error(PVR_ERROR_NO_ERROR);
640   PVR_CLIENTMAP clients;
641   GetConnectedClients(clients);
642
643   /* get the member list from each client */
644   for (PVR_CLIENTMAP_ITR itrClients = clients.begin(); itrClients != clients.end(); itrClients++)
645   {
646     PVR_ERROR currentError = (*itrClients).second->GetChannelGroupMembers(group);
647     if (currentError != PVR_ERROR_NOT_IMPLEMENTED &&
648         currentError != PVR_ERROR_NO_ERROR)
649     {
650       error = currentError;
651       CLog::Log(LOGERROR, "PVR - %s - cannot get group members from client '%d': %s",__FUNCTION__, (*itrClients).first, CPVRClient::ToString(error));
652     }
653   }
654
655   return error;
656 }
657
658 bool CPVRClients::HasMenuHooks(int iClientID, PVR_MENUHOOK_CAT cat)
659 {
660   if (iClientID < 0)
661     iClientID = GetPlayingClientID();
662
663   PVR_CLIENT client;
664   return (GetConnectedClient(iClientID, client) &&
665       client->HaveMenuHooks(cat));
666 }
667
668 bool CPVRClients::GetMenuHooks(int iClientID, PVR_MENUHOOK_CAT cat, PVR_MENUHOOKS *hooks)
669 {
670   bool bReturn(false);
671
672   if (iClientID < 0)
673     iClientID = GetPlayingClientID();
674
675   PVR_CLIENT client;
676   if (GetConnectedClient(iClientID, client) && client->HaveMenuHooks(cat))
677   {
678     *hooks = *(client->GetMenuHooks());
679     bReturn = true;
680   }
681
682   return bReturn;
683 }
684
685 void CPVRClients::ProcessMenuHooks(int iClientID, PVR_MENUHOOK_CAT cat, const CFileItem *item)
686 {
687   PVR_MENUHOOKS *hooks = NULL;
688
689   // get client id
690   if (iClientID < 0 && cat == PVR_MENUHOOK_SETTING)
691   {
692     PVR_CLIENTMAP clients;
693     GetConnectedClients(clients);
694
695     if (clients.size() == 1)
696     {
697       iClientID = clients.begin()->first;
698     }
699     else if (clients.size() > 1)
700     {
701       // have user select client
702       CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
703       pDialog->Reset();
704       pDialog->SetHeading(19196);
705
706       PVR_CLIENTMAP_ITR itrClients;
707       for (itrClients = clients.begin(); itrClients != clients.end(); itrClients++)
708       {
709         pDialog->Add(itrClients->second->GetBackendName());
710       }
711       pDialog->DoModal();
712
713       int selection = pDialog->GetSelectedLabel();
714       if (selection >= 0)
715       {
716         itrClients = clients.begin();
717         for (int i = 0; i < selection; i++)
718           itrClients++;
719         iClientID = itrClients->first;
720       }
721     }
722   }
723
724   if (iClientID < 0)
725     iClientID = GetPlayingClientID();
726
727   PVR_CLIENT client;
728   if (GetConnectedClient(iClientID, client) && client->HaveMenuHooks(cat))
729   {
730     hooks = client->GetMenuHooks();
731     std::vector<int> hookIDs;
732
733     CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
734     pDialog->Reset();
735     pDialog->SetHeading(19196);
736     for (unsigned int i = 0; i < hooks->size(); i++)
737       if (hooks->at(i).category == cat || hooks->at(i).category == PVR_MENUHOOK_ALL)
738       {
739         pDialog->Add(client->GetString(hooks->at(i).iLocalizedStringId));
740         hookIDs.push_back(i);
741       }
742     pDialog->DoModal();
743
744     int selection = pDialog->GetSelectedLabel();
745     if (selection >= 0)
746       client->CallMenuHook(hooks->at(hookIDs.at(selection)), item);
747   }
748 }
749
750 bool CPVRClients::IsRunningChannelScan(void) const
751 {
752   CSingleLock lock(m_critSection);
753   return m_bChannelScanRunning;
754 }
755
756 vector<PVR_CLIENT> CPVRClients::GetClientsSupportingChannelScan(void) const
757 {
758   vector<PVR_CLIENT> possibleScanClients;
759   CSingleLock lock(m_critSection);
760
761   /* get clients that support channel scanning */
762   for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
763   {
764     if (itr->second->ReadyToUse() && itr->second->SupportsChannelScan())
765       possibleScanClients.push_back(itr->second);
766   }
767
768   return possibleScanClients;
769 }
770
771 void CPVRClients::StartChannelScan(void)
772 {
773   PVR_CLIENT scanClient;
774   CSingleLock lock(m_critSection);
775   vector<PVR_CLIENT> possibleScanClients = GetClientsSupportingChannelScan();
776   m_bChannelScanRunning = true;
777
778   /* multiple clients found */
779   if (possibleScanClients.size() > 1)
780   {
781     CGUIDialogSelect* pDialog= (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
782
783     pDialog->Reset();
784     pDialog->SetHeading(19119);
785
786     for (unsigned int i = 0; i < possibleScanClients.size(); i++)
787       pDialog->Add(possibleScanClients[i]->GetFriendlyName());
788
789     pDialog->DoModal();
790
791     int selection = pDialog->GetSelectedLabel();
792     if (selection >= 0)
793       scanClient = possibleScanClients[selection];
794   }
795   /* one client found */
796   else if (possibleScanClients.size() == 1)
797   {
798     scanClient = possibleScanClients[0];
799   }
800   /* no clients found */
801   else if (!scanClient)
802   {
803     CGUIDialogOK::ShowAndGetInput(19033,0,19192,0);
804     return;
805   }
806
807   /* start the channel scan */
808   CLog::Log(LOGNOTICE,"PVR - %s - starting to scan for channels on client %s",
809       __FUNCTION__, scanClient->GetFriendlyName().c_str());
810   long perfCnt = XbmcThreads::SystemClockMillis();
811
812   /* stop the supervisor thread */
813   g_PVRManager.StopUpdateThreads();
814
815   /* do the scan */
816   if (scanClient->StartChannelScan() != PVR_ERROR_NO_ERROR)
817     /* an error occured */
818     CGUIDialogOK::ShowAndGetInput(19111,0,19193,0);
819
820   /* restart the supervisor thread */
821   g_PVRManager.StartUpdateThreads();
822
823   CLog::Log(LOGNOTICE, "PVRManager - %s - channel scan finished after %li.%li seconds",
824       __FUNCTION__, (XbmcThreads::SystemClockMillis()-perfCnt)/1000, (XbmcThreads::SystemClockMillis()-perfCnt)%1000);
825   m_bChannelScanRunning = false;
826 }
827
828 bool CPVRClients::IsKnownClient(const AddonPtr client) const
829 {
830   // database IDs start at 1
831   return GetClientId(client) > 0;
832 }
833
834 int CPVRClients::RegisterClient(AddonPtr client)
835 {
836   int iClientId(-1);
837   if (!client->Enabled())
838     return -1;
839
840   CLog::Log(LOGDEBUG, "%s - registering add-on '%s'", __FUNCTION__, client->Name().c_str());
841
842   CPVRDatabase *database = GetPVRDatabase();
843   if (!database)
844     return -1;
845
846   // check whether we already know this client
847   iClientId = database->GetClientId(client->ID());
848
849   // try to register the new client in the db
850   if (iClientId < 0 && (iClientId = database->Persist(client)) < 0)
851   {
852     CLog::Log(LOGERROR, "PVR - %s - can't add client '%s' to the database", __FUNCTION__, client->Name().c_str());
853     return -1;
854   }
855
856   PVR_CLIENT addon;
857   // load and initialise the client libraries
858   {
859     CSingleLock lock(m_critSection);
860     PVR_CLIENTMAP_ITR existingClient = m_clientMap.find(iClientId);
861     if (existingClient != m_clientMap.end())
862     {
863       // return existing client
864       addon = existingClient->second;
865     }
866     else
867     {
868       // create a new client instance
869       addon = boost::dynamic_pointer_cast<CPVRClient>(client);
870       m_clientMap.insert(std::make_pair(iClientId, addon));
871     }
872   }
873
874   if (iClientId < 0)
875     CLog::Log(LOGERROR, "PVR - %s - can't register add-on '%s'", __FUNCTION__, client->Name().c_str());
876
877   return iClientId;
878 }
879
880 bool CPVRClients::UpdateAndInitialiseClients(bool bInitialiseAllClients /* = false */)
881 {
882   bool bReturn(true);
883   ADDON::VECADDONS map;
884   ADDON::VECADDONS disableAddons;
885   {
886     CSingleLock lock(m_critSection);
887     map = m_addons;
888   }
889
890   if (map.size() == 0)
891     return false;
892
893   for (unsigned iClientPtr = 0; iClientPtr < map.size(); iClientPtr++)
894   {
895     const AddonPtr clientAddon = map.at(iClientPtr);
896     bool bEnabled = clientAddon->Enabled() &&
897         !CAddonMgr::Get().IsAddonDisabled(clientAddon->ID());
898
899     if (!bEnabled && IsKnownClient(clientAddon))
900     {
901       CSingleLock lock(m_critSection);
902       /* stop the client and remove it from the db */
903       StopClient(clientAddon, false);
904       ADDON::VECADDONS::iterator addonPtr = std::find(m_addons.begin(), m_addons.end(), clientAddon);
905       if (addonPtr != m_addons.end())
906         m_addons.erase(addonPtr);
907
908     }
909     else if (bEnabled && (bInitialiseAllClients || !IsKnownClient(clientAddon) || !IsConnectedClient(clientAddon)))
910     {
911       bool bDisabled(false);
912
913       // register the add-on in the pvr db, and create the CPVRClient instance
914       int iClientId = RegisterClient(clientAddon);
915       if (iClientId < 0)
916       {
917         // failed to register or create the add-on, disable it
918         CLog::Log(LOGWARNING, "%s - failed to register add-on %s, disabling it", __FUNCTION__, clientAddon->Name().c_str());
919         disableAddons.push_back(clientAddon);
920         bDisabled = true;
921       }
922       else
923       {
924         ADDON_STATUS status(ADDON_STATUS_UNKNOWN);
925         PVR_CLIENT addon;
926         {
927           CSingleLock lock(m_critSection);
928           if (!GetClient(iClientId, addon))
929           {
930             CLog::Log(LOGWARNING, "%s - failed to find add-on %s, disabling it", __FUNCTION__, clientAddon->Name().c_str());
931             disableAddons.push_back(clientAddon);
932             bDisabled = true;
933           }
934         }
935
936         // throttle connection attempts, no more than 1 attempt per 5 seconds
937         if (!bDisabled && addon->Enabled())
938         {
939           time_t now;
940           CDateTime::GetCurrentDateTime().GetAsTime(now);
941           std::map<int, time_t>::iterator it = m_connectionAttempts.find(iClientId);
942           if (it != m_connectionAttempts.end() && now < it->second)
943             continue;
944           m_connectionAttempts[iClientId] = now + 5;
945         }
946
947         // re-check the enabled status. newly installed clients get disabled when they're added to the db
948         if (!bDisabled && addon->Enabled() && (status = addon->Create(iClientId)) != ADDON_STATUS_OK)
949         {
950           CLog::Log(LOGWARNING, "%s - failed to create add-on %s, status = %d", __FUNCTION__, clientAddon->Name().c_str(), status);
951           if (!addon.get() || !addon->DllLoaded() || status == ADDON_STATUS_PERMANENT_FAILURE)
952           {
953             // failed to load the dll of this add-on, disable it
954             CLog::Log(LOGWARNING, "%s - failed to load the dll for add-on %s, disabling it", __FUNCTION__, clientAddon->Name().c_str());
955             disableAddons.push_back(clientAddon);
956             bDisabled = true;
957           }
958         }
959       }
960
961       if (bDisabled && (g_PVRManager.GetState() == ManagerStateStarted || g_PVRManager.GetState() == ManagerStateStarting))
962         CGUIDialogOK::ShowAndGetInput(24070, 24071, 16029, 0);
963     }
964   }
965
966   // disable add-ons that failed to initialise
967   if (disableAddons.size() > 0)
968   {
969     CSingleLock lock(m_critSection);
970     for (ADDON::VECADDONS::iterator it = disableAddons.begin(); it != disableAddons.end(); it++)
971     {
972       // disable in the add-on db
973       CAddonMgr::Get().DisableAddon((*it)->ID(), true);
974
975       // remove from the pvr add-on list
976       ADDON::VECADDONS::iterator addonPtr = std::find(m_addons.begin(), m_addons.end(), *it);
977       if (addonPtr != m_addons.end())
978         m_addons.erase(addonPtr);
979     }
980   }
981
982   return bReturn;
983 }
984
985 void CPVRClients::Process(void)
986 {
987   bool bCheckedEnabledClientsOnStartup(false);
988
989   CAddonMgr::Get().RegisterAddonMgrCallback(ADDON_PVRDLL, this);
990   CAddonMgr::Get().RegisterObserver(this);
991
992   UpdateAddons();
993
994   while (!g_application.m_bStop && !m_bStop)
995   {
996     UpdateAndInitialiseClients();
997
998     if (!bCheckedEnabledClientsOnStartup)
999     {
1000       bCheckedEnabledClientsOnStartup = true;
1001       if (!HasEnabledClients() && !m_bNoAddonWarningDisplayed)
1002         ShowDialogNoClientsEnabled();
1003     }
1004
1005     PVR_CLIENT client;
1006     if (GetPlayingClient(client))
1007       client->UpdateCharInfoSignalStatus();
1008     Sleep(1000);
1009   }
1010 }
1011
1012 void CPVRClients::ShowDialogNoClientsEnabled(void)
1013 {
1014   if (g_PVRManager.GetState() != ManagerStateStarted && g_PVRManager.GetState() != ManagerStateStarting)
1015     return;
1016
1017   CGUIDialogOK::ShowAndGetInput(19240, 19241, 19242, 19243);
1018
1019   vector<CStdString> params;
1020   params.push_back("addons://disabled/xbmc.pvrclient");
1021   params.push_back("return");
1022   g_windowManager.ActivateWindow(WINDOW_ADDON_BROWSER, params);
1023 }
1024
1025 void CPVRClients::SaveCurrentChannelSettings(void)
1026 {
1027   CPVRChannelPtr channel;
1028   {
1029     CSingleLock lock(m_critSection);
1030     if (!GetPlayingChannel(channel) || !m_bIsValidChannelSettings)
1031       return;
1032   }
1033
1034   CPVRDatabase *database = GetPVRDatabase();
1035   if (!database)
1036     return;
1037
1038   if (CMediaSettings::Get().GetCurrentVideoSettings() != CMediaSettings::Get().GetDefaultVideoSettings())
1039   {
1040     CLog::Log(LOGDEBUG, "PVR - %s - persisting custom channel settings for channel '%s'",
1041         __FUNCTION__, channel->ChannelName().c_str());
1042     database->PersistChannelSettings(*channel, CMediaSettings::Get().GetCurrentVideoSettings());
1043   }
1044   else
1045   {
1046     CLog::Log(LOGDEBUG, "PVR - %s - no custom channel settings for channel '%s'",
1047         __FUNCTION__, channel->ChannelName().c_str());
1048     database->DeleteChannelSettings(*channel);
1049   }
1050 }
1051
1052 void CPVRClients::LoadCurrentChannelSettings(void)
1053 {
1054   CPVRChannelPtr channel;
1055   {
1056     CSingleLock lock(m_critSection);
1057     if (!GetPlayingChannel(channel))
1058       return;
1059   }
1060
1061   CPVRDatabase *database = GetPVRDatabase();
1062   if (!database)
1063     return;
1064
1065   if (g_application.m_pPlayer->HasPlayer())
1066   {
1067     /* set the default settings first */
1068     CVideoSettings loadedChannelSettings = CMediaSettings::Get().GetDefaultVideoSettings();
1069
1070     /* try to load the settings from the database */
1071     database->GetChannelSettings(*channel, loadedChannelSettings);
1072
1073     CMediaSettings::Get().GetCurrentVideoSettings() = CMediaSettings::Get().GetDefaultVideoSettings();
1074     CMediaSettings::Get().GetCurrentVideoSettings().m_Brightness          = loadedChannelSettings.m_Brightness;
1075     CMediaSettings::Get().GetCurrentVideoSettings().m_Contrast            = loadedChannelSettings.m_Contrast;
1076     CMediaSettings::Get().GetCurrentVideoSettings().m_Gamma               = loadedChannelSettings.m_Gamma;
1077     CMediaSettings::Get().GetCurrentVideoSettings().m_Crop                = loadedChannelSettings.m_Crop;
1078     CMediaSettings::Get().GetCurrentVideoSettings().m_CropLeft            = loadedChannelSettings.m_CropLeft;
1079     CMediaSettings::Get().GetCurrentVideoSettings().m_CropRight           = loadedChannelSettings.m_CropRight;
1080     CMediaSettings::Get().GetCurrentVideoSettings().m_CropTop             = loadedChannelSettings.m_CropTop;
1081     CMediaSettings::Get().GetCurrentVideoSettings().m_CropBottom          = loadedChannelSettings.m_CropBottom;
1082     CMediaSettings::Get().GetCurrentVideoSettings().m_CustomPixelRatio    = loadedChannelSettings.m_CustomPixelRatio;
1083     CMediaSettings::Get().GetCurrentVideoSettings().m_CustomZoomAmount    = loadedChannelSettings.m_CustomZoomAmount;
1084     CMediaSettings::Get().GetCurrentVideoSettings().m_CustomVerticalShift = loadedChannelSettings.m_CustomVerticalShift;
1085     CMediaSettings::Get().GetCurrentVideoSettings().m_NoiseReduction      = loadedChannelSettings.m_NoiseReduction;
1086     CMediaSettings::Get().GetCurrentVideoSettings().m_Sharpness           = loadedChannelSettings.m_Sharpness;
1087     CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod     = loadedChannelSettings.m_InterlaceMethod;
1088     CMediaSettings::Get().GetCurrentVideoSettings().m_OutputToAllSpeakers = loadedChannelSettings.m_OutputToAllSpeakers;
1089     CMediaSettings::Get().GetCurrentVideoSettings().m_AudioDelay          = loadedChannelSettings.m_AudioDelay;
1090     CMediaSettings::Get().GetCurrentVideoSettings().m_AudioStream         = loadedChannelSettings.m_AudioStream;
1091     CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn          = loadedChannelSettings.m_SubtitleOn;
1092     CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleDelay       = loadedChannelSettings.m_SubtitleDelay;
1093     CMediaSettings::Get().GetCurrentVideoSettings().m_CustomNonLinStretch = loadedChannelSettings.m_CustomNonLinStretch;
1094     CMediaSettings::Get().GetCurrentVideoSettings().m_ScalingMethod       = loadedChannelSettings.m_ScalingMethod;
1095     CMediaSettings::Get().GetCurrentVideoSettings().m_PostProcess         = loadedChannelSettings.m_PostProcess;
1096     CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode     = loadedChannelSettings.m_DeinterlaceMode;
1097
1098     /* only change the view mode if it's different */
1099     if (CMediaSettings::Get().GetCurrentVideoSettings().m_ViewMode != loadedChannelSettings.m_ViewMode)
1100     {
1101       CMediaSettings::Get().GetCurrentVideoSettings().m_ViewMode = loadedChannelSettings.m_ViewMode;
1102
1103       g_renderManager.SetViewMode(CMediaSettings::Get().GetCurrentVideoSettings().m_ViewMode);
1104       CMediaSettings::Get().GetCurrentVideoSettings().m_CustomZoomAmount = CDisplaySettings::Get().GetZoomAmount();
1105       CMediaSettings::Get().GetCurrentVideoSettings().m_CustomPixelRatio = CDisplaySettings::Get().GetPixelRatio();
1106     }
1107
1108     /* only change the subtitle stream, if it's different */
1109     if (CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleStream != loadedChannelSettings.m_SubtitleStream)
1110     {
1111       CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleStream = loadedChannelSettings.m_SubtitleStream;
1112
1113       g_application.m_pPlayer->SetSubtitle(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleStream);
1114     }
1115
1116     /* only change the audio stream if it's different */
1117     if (g_application.m_pPlayer->GetAudioStream() != CMediaSettings::Get().GetCurrentVideoSettings().m_AudioStream &&
1118         CMediaSettings::Get().GetCurrentVideoSettings().m_AudioStream >= 0)
1119       g_application.m_pPlayer->SetAudioStream(CMediaSettings::Get().GetCurrentVideoSettings().m_AudioStream);
1120
1121     g_application.m_pPlayer->SetAVDelay(CMediaSettings::Get().GetCurrentVideoSettings().m_AudioDelay);
1122     g_application.m_pPlayer->SetDynamicRangeCompression((long)(CMediaSettings::Get().GetCurrentVideoSettings().m_VolumeAmplification * 100));
1123     g_application.m_pPlayer->SetSubtitleVisible(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleOn);
1124     g_application.m_pPlayer->SetSubTitleDelay(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleDelay);
1125
1126     /* settings can be saved on next channel switch */
1127     m_bIsValidChannelSettings = true;
1128   }
1129 }
1130
1131 bool CPVRClients::UpdateAddons(void)
1132 {
1133   ADDON::VECADDONS addons;
1134   bool bReturn(CAddonMgr::Get().GetAddons(ADDON_PVRDLL, addons, true));
1135
1136   if (bReturn)
1137   {
1138     CSingleLock lock(m_critSection);
1139     m_addons = addons;
1140   }
1141   
1142   // handle "new" addons which aren't yet in the db - these have to be added first
1143   for (unsigned iClientPtr = 0; iClientPtr < m_addons.size(); iClientPtr++)
1144   {
1145     const AddonPtr clientAddon = m_addons.at(iClientPtr);
1146   
1147     if (!m_addonDb.HasAddon(clientAddon->ID()))
1148     {
1149       m_addonDb.AddAddon(clientAddon, -1);
1150     }
1151   }
1152
1153   if ((!bReturn || addons.size() == 0) && !m_bNoAddonWarningDisplayed &&
1154       !CAddonMgr::Get().HasAddons(ADDON_PVRDLL, false) &&
1155       (g_PVRManager.GetState() == ManagerStateStarted || g_PVRManager.GetState() == ManagerStateStarting))
1156   {
1157     // No PVR add-ons could be found
1158     // You need a tuner, backend software, and an add-on for the backend to be able to use PVR.
1159     // Please visit xbmc.org/pvr to learn more.
1160     m_bNoAddonWarningDisplayed = true;
1161     CSettings::Get().SetBool("pvrmanager.enabled", false);
1162     CGUIDialogOK::ShowAndGetInput(19271, 19272, 19273, 19274);
1163     CGUIMessage msg(GUI_MSG_UPDATE, WINDOW_SETTINGS_MYPVR, 0);
1164     g_windowManager.SendThreadMessage(msg, WINDOW_SETTINGS_MYPVR);
1165   }
1166
1167   return bReturn;
1168 }
1169
1170 void CPVRClients::Notify(const Observable &obs, const ObservableMessage msg)
1171 {
1172   if (msg == ObservableMessageAddons)
1173     UpdateAddons();
1174 }
1175
1176 bool CPVRClients::GetClient(const CStdString &strId, ADDON::AddonPtr &addon) const
1177 {
1178   CSingleLock lock(m_critSection);
1179   for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++)
1180   {
1181     if (itr->second->ID() == strId)
1182     {
1183       addon = itr->second;
1184       return true;
1185     }
1186   }
1187   return false;
1188 }
1189
1190 bool CPVRClients::SupportsChannelGroups(int iClientId) const
1191 {
1192   PVR_CLIENT client;
1193   return GetConnectedClient(iClientId, client) && client->SupportsChannelGroups();
1194 }
1195
1196 bool CPVRClients::SupportsChannelScan(int iClientId) const
1197 {
1198   PVR_CLIENT client;
1199   return GetConnectedClient(iClientId, client) && client->SupportsChannelScan();
1200 }
1201
1202 bool CPVRClients::SupportsEPG(int iClientId) const
1203 {
1204   PVR_CLIENT client;
1205   return GetConnectedClient(iClientId, client) && client->SupportsEPG();
1206 }
1207
1208 bool CPVRClients::SupportsLastPlayedPosition(int iClientId) const
1209 {
1210   PVR_CLIENT client;
1211   return GetConnectedClient(iClientId, client) && client->SupportsLastPlayedPosition();
1212 }
1213
1214 bool CPVRClients::SupportsRadio(int iClientId) const
1215 {
1216   PVR_CLIENT client;
1217   return GetConnectedClient(iClientId, client) && client->SupportsRadio();
1218 }
1219
1220 bool CPVRClients::SupportsRecordings(int iClientId) const
1221 {
1222   PVR_CLIENT client;
1223   return GetConnectedClient(iClientId, client) && client->SupportsRecordings();
1224 }
1225
1226 bool CPVRClients::SupportsRecordingFolders(int iClientId) const
1227 {
1228   PVR_CLIENT client;
1229   return GetConnectedClient(iClientId, client) && client->SupportsRecordingFolders();
1230 }
1231
1232 bool CPVRClients::SupportsRecordingPlayCount(int iClientId) const
1233 {
1234   PVR_CLIENT client;
1235   return GetConnectedClient(iClientId, client) && client->SupportsRecordingPlayCount();
1236 }
1237
1238 bool CPVRClients::SupportsRecordingEdl(int iClientId) const
1239 {
1240   PVR_CLIENT client;
1241   return GetConnectedClient(iClientId, client) && client->SupportsRecordingEdl();
1242 }
1243
1244 bool CPVRClients::SupportsTimers(int iClientId) const
1245 {
1246   PVR_CLIENT client;
1247   return GetConnectedClient(iClientId, client) && client->SupportsTimers();
1248 }
1249
1250 bool CPVRClients::SupportsTV(int iClientId) const
1251 {
1252   PVR_CLIENT client;
1253   return GetConnectedClient(iClientId, client) && client->SupportsTV();
1254 }
1255
1256 bool CPVRClients::HandlesDemuxing(int iClientId) const
1257 {
1258   PVR_CLIENT client;
1259   return GetConnectedClient(iClientId, client) && client->HandlesDemuxing();
1260 }
1261
1262 bool CPVRClients::HandlesInputStream(int iClientId) const
1263 {
1264   PVR_CLIENT client;
1265   return GetConnectedClient(iClientId, client) && client->HandlesInputStream();
1266 }
1267
1268 bool CPVRClients::GetPlayingClient(PVR_CLIENT &client) const
1269 {
1270   return GetConnectedClient(GetPlayingClientID(), client);
1271 }
1272
1273 bool CPVRClients::OpenStream(const CPVRChannel &tag, bool bIsSwitchingChannel)
1274 {
1275   bool bReturn(false);
1276   CloseStream();
1277
1278   /* try to open the stream on the client */
1279   PVR_CLIENT client;
1280   if (GetConnectedClient(tag.ClientID(), client) &&
1281       client->OpenStream(tag, bIsSwitchingChannel))
1282   {
1283     CSingleLock lock(m_critSection);
1284     m_playingClientId = tag.ClientID();
1285     m_bIsPlayingLiveTV = true;
1286
1287     if (tag.ClientID() == PVR_VIRTUAL_CLIENT_ID)
1288       m_strPlayingClientName = g_localizeStrings.Get(19209);
1289     else if (!tag.IsVirtual() && client.get())
1290       m_strPlayingClientName = client->GetFriendlyName();
1291     else
1292       m_strPlayingClientName = g_localizeStrings.Get(13205);
1293
1294     bReturn = true;
1295   }
1296
1297   return bReturn;
1298 }
1299
1300 bool CPVRClients::OpenStream(const CPVRRecording &tag)
1301 {
1302   bool bReturn(false);
1303   CloseStream();
1304
1305   /* try to open the recording stream on the client */
1306   PVR_CLIENT client;
1307   if (GetConnectedClient(tag.m_iClientId, client) &&
1308       client->OpenStream(tag))
1309   {
1310     CSingleLock lock(m_critSection);
1311     m_playingClientId = tag.m_iClientId;
1312     m_bIsPlayingRecording = true;
1313     m_strPlayingClientName = client->GetFriendlyName();
1314     bReturn = true;
1315   }
1316
1317   return bReturn;
1318 }
1319
1320 void CPVRClients::CloseStream(void)
1321 {
1322   PVR_CLIENT playingClient;
1323   if (GetPlayingClient(playingClient))
1324     playingClient->CloseStream();
1325
1326   CSingleLock lock(m_critSection);
1327   m_bIsPlayingLiveTV     = false;
1328   m_bIsPlayingRecording  = false;
1329   m_playingClientId      = PVR_INVALID_CLIENT_ID;
1330   m_strPlayingClientName = "";
1331 }
1332
1333 int CPVRClients::ReadStream(void* lpBuf, int64_t uiBufSize)
1334 {
1335   PVR_CLIENT client;
1336   if (GetPlayingClient(client))
1337     return client->ReadStream(lpBuf, uiBufSize);
1338   return -EINVAL;
1339 }
1340
1341 int64_t CPVRClients::GetStreamLength(void)
1342 {
1343   PVR_CLIENT client;
1344   if (GetPlayingClient(client))
1345     return client->GetStreamLength();
1346   return -EINVAL;
1347 }
1348
1349 int64_t CPVRClients::SeekStream(int64_t iFilePosition, int iWhence/* = SEEK_SET*/)
1350 {
1351   PVR_CLIENT client;
1352   if (GetPlayingClient(client))
1353     return client->SeekStream(iFilePosition, iWhence);
1354   return -EINVAL;
1355 }
1356
1357 int64_t CPVRClients::GetStreamPosition(void)
1358 {
1359   PVR_CLIENT client;
1360   if (GetPlayingClient(client))
1361     return client->GetStreamPosition();
1362   return -EINVAL;
1363 }
1364
1365 void CPVRClients::PauseStream(bool bPaused)
1366 {
1367   PVR_CLIENT client;
1368   if (GetPlayingClient(client))
1369     client->PauseStream(bPaused);
1370 }
1371
1372 CStdString CPVRClients::GetCurrentInputFormat(void) const
1373 {
1374   CStdString strReturn;
1375   CPVRChannelPtr currentChannel;
1376   if (GetPlayingChannel(currentChannel))
1377     strReturn = currentChannel->InputFormat();
1378
1379   return strReturn;
1380 }
1381
1382 PVR_STREAM_PROPERTIES CPVRClients::GetCurrentStreamProperties(void)
1383 {
1384   PVR_STREAM_PROPERTIES props;
1385   PVR_CLIENT client;
1386   
1387   memset(&props, 0, sizeof(props));
1388   if (GetPlayingClient(client))
1389     client->GetStreamProperties(&props);
1390
1391   return props;
1392 }
1393
1394 bool CPVRClients::IsPlaying(void) const
1395 {
1396   CSingleLock lock(m_critSection);
1397   return m_bIsPlayingRecording || m_bIsPlayingLiveTV;
1398 }
1399
1400 bool CPVRClients::IsPlayingRadio(void) const
1401 {
1402   PVR_CLIENT client;
1403   if (GetPlayingClient(client))
1404     return client->IsPlayingLiveRadio();
1405   return false;
1406 }
1407
1408 bool CPVRClients::IsPlayingTV(void) const
1409 {
1410   PVR_CLIENT client;
1411   if (GetPlayingClient(client))
1412     return client->IsPlayingLiveTV();
1413   return false;
1414 }
1415
1416 bool CPVRClients::IsPlayingRecording(void) const
1417 {
1418   CSingleLock lock(m_critSection);
1419   return m_bIsPlayingRecording;
1420 }
1421
1422 bool CPVRClients::IsReadingLiveStream(void) const
1423 {
1424   CSingleLock lock(m_critSection);
1425   return m_bIsPlayingLiveTV;
1426 }
1427
1428 bool CPVRClients::IsEncrypted(void) const
1429 {
1430   PVR_CLIENT client;
1431   if (GetPlayingClient(client))
1432     return client->IsPlayingEncryptedChannel();
1433   return false;
1434 }