6b0294a93b2d87bf724ab48e47cff0eb1f73948c
[vuplus_xbmc] / xbmc / cores / playercorefactory / PlayerCoreFactory.cpp
1 /*
2  *      Copyright (C) 2005-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 "utils/BitstreamStats.h"
22 #include "PlayerCoreFactory.h"
23 #include "threads/SingleLock.h"
24 #include "cores/AudioEngine/Utils/AEUtil.h"
25 #include "cores/dvdplayer/DVDPlayer.h"
26 #include "cores/paplayer/PAPlayer.h"
27 #include "cores/paplayer/DVDPlayerCodec.h"
28 #include "dialogs/GUIDialogContextMenu.h"
29 #include "utils/HttpHeader.h"
30 #include "settings/Settings.h"
31 #include "URL.h"
32 #include "FileItem.h"
33 #include "profiles/ProfilesManager.h"
34 #include "settings/AdvancedSettings.h"
35 #include "utils/AutoPtrHandle.h"
36 #include "cores/ExternalPlayer/ExternalPlayer.h"
37 #include "PlayerCoreConfig.h"
38 #include "PlayerSelectionRule.h"
39 #include "guilib/LocalizeStrings.h"
40
41 #define PLAYERCOREFACTORY_XML "playercorefactory.xml"
42
43 using namespace AUTOPTR;
44
45 CPlayerCoreFactory::CPlayerCoreFactory()
46 { }
47
48 CPlayerCoreFactory::~CPlayerCoreFactory()
49 {
50   for(std::vector<CPlayerCoreConfig *>::iterator it = m_vecCoreConfigs.begin(); it != m_vecCoreConfigs.end(); ++it)
51     delete *it;
52   for(std::vector<CPlayerSelectionRule *>::iterator it = m_vecCoreSelectionRules.begin(); it != m_vecCoreSelectionRules.end(); ++it)
53     delete *it;
54 }
55
56 CPlayerCoreFactory& CPlayerCoreFactory::Get()
57 {
58   static CPlayerCoreFactory sPlayerCoreFactory;
59   return sPlayerCoreFactory;
60 }
61
62 void CPlayerCoreFactory::OnSettingsLoaded()
63 {
64   LoadConfiguration("special://xbmc/system/" PLAYERCOREFACTORY_XML, true);
65   LoadConfiguration(CProfilesManager::Get().GetUserDataItem(PLAYERCOREFACTORY_XML), false);
66 }
67
68 /* generic function to make a vector unique, removes later duplicates */
69 template<typename T> void unique (T &con)
70 {
71   typename T::iterator cur, end;
72   cur = con.begin();
73   end = con.end();
74   while (cur != end)
75   {
76     typename T::value_type i = *cur;
77     end = remove (++cur, end, i);
78   }
79   con.erase (end, con.end());
80 }
81
82 IPlayer* CPlayerCoreFactory::CreatePlayer(const CStdString& strCore, IPlayerCallback& callback) const
83 {
84   return CreatePlayer(GetPlayerCore(strCore), callback );
85 }
86
87 IPlayer* CPlayerCoreFactory::CreatePlayer(const PLAYERCOREID eCore, IPlayerCallback& callback) const
88 {
89   CSingleLock lock(m_section);
90   if (m_vecCoreConfigs.empty() || eCore-1 > m_vecCoreConfigs.size()-1)
91     return NULL;
92
93   return m_vecCoreConfigs[eCore-1]->CreatePlayer(callback);
94 }
95
96 PLAYERCOREID CPlayerCoreFactory::GetPlayerCore(const CStdString& strCoreName) const
97 {
98   CSingleLock lock(m_section);
99   if (!strCoreName.empty())
100   {
101     // Dereference "*default*player" aliases
102     CStdString strRealCoreName;
103     if (strCoreName.Equals("audiodefaultplayer", false)) strRealCoreName = g_advancedSettings.m_audioDefaultPlayer;
104     else if (strCoreName.Equals("videodefaultplayer", false)) strRealCoreName = g_advancedSettings.m_videoDefaultPlayer;
105     else if (strCoreName.Equals("videodefaultdvdplayer", false)) strRealCoreName = g_advancedSettings.m_videoDefaultDVDPlayer;
106     else strRealCoreName = strCoreName;
107
108     for(PLAYERCOREID i = 0; i < m_vecCoreConfigs.size(); i++)
109     {
110       if (m_vecCoreConfigs[i]->GetName().Equals(strRealCoreName, false))
111         return i+1;
112     }
113     CLog::Log(LOGWARNING, "CPlayerCoreFactory::GetPlayerCore(%s): no such core: %s", strCoreName.c_str(), strRealCoreName.c_str());
114   }
115   return EPC_NONE;
116 }
117
118 CStdString CPlayerCoreFactory::GetPlayerName(const PLAYERCOREID eCore) const
119 {
120   CSingleLock lock(m_section);
121   return m_vecCoreConfigs[eCore-1]->GetName();
122 }
123
124 CPlayerCoreConfig* CPlayerCoreFactory::GetPlayerConfig(const CStdString& strCoreName) const
125 {
126   CSingleLock lock(m_section);
127   PLAYERCOREID id = GetPlayerCore(strCoreName);
128   if (id != EPC_NONE) return m_vecCoreConfigs[id-1];
129   else return NULL;
130 }
131
132 void CPlayerCoreFactory::GetPlayers( VECPLAYERCORES &vecCores ) const
133 {
134   CSingleLock lock(m_section);
135   for(unsigned int i = 0; i < m_vecCoreConfigs.size(); i++)
136   {
137     if(m_vecCoreConfigs[i]->m_eCore == EPC_NONE)
138       continue;
139     if (m_vecCoreConfigs[i]->m_bPlaysAudio || m_vecCoreConfigs[i]->m_bPlaysVideo)
140       vecCores.push_back(i+1);
141   }
142 }
143
144 void CPlayerCoreFactory::GetPlayers( VECPLAYERCORES &vecCores, const bool audio, const bool video ) const
145 {
146   CSingleLock lock(m_section);
147   CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: for video=%d, audio=%d", video, audio);
148
149   for(unsigned int i = 0; i < m_vecCoreConfigs.size(); i++)
150   {
151     if(m_vecCoreConfigs[i]->m_eCore == EPC_NONE)
152       continue;
153     if (audio == m_vecCoreConfigs[i]->m_bPlaysAudio && video == m_vecCoreConfigs[i]->m_bPlaysVideo)
154     {
155       CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding player: %s (%d)", m_vecCoreConfigs[i]->m_name.c_str(), i+1);
156       vecCores.push_back(i+1);
157     }
158   }
159 }
160
161 void CPlayerCoreFactory::GetPlayers( const CFileItem& item, VECPLAYERCORES &vecCores) const
162 {
163   CURL url(item.GetPath());
164
165   CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers(%s)", item.GetPath().c_str());
166
167   // Process rules
168   for(unsigned int i = 0; i < m_vecCoreSelectionRules.size(); i++)
169     m_vecCoreSelectionRules[i]->GetPlayers(item, vecCores);
170
171   CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: matched %"PRIuS" rules with players", vecCores.size());
172
173   if( PAPlayer::HandlesType(url.GetFileType()) )
174   {
175     // We no longer force PAPlayer as our default audio player (used to be true):
176     bool bAdd = false;
177     if (url.GetProtocol().Equals("mms"))
178     {
179        bAdd = false;
180     }
181     else if (item.IsType(".wma"))
182     {
183 //      bAdd = true;
184 //      DVDPlayerCodec codec;
185 //      if (!codec.Init(item.GetPath(),2048))
186 //        bAdd = false;
187 //      codec.DeInit();
188     }
189
190     if (bAdd)
191     {
192       if( CSettings::Get().GetInt("audiooutput.mode") == AUDIO_ANALOG )
193       {
194         CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding PAPlayer (%d)", EPC_PAPLAYER);
195         vecCores.push_back(EPC_PAPLAYER);
196       }
197       else if (url.GetFileType().Equals("ac3") 
198             || url.GetFileType().Equals("dts"))
199       {
200         CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding DVDPlayer (%d)", EPC_DVDPLAYER);
201         vecCores.push_back(EPC_DVDPLAYER);
202       }
203       else
204       {
205         CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding PAPlayer (%d)", EPC_PAPLAYER);
206         vecCores.push_back(EPC_PAPLAYER);
207       }
208     }
209   }
210
211   // Process defaults
212
213   // Set video default player. Check whether it's video first (overrule audio check)
214   // Also push these players in case it is NOT audio either
215   if (item.IsVideo() || !item.IsAudio())
216   {
217     PLAYERCOREID eVideoDefault = GetPlayerCore("videodefaultplayer");
218     if (eVideoDefault != EPC_NONE)
219     {
220       CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding videodefaultplayer (%d)", eVideoDefault);
221       vecCores.push_back(eVideoDefault);
222     }
223     GetPlayers(vecCores, false, true);  // Video-only players
224     GetPlayers(vecCores, true, true);   // Audio & video players
225   }
226
227   // Set audio default player
228   // Pushback all audio players in case we don't know the type
229   if (item.IsAudio())
230   {
231     PLAYERCOREID eAudioDefault = GetPlayerCore("audiodefaultplayer");
232     if (eAudioDefault != EPC_NONE)
233     {
234       CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding audiodefaultplayer (%d)", eAudioDefault);
235       vecCores.push_back(eAudioDefault);
236     }
237     GetPlayers(vecCores, true, false); // Audio-only players
238     GetPlayers(vecCores, true, true);  // Audio & video players
239   }
240
241   /* make our list unique, preserving first added players */
242   unique(vecCores);
243
244   CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: added %"PRIuS" players", vecCores.size());
245 }
246
247 void CPlayerCoreFactory::GetRemotePlayers( VECPLAYERCORES &vecCores ) const
248 {
249   CSingleLock lock(m_section);
250   for(unsigned int i = 0; i < m_vecCoreConfigs.size(); i++)
251   {
252     if(m_vecCoreConfigs[i]->m_eCore != EPC_UPNPPLAYER)
253       continue;
254     vecCores.push_back(i+1);
255   }
256 }
257
258 PLAYERCOREID CPlayerCoreFactory::GetDefaultPlayer( const CFileItem& item ) const
259 {
260   VECPLAYERCORES vecCores;
261   GetPlayers(item, vecCores);
262
263   //If we have any players return the first one
264   if( !vecCores.empty() ) return vecCores.at(0);
265
266   return EPC_NONE;
267 }
268
269 PLAYERCOREID CPlayerCoreFactory::SelectPlayerDialog(VECPLAYERCORES &vecCores, float posX, float posY) const
270 {
271   CContextButtons choices;
272   if (vecCores.size())
273   {
274     //Add default player
275     CStdString strCaption = CPlayerCoreFactory::GetPlayerName(vecCores[0]);
276     strCaption += " (";
277     strCaption += g_localizeStrings.Get(13278);
278     strCaption += ")";
279     choices.Add(0, strCaption);
280
281     //Add all other players
282     for( unsigned int i = 1; i < vecCores.size(); i++ )
283       choices.Add(i, CPlayerCoreFactory::GetPlayerName(vecCores[i]));
284
285     int choice = CGUIDialogContextMenu::ShowAndGetChoice(choices);
286     if (choice >= 0)
287       return vecCores[choice];
288   }
289   return EPC_NONE;
290 }
291
292 PLAYERCOREID CPlayerCoreFactory::SelectPlayerDialog(float posX, float posY) const
293 {
294   VECPLAYERCORES vecCores;
295   GetPlayers(vecCores);
296   return SelectPlayerDialog(vecCores, posX, posY);
297 }
298
299 bool CPlayerCoreFactory::LoadConfiguration(const std::string &file, bool clear)
300 {
301   CSingleLock lock(m_section);
302
303   CLog::Log(LOGNOTICE, "Loading player core factory settings from %s.", file.c_str());
304   if (!XFILE::CFile::Exists(file))
305   { // tell the user it doesn't exist
306     CLog::Log(LOGNOTICE, "%s does not exist. Skipping.", file.c_str());
307     return false;
308   }
309
310   CXBMCTinyXML playerCoreFactoryXML;
311   if (!playerCoreFactoryXML.LoadFile(file))
312   {
313     CLog::Log(LOGERROR, "Error loading %s, Line %d (%s)", file.c_str(), playerCoreFactoryXML.ErrorRow(), playerCoreFactoryXML.ErrorDesc());
314     return false;
315   }
316
317   TiXmlElement *pConfig = playerCoreFactoryXML.RootElement();
318   if (pConfig == NULL)
319   {
320       CLog::Log(LOGERROR, "Error loading %s, Bad structure", file.c_str());
321       return false;
322   }
323
324   if (clear)
325   {
326     for(std::vector<CPlayerCoreConfig *>::iterator it = m_vecCoreConfigs.begin(); it != m_vecCoreConfigs.end(); ++it)
327       delete *it;
328     m_vecCoreConfigs.clear();
329     // Builtin players; hard-coded because re-ordering them would break scripts
330     CPlayerCoreConfig* dvdplayer = new CPlayerCoreConfig("DVDPlayer", EPC_DVDPLAYER, NULL);
331     dvdplayer->m_bPlaysAudio = dvdplayer->m_bPlaysVideo = true;
332     m_vecCoreConfigs.push_back(dvdplayer);
333
334      // Don't remove this, its a placeholder for the old MPlayer core, it would break scripts
335     CPlayerCoreConfig* mplayer = new CPlayerCoreConfig("oldmplayercore", EPC_DVDPLAYER, NULL);
336     m_vecCoreConfigs.push_back(mplayer);
337
338     CPlayerCoreConfig* paplayer = new CPlayerCoreConfig("PAPlayer", EPC_PAPLAYER, NULL);
339     paplayer->m_bPlaysAudio = true;
340     m_vecCoreConfigs.push_back(paplayer);
341
342 #if defined(HAS_AMLPLAYER)
343     CPlayerCoreConfig* amlplayer = new CPlayerCoreConfig("AMLPlayer", EPC_AMLPLAYER, NULL);
344     amlplayer->m_bPlaysAudio = true;
345     amlplayer->m_bPlaysVideo = true;
346     m_vecCoreConfigs.push_back(amlplayer);
347 #endif
348
349 #if defined(HAS_OMXPLAYER)
350     CPlayerCoreConfig* omxplayer = new CPlayerCoreConfig("OMXPlayer", EPC_OMXPLAYER, NULL);
351     omxplayer->m_bPlaysAudio = true;
352     omxplayer->m_bPlaysVideo = true;
353     m_vecCoreConfigs.push_back(omxplayer);
354 #endif
355
356     for(std::vector<CPlayerSelectionRule *>::iterator it = m_vecCoreSelectionRules.begin(); it != m_vecCoreSelectionRules.end(); ++it)
357       delete *it;
358     m_vecCoreSelectionRules.clear();
359   }
360
361   if (!pConfig || strcmpi(pConfig->Value(),"playercorefactory") != 0)
362   {
363     CLog::Log(LOGERROR, "Error loading configuration, no <playercorefactory> node");
364     return false;
365   }
366
367   TiXmlElement *pPlayers = pConfig->FirstChildElement("players");
368   if (pPlayers)
369   {
370     TiXmlElement* pPlayer = pPlayers->FirstChildElement("player");
371     while (pPlayer)
372     {
373       CStdString name = pPlayer->Attribute("name");
374       CStdString type = pPlayer->Attribute("type");
375       if (type.length() == 0) type = name;
376       type.ToLower();
377
378       EPLAYERCORES eCore = EPC_NONE;
379       if (type == "dvdplayer" || type == "mplayer") eCore = EPC_DVDPLAYER;
380       if (type == "paplayer" ) eCore = EPC_PAPLAYER;
381       if (type == "externalplayer" ) eCore = EPC_EXTPLAYER;
382
383       if (eCore != EPC_NONE)
384       {
385         m_vecCoreConfigs.push_back(new CPlayerCoreConfig(name, eCore, pPlayer));
386       }
387
388       pPlayer = pPlayer->NextSiblingElement("player");
389     }
390   }
391
392   TiXmlElement *pRule = pConfig->FirstChildElement("rules");
393   while (pRule)
394   {
395     const char* szAction = pRule->Attribute("action");
396     if (szAction)
397     {
398       if (stricmp(szAction, "append") == 0)
399       {
400         m_vecCoreSelectionRules.push_back(new CPlayerSelectionRule(pRule));
401       }
402       else if (stricmp(szAction, "prepend") == 0)
403       {
404         m_vecCoreSelectionRules.insert(m_vecCoreSelectionRules.begin(), 1, new CPlayerSelectionRule(pRule));
405       }
406       else
407       {
408         m_vecCoreSelectionRules.clear();
409         m_vecCoreSelectionRules.push_back(new CPlayerSelectionRule(pRule));
410       }
411     }
412     else
413     {
414       m_vecCoreSelectionRules.push_back(new CPlayerSelectionRule(pRule));
415     }
416
417     pRule = pRule->NextSiblingElement("rules");
418   }
419
420   // succeeded - tell the user it worked
421   CLog::Log(LOGNOTICE, "Loaded playercorefactory configuration");
422
423   return true;
424 }
425
426 void CPlayerCoreFactory::OnPlayerDiscovered(const CStdString& id, const CStdString& name, EPLAYERCORES core)
427 {
428   CSingleLock lock(m_section);
429   std::vector<CPlayerCoreConfig *>::iterator it;
430   for(it  = m_vecCoreConfigs.begin();
431       it != m_vecCoreConfigs.end();
432       ++it)
433   {
434     if ((*it)->GetId() == id)
435     {
436       (*it)->m_name  = name;
437       (*it)->m_eCore = core;
438       return;
439     }
440   }
441
442   CPlayerCoreConfig* player = new CPlayerCoreConfig(name, core, NULL, id);
443   player->m_bPlaysAudio = true;
444   player->m_bPlaysVideo = true;
445   m_vecCoreConfigs.push_back(player);
446 }
447
448 void CPlayerCoreFactory::OnPlayerRemoved(const CStdString& id)
449 {
450   CSingleLock lock(m_section);
451   std::vector<CPlayerCoreConfig *>::iterator it;
452   for(it  = m_vecCoreConfigs.begin();
453       it != m_vecCoreConfigs.end();
454       ++it)
455   {
456     if ((*it)->GetId() == id)
457       (*it)->m_eCore = EPC_NONE;
458   }
459 }