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