1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "PlayerCoreFactory.h"
10
11 #include "FileItem.h"
12 #include "PlayerCoreConfig.h"
13 #include "PlayerSelectionRule.h"
14 #include "URL.h"
15 #include "cores/IPlayerCallback.h"
16 #include "cores/paplayer/PAPlayer.h"
17 #include "dialogs/GUIDialogContextMenu.h"
18 #include "guilib/LocalizeStrings.h"
19 #include "profiles/ProfileManager.h"
20 #include "settings/AdvancedSettings.h"
21 #include "settings/Settings.h"
22 #include "settings/SettingsComponent.h"
23 #include "settings/lib/SettingsManager.h"
24 #include "threads/SingleLock.h"
25 #include "utils/StringUtils.h"
26 #include "utils/XMLUtils.h"
27
28 #include <sstream>
29
30 #define PLAYERCOREFACTORY_XML "playercorefactory.xml"
31
CPlayerCoreFactory(const CProfileManager & profileManager)32 CPlayerCoreFactory::CPlayerCoreFactory(const CProfileManager &profileManager) :
33 m_profileManager(profileManager)
34 {
35 m_settings = CServiceBroker::GetSettingsComponent()->GetSettings();
36
37 if (m_settings->IsLoaded())
38 OnSettingsLoaded();
39
40 m_settings->GetSettingsManager()->RegisterSettingsHandler(this);
41 }
42
~CPlayerCoreFactory()43 CPlayerCoreFactory::~CPlayerCoreFactory()
44 {
45 m_settings->GetSettingsManager()->UnregisterSettingsHandler(this);
46
47 for(std::vector<CPlayerCoreConfig *>::iterator it = m_vecPlayerConfigs.begin(); it != m_vecPlayerConfigs.end(); ++it)
48 delete *it;
49 for(std::vector<CPlayerSelectionRule *>::iterator it = m_vecCoreSelectionRules.begin(); it != m_vecCoreSelectionRules.end(); ++it)
50 delete *it;
51 }
52
OnSettingsLoaded()53 void CPlayerCoreFactory::OnSettingsLoaded()
54 {
55 LoadConfiguration("special://xbmc/system/" PLAYERCOREFACTORY_XML, true);
56 LoadConfiguration(m_profileManager.GetUserDataItem(PLAYERCOREFACTORY_XML), false);
57 }
58
CreatePlayer(const std::string & nameId,IPlayerCallback & callback) const59 IPlayer* CPlayerCoreFactory::CreatePlayer(const std::string& nameId, IPlayerCallback& callback) const
60 {
61 CSingleLock lock(m_section);
62 size_t idx = GetPlayerIndex(nameId);
63
64 if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size())
65 return nullptr;
66
67 return m_vecPlayerConfigs[idx]->CreatePlayer(callback);
68 }
69
GetPlayers(std::vector<std::string> & players) const70 void CPlayerCoreFactory::GetPlayers(std::vector<std::string>&players) const
71 {
72 CSingleLock lock(m_section);
73 players.clear();
74 for (auto conf: m_vecPlayerConfigs)
75 {
76 if (conf->m_bPlaysAudio || conf->m_bPlaysVideo)
77 players.push_back(conf->m_name);
78 }
79 }
80
GetPlayers(std::vector<std::string> & players,const bool audio,const bool video) const81 void CPlayerCoreFactory::GetPlayers(std::vector<std::string>&players, const bool audio, const bool video) const
82 {
83 CSingleLock lock(m_section);
84 CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: for video=%d, audio=%d", video, audio);
85
86 for (auto conf: m_vecPlayerConfigs)
87 {
88 if (audio == conf->m_bPlaysAudio && video == conf->m_bPlaysVideo)
89 {
90 if (std::find(players.begin(), players.end(), conf->m_name) != players.end())
91 continue;
92
93 CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding player: %s", conf->m_name.c_str());
94 players.push_back(conf->m_name);
95 }
96 }
97 }
98
GetPlayers(const CFileItem & item,std::vector<std::string> & players) const99 void CPlayerCoreFactory::GetPlayers(const CFileItem& item, std::vector<std::string>&players) const
100 {
101 CURL url(item.GetDynPath());
102
103 CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers(%s)", CURL::GetRedacted(item.GetDynPath()).c_str());
104
105 std::vector<std::string>validPlayers;
106 GetPlayers(validPlayers);
107
108 // Process rules
109 for (auto rule: m_vecCoreSelectionRules)
110 rule->GetPlayers(item, validPlayers, players);
111
112 CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: matched {0} rules with players", players.size());
113
114 // Process defaults
115
116 // Set video default player. Check whether it's video first (overrule audio and
117 // game check). Also push these players in case it is NOT audio or game either.
118 if (item.IsVideo() || (!item.IsAudio() && !item.IsGame()))
119 {
120 int idx = GetPlayerIndex("videodefaultplayer");
121 if (idx > -1)
122 {
123 std::string eVideoDefault = GetPlayerName(idx);
124 CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding videodefaultplayer (%s)", eVideoDefault.c_str());
125 players.push_back(eVideoDefault);
126 }
127 GetPlayers(players, false, true); // Video-only players
128 GetPlayers(players, true, true); // Audio & video players
129 }
130
131 // Set audio default player
132 // Pushback all audio players in case we don't know the type
133 if (item.IsAudio())
134 {
135 int idx = GetPlayerIndex("audiodefaultplayer");
136 if (idx > -1)
137 {
138 std::string eAudioDefault = GetPlayerName(idx);
139 CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding audiodefaultplayer (%s)", eAudioDefault.c_str());
140 players.push_back(eAudioDefault);
141 }
142 GetPlayers(players, true, false); // Audio-only players
143 GetPlayers(players, true, true); // Audio & video players
144 }
145
146 if (item.IsGame())
147 {
148 CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding retroplayer");
149 players.emplace_back("RetroPlayer");
150 }
151
152 CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: added {0} players", players.size());
153 }
154
GetPlayerIndex(const std::string & strCoreName) const155 int CPlayerCoreFactory::GetPlayerIndex(const std::string& strCoreName) const
156 {
157 CSingleLock lock(m_section);
158 if (!strCoreName.empty())
159 {
160 // Dereference "*default*player" aliases
161 std::string strRealCoreName;
162 if (StringUtils::EqualsNoCase(strCoreName, "audiodefaultplayer"))
163 strRealCoreName = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_audioDefaultPlayer;
164 else if (StringUtils::EqualsNoCase(strCoreName, "videodefaultplayer"))
165 strRealCoreName = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoDefaultPlayer;
166 else
167 strRealCoreName = strCoreName;
168
169 for(size_t i = 0; i < m_vecPlayerConfigs.size(); i++)
170 {
171 if (StringUtils::EqualsNoCase(m_vecPlayerConfigs[i]->GetName(), strRealCoreName))
172 return i;
173 }
174 CLog::Log(LOGWARNING, "CPlayerCoreFactory::GetPlayer(%s): no such player: %s", strCoreName.c_str(), strRealCoreName.c_str());
175 }
176 return -1;
177 }
178
GetPlayerName(size_t idx) const179 std::string CPlayerCoreFactory::GetPlayerName(size_t idx) const
180 {
181 CSingleLock lock(m_section);
182 if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size())
183 return "";
184
185 return m_vecPlayerConfigs[idx]->m_name;
186 }
187
GetPlayers(std::vector<std::string> & players,std::string & type) const188 void CPlayerCoreFactory::GetPlayers(std::vector<std::string>&players, std::string &type) const
189 {
190 CSingleLock lock(m_section);
191 for (auto config: m_vecPlayerConfigs)
192 {
193 if (config->m_type != type)
194 continue;
195 players.push_back(config->m_name);
196 }
197 }
198
GetRemotePlayers(std::vector<std::string> & players) const199 void CPlayerCoreFactory::GetRemotePlayers(std::vector<std::string>&players) const
200 {
201 CSingleLock lock(m_section);
202 for (auto config: m_vecPlayerConfigs)
203 {
204 if (config->m_type != "remote")
205 continue;
206 players.push_back(config->m_name);
207 }
208 }
209
GetPlayerType(const std::string & player) const210 std::string CPlayerCoreFactory::GetPlayerType(const std::string& player) const
211 {
212 CSingleLock lock(m_section);
213 size_t idx = GetPlayerIndex(player);
214
215 if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size())
216 return "";
217
218 return m_vecPlayerConfigs[idx]->m_type;
219 }
220
PlaysAudio(const std::string & player) const221 bool CPlayerCoreFactory::PlaysAudio(const std::string& player) const
222 {
223 CSingleLock lock(m_section);
224 size_t idx = GetPlayerIndex(player);
225
226 if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size())
227 return false;
228
229 return m_vecPlayerConfigs[idx]->m_bPlaysAudio;
230 }
231
PlaysVideo(const std::string & player) const232 bool CPlayerCoreFactory::PlaysVideo(const std::string& player) const
233 {
234 CSingleLock lock(m_section);
235 size_t idx = GetPlayerIndex(player);
236
237 if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size())
238 return false;
239
240 return m_vecPlayerConfigs[idx]->m_bPlaysVideo;
241 }
242
GetDefaultPlayer(const CFileItem & item) const243 std::string CPlayerCoreFactory::GetDefaultPlayer(const CFileItem& item) const
244 {
245 std::vector<std::string>players;
246 GetPlayers(item, players);
247
248 //If we have any players return the first one
249 if (!players.empty())
250 return players.at(0);
251
252 return "";
253 }
254
SelectPlayerDialog(const std::vector<std::string> & players,float posX,float posY) const255 std::string CPlayerCoreFactory::SelectPlayerDialog(const std::vector<std::string>&players, float posX, float posY) const
256 {
257 CContextButtons choices;
258 if (players.size())
259 {
260 //Add default player
261 std::string strCaption = players[0];
262 strCaption += " (";
263 strCaption += g_localizeStrings.Get(13278);
264 strCaption += ")";
265 choices.Add(0, strCaption);
266
267 //Add all other players
268 for (unsigned int i = 1; i < players.size(); i++)
269 choices.Add(i, players[i]);
270
271 int choice = CGUIDialogContextMenu::ShowAndGetChoice(choices);
272 if (choice >= 0)
273 return players[choice];
274 }
275 return "";
276 }
277
SelectPlayerDialog(float posX,float posY) const278 std::string CPlayerCoreFactory::SelectPlayerDialog(float posX, float posY) const
279 {
280 std::vector<std::string>players;
281 GetPlayers(players);
282 return SelectPlayerDialog(players, posX, posY);
283 }
284
LoadConfiguration(const std::string & file,bool clear)285 bool CPlayerCoreFactory::LoadConfiguration(const std::string &file, bool clear)
286 {
287 CSingleLock lock(m_section);
288
289 CLog::Log(LOGINFO, "Loading player core factory settings from %s.", file.c_str());
290 if (!XFILE::CFile::Exists(file))
291 { // tell the user it doesn't exist
292 CLog::Log(LOGINFO, "%s does not exist. Skipping.", file.c_str());
293 return false;
294 }
295
296 CXBMCTinyXML playerCoreFactoryXML;
297 if (!playerCoreFactoryXML.LoadFile(file))
298 {
299 CLog::Log(LOGERROR, "Error loading %s, Line %d (%s)", file.c_str(), playerCoreFactoryXML.ErrorRow(), playerCoreFactoryXML.ErrorDesc());
300 return false;
301 }
302
303 TiXmlElement *pConfig = playerCoreFactoryXML.RootElement();
304 if (pConfig == NULL)
305 {
306 CLog::Log(LOGERROR, "Error loading %s, Bad structure", file.c_str());
307 return false;
308 }
309
310 if (clear)
311 {
312 for (auto config: m_vecPlayerConfigs)
313 delete config;
314 m_vecPlayerConfigs.clear();
315
316 for (auto rule: m_vecCoreSelectionRules)
317 delete rule;
318 m_vecCoreSelectionRules.clear();
319
320 // Builtin players
321 CPlayerCoreConfig* VideoPlayer = new CPlayerCoreConfig("VideoPlayer", "video", nullptr);
322 VideoPlayer->m_bPlaysAudio = true;
323 VideoPlayer->m_bPlaysVideo = true;
324 m_vecPlayerConfigs.push_back(VideoPlayer);
325
326 CPlayerCoreConfig* paplayer = new CPlayerCoreConfig("PAPlayer", "music", nullptr);
327 paplayer->m_bPlaysAudio = true;
328 m_vecPlayerConfigs.push_back(paplayer);
329
330 CPlayerCoreConfig* retroPlayer = new CPlayerCoreConfig("RetroPlayer", "game", nullptr);
331 m_vecPlayerConfigs.push_back(retroPlayer);
332 }
333
334 if (!pConfig || StringUtils::CompareNoCase(pConfig->Value(), "playercorefactory") != 0)
335 {
336 CLog::Log(LOGERROR, "Error loading configuration, no <playercorefactory> node");
337 return false;
338 }
339
340 TiXmlElement *pPlayers = pConfig->FirstChildElement("players");
341 if (pPlayers)
342 {
343 TiXmlElement* pPlayer = pPlayers->FirstChildElement("player");
344 while (pPlayer)
345 {
346 std::string name = XMLUtils::GetAttribute(pPlayer, "name");
347 std::string type = XMLUtils::GetAttribute(pPlayer, "type");
348 if (type.empty()) type = name;
349 StringUtils::ToLower(type);
350
351 std::string internaltype;
352 if (type == "videoplayer")
353 internaltype = "video";
354 else if (type == "paplayer")
355 internaltype = "music";
356 else if (type == "externalplayer")
357 internaltype = "external";
358
359 int count = 0;
360 std::string playername = name;
361 while (GetPlayerIndex(playername) >= 0)
362 {
363 count++;
364 std::stringstream itoa;
365 itoa << count;
366 playername = name + itoa.str();
367 }
368
369 if (!internaltype.empty())
370 {
371 m_vecPlayerConfigs.push_back(new CPlayerCoreConfig(playername, internaltype, pPlayer));
372 }
373
374 pPlayer = pPlayer->NextSiblingElement("player");
375 }
376 }
377
378 TiXmlElement *pRule = pConfig->FirstChildElement("rules");
379 while (pRule)
380 {
381 const char* szAction = pRule->Attribute("action");
382 if (szAction)
383 {
384 if (StringUtils::CompareNoCase(szAction, "append") == 0)
385 {
386 m_vecCoreSelectionRules.push_back(new CPlayerSelectionRule(pRule));
387 }
388 else if (StringUtils::CompareNoCase(szAction, "prepend") == 0)
389 {
390 m_vecCoreSelectionRules.insert(m_vecCoreSelectionRules.begin(), 1, new CPlayerSelectionRule(pRule));
391 }
392 else
393 {
394 m_vecCoreSelectionRules.clear();
395 m_vecCoreSelectionRules.push_back(new CPlayerSelectionRule(pRule));
396 }
397 }
398 else
399 {
400 m_vecCoreSelectionRules.push_back(new CPlayerSelectionRule(pRule));
401 }
402
403 pRule = pRule->NextSiblingElement("rules");
404 }
405
406 // succeeded - tell the user it worked
407 CLog::Log(LOGINFO, "Loaded playercorefactory configuration");
408
409 return true;
410 }
411
OnPlayerDiscovered(const std::string & id,const std::string & name)412 void CPlayerCoreFactory::OnPlayerDiscovered(const std::string& id, const std::string& name)
413 {
414 CSingleLock lock(m_section);
415 std::vector<CPlayerCoreConfig *>::iterator it;
416 for (it = m_vecPlayerConfigs.begin(); it != m_vecPlayerConfigs.end(); ++it)
417 {
418 if ((*it)->GetId() == id)
419 {
420 (*it)->m_name = name;
421 (*it)->m_type = "remote";
422 return;
423 }
424 }
425
426 int count = 0;
427 std::string playername = name;
428 while (GetPlayerIndex(playername) >= 0)
429 {
430 count++;
431 std::stringstream itoa;
432 itoa << count;
433 playername = name + itoa.str();
434 }
435
436 CPlayerCoreConfig* player = new CPlayerCoreConfig(playername, "remote", nullptr, id);
437 player->m_bPlaysAudio = true;
438 player->m_bPlaysVideo = true;
439 m_vecPlayerConfigs.push_back(player);
440 }
441
OnPlayerRemoved(const std::string & id)442 void CPlayerCoreFactory::OnPlayerRemoved(const std::string& id)
443 {
444 CSingleLock lock(m_section);
445 std::vector<CPlayerCoreConfig *>::iterator it;
446 for(it = m_vecPlayerConfigs.begin(); it != m_vecPlayerConfigs.end(); ++it)
447 {
448 if ((*it)->GetId() == id)
449 (*it)->m_type = "";
450 }
451 }
452