diff --git a/addons/library.kodi.guilib/libKODI_guilib.h b/addons/library.kodi.guilib/libKODI_guilib.h index 220f341250592..c5004157004b0 100644 --- a/addons/library.kodi.guilib/libKODI_guilib.h +++ b/addons/library.kodi.guilib/libKODI_guilib.h @@ -825,13 +825,13 @@ class CAddonGUIRenderingControl virtual ~CAddonGUIRenderingControl(); virtual void Init(); - virtual bool Create(int x, int y, int w, int h, void *device); + virtual bool Create(int x, int y, int w, int h, float ratio); virtual void Render(); virtual void Stop(); virtual bool Dirty(); GUIHANDLE m_cbhdl; - bool (*CBCreate)(GUIHANDLE cbhdl, int x, int y, int w, int h, void *device); + bool (*CBCreate)(GUIHANDLE cbhdl, int x, int y, int w, int h, float ratio); void (*CBRender)(GUIHANDLE cbhdl); void (*CBStop)(GUIHANDLE cbhdl); bool (*CBDirty)(GUIHANDLE cbhdl); diff --git a/lib/addons/library.kodi.guilib/libKODI_guilib.cpp b/lib/addons/library.kodi.guilib/libKODI_guilib.cpp index 42ede0a99e1f1..f1e07b45ff020 100644 --- a/lib/addons/library.kodi.guilib/libKODI_guilib.cpp +++ b/lib/addons/library.kodi.guilib/libKODI_guilib.cpp @@ -932,10 +932,10 @@ DLLEXPORT void GUI_control_release_rendering(CAddonGUIRenderingControl* p) delete p; } -DLLEXPORT bool GUI_control_rendering_create(GUIHANDLE handle, int x, int y, int w, int h, void *device) +DLLEXPORT bool GUI_control_rendering_create(GUIHANDLE handle, int x, int y, int w, int h, float ratio) { CAddonGUIRenderingControl *pControl = (CAddonGUIRenderingControl*) handle; - return pControl->Create(x,y,w,h,device); + return pControl->Create(x,y,w,h,ratio); } DLLEXPORT void GUI_control_rendering_render(GUIHANDLE handle) @@ -975,12 +975,12 @@ void CAddonGUIRenderingControl::Init() ((CB_GUILib*)m_cb)->RenderAddon_SetCallbacks(((AddonCB*)m_Handle)->addonData, m_RenderingHandle, this, GUI_control_rendering_create, GUI_control_rendering_render, GUI_control_rendering_stop, GUI_control_rendering_dirty); } -bool CAddonGUIRenderingControl::Create(int x, int y, int w, int h, void *device) +bool CAddonGUIRenderingControl::Create(int x, int y, int w, int h, float ratio) { if (!CBCreate) return false; - return CBCreate(m_cbhdl, x, y, w, h, device); + return CBCreate(m_cbhdl, x, y, w, h, ratio); } void CAddonGUIRenderingControl::Render() diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index 034b1e385fe6e..a47fbaec2e7ab 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -168,6 +168,7 @@ #include "addons/AddonInstaller.h" #include "addons/AddonManager.h" #include "addons/RepositoryUpdater.h" +#include "addons/Visualisation.h" #include "music/tags/MusicInfoTag.h" #include "music/tags/MusicInfoTagLoaderFactory.h" #include "CompileInfo.h" @@ -4680,6 +4681,8 @@ void CApplication::ProcessSlow() CSFTPSessionManager::ClearOutIdleSessions(); #endif + CVisualisationManager::GetInstance().ClearOutIdle(); + g_mediaManager.ProcessEvents(); CAEFactory::GarbageCollect(); diff --git a/xbmc/addons/AddonCallbacks.h b/xbmc/addons/AddonCallbacks.h index 25352d24002af..2cba16620f5a2 100644 --- a/xbmc/addons/AddonCallbacks.h +++ b/xbmc/addons/AddonCallbacks.h @@ -195,7 +195,7 @@ typedef void (*GUIListItem_SetInfo)(void *addonData, GUIHANDLE handle, co typedef void (*GUIListItem_SetProperty)(void *addonData, GUIHANDLE handle, const char *key, const char *value); typedef const char* (*GUIListItem_GetProperty)(void *addonData, GUIHANDLE handle, const char *key); typedef void (*GUIListItem_SetPath)(void *addonData, GUIHANDLE handle, const char *path); -typedef void (*GUIRenderAddon_SetCallbacks)(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*createCB)(GUIHANDLE,int,int,int,int,void*), void (*renderCB)(GUIHANDLE), void (*stopCB)(GUIHANDLE), bool (*dirtyCB)(GUIHANDLE)); +typedef void (*GUIRenderAddon_SetCallbacks)(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*createCB)(GUIHANDLE,int,int,int,int,float), void (*renderCB)(GUIHANDLE), void (*stopCB)(GUIHANDLE), bool (*dirtyCB)(GUIHANDLE)); typedef void (*GUIRenderAddon_Delete)(void *addonData, GUIHANDLE handle); typedef void (*GUIRenderAddon_MarkDirty)(void *addonData, GUIHANDLE handle); diff --git a/xbmc/addons/AddonCallbacksGUI.cpp b/xbmc/addons/AddonCallbacksGUI.cpp index efc36fef5948b..c29efe4e8304c 100644 --- a/xbmc/addons/AddonCallbacksGUI.cpp +++ b/xbmc/addons/AddonCallbacksGUI.cpp @@ -1632,7 +1632,7 @@ void CAddonCallbacksGUI::ListItem_SetPath(void *addonData, GUIHANDLE handle, con ((CFileItem*)handle)->SetPath(path); } -void CAddonCallbacksGUI::RenderAddon_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*createCB)(GUIHANDLE,int,int,int,int,void*), void (*renderCB)(GUIHANDLE), void (*stopCB)(GUIHANDLE), bool (*dirtyCB)(GUIHANDLE)) +void CAddonCallbacksGUI::RenderAddon_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*createCB)(GUIHANDLE,int,int,int,int,float), void (*renderCB)(GUIHANDLE), void (*stopCB)(GUIHANDLE), bool (*dirtyCB)(GUIHANDLE)) { CAddonCallbacks* helper = (CAddonCallbacks*) addonData; if (!helper || !handle) @@ -2236,11 +2236,11 @@ CGUIAddonRenderingControl::CGUIAddonRenderingControl(CGUIRenderingControl *pCont m_refCount{1} { } -bool CGUIAddonRenderingControl::Create(int x, int y, int w, int h, void *device) +bool CGUIAddonRenderingControl::Create(int x, int y, int w, int h, float ratio) { if (CBCreate) { - if (CBCreate(m_clientHandle, x, y, w, h, device)) + if (CBCreate(m_clientHandle, x, y, w, h, ratio)) { m_refCount++; return true; diff --git a/xbmc/addons/AddonCallbacksGUI.h b/xbmc/addons/AddonCallbacksGUI.h index ae032a7873d50..2c8879c9ec71f 100644 --- a/xbmc/addons/AddonCallbacksGUI.h +++ b/xbmc/addons/AddonCallbacksGUI.h @@ -141,7 +141,7 @@ class CAddonCallbacksGUI static void ListItem_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value); static const char * ListItem_GetProperty(void *addonData, GUIHANDLE handle, const char *key); static void ListItem_SetPath(void *addonData, GUIHANDLE handle, const char *path); - static void RenderAddon_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*createCB)(GUIHANDLE,int,int,int,int,void*), void (*renderCB)(GUIHANDLE), void (*stopCB)(GUIHANDLE), bool (*dirtyCB)(GUIHANDLE)); + static void RenderAddon_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*createCB)(GUIHANDLE,int,int,int,int,float), void (*renderCB)(GUIHANDLE), void (*stopCB)(GUIHANDLE), bool (*dirtyCB)(GUIHANDLE)); static void RenderAddon_Delete(void *addonData, GUIHANDLE handle); static void RenderAddon_MarkDirty(void *addonData, GUIHANDLE handle); @@ -253,13 +253,13 @@ friend class CAddonCallbacksGUI; public: CGUIAddonRenderingControl(CGUIRenderingControl *pControl); virtual ~CGUIAddonRenderingControl() {} - virtual bool Create(int x, int y, int w, int h, void *device); + virtual bool Create(int x, int y, int w, int h, float ratio); virtual void Render(); virtual void Stop(); virtual bool IsDirty(); virtual void Delete(); protected: - bool (*CBCreate) (GUIHANDLE cbhdl, int x, int y, int w, int h, void *device); + bool (*CBCreate) (GUIHANDLE cbhdl, int x, int y, int w, int h, float ratio); void (*CBRender)(GUIHANDLE cbhdl); void (*CBStop)(GUIHANDLE cbhdl); bool (*CBDirty)(GUIHANDLE cbhdl); diff --git a/xbmc/addons/Visualisation.cpp b/xbmc/addons/Visualisation.cpp index f37c9ae622ddb..fb5238e61f8a4 100644 --- a/xbmc/addons/Visualisation.cpp +++ b/xbmc/addons/Visualisation.cpp @@ -62,7 +62,7 @@ void CAudioBuffer::Set(const float* psBuffer, int iSize) for (int i = iSize; i < m_iLen; ++i) m_pBuffer[i] = 0; } -bool CVisualisation::Create(int x, int y, int w, int h, void *device) +bool CVisualisation::Create() { m_pInfo = new VIS_PROPS; #ifdef HAS_DX @@ -70,12 +70,6 @@ bool CVisualisation::Create(int x, int y, int w, int h, void *device) #else m_pInfo->device = NULL; #endif - m_pInfo->x = x; - m_pInfo->y = y; - m_pInfo->width = w; - m_pInfo->height = h; - m_pInfo->pixelRatio = g_graphicsContext.GetResInfo().fPixelRatio; - m_pInfo->name = strdup(Name().c_str()); m_pInfo->presets = strdup(CSpecialProtocol::TranslatePath(Path()).c_str()); m_pInfo->profile = strdup(CSpecialProtocol::TranslatePath(Profile()).c_str()); @@ -83,19 +77,6 @@ bool CVisualisation::Create(int x, int y, int w, int h, void *device) if (CAddonDll::Create() == ADDON_STATUS_OK) { - // Start the visualisation - std::string strFile = URIUtils::GetFileName(g_application.CurrentFile()); - CLog::Log(LOGDEBUG, "Visualisation::Start()\n"); - try - { - m_pStruct->Start(m_iChannels, m_iSamplesPerSec, m_iBitsPerSample, strFile.c_str()); - } - catch (std::exception e) - { - HandleException(e, "m_pStruct->Start() (CVisualisation::Create)"); - return false; - } - m_hasPresets = GetPresets(); if (GetSubModules()) @@ -105,22 +86,34 @@ bool CVisualisation::Create(int x, int y, int w, int h, void *device) CreateBuffers(); - CAEFactory::RegisterAudioCallback(this); - return true; } return false; } -void CVisualisation::Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const std::string &strSongName) +bool CVisualisation::Create(int x, int y, int width, int height, float fPixelRatio) +{ + std::string strFile = URIUtils::GetFileName(g_application.CurrentFile()); + Start(x, y, width, height, fPixelRatio, m_iChannels, + m_iSamplesPerSec, m_iBitsPerSample, strFile); +} + +void CVisualisation::Start(int x, int y, int width, int height, float pixelRatio, + int iChannels, int iSamplesPerSec, int iBitsPerSample, const std::string &strSongName) { // notify visz. that new song has been started // pass it the nr of audio channels, sample rate, bits/sample and offcourse the songname + // and the screen info + + // Start the visualisation if (Initialized()) { + CAEFactory::RegisterAudioCallback(this); + try { - m_pStruct->Start(iChannels, iSamplesPerSec, iBitsPerSample, strSongName.c_str()); + m_pStruct->Start(x, y, width, height, pixelRatio, + iChannels, iSamplesPerSec, iBitsPerSample, strSongName.c_str()); } catch (std::exception e) { @@ -473,3 +466,53 @@ std::string CVisualisation::GetPresetName() return ""; } + +CVisualisationManager::CVisualisationManager() +{ +} + +CVisualisationManager& CVisualisationManager::GetInstance() +{ + static CVisualisationManager instance; + + return instance; +} + +VisualisationPtr CVisualisationManager::GetAddon(const std::string& id, int slot) +{ + if (m_addons.find(slot) == m_addons.end() || + m_addons[slot].first->ID() != id) + { + AddonPtr addon; + CAddonMgr::GetInstance().GetAddon(id, addon, ADDON_VIZ); + if (addon) + m_addons[slot].first = std::static_pointer_cast(addon); + else + return VisualisationPtr(); + m_addons[slot].first->Create(); + } + + m_addons[slot].second.Stop(); + return m_addons[slot].first; +} + +void CVisualisationManager::Release(int slot) +{ + m_addons[slot].second.StartZero(); +} + +void CVisualisationManager::ClearOutIdle() +{ + std::vector remove; + for (auto& it : m_addons) + { + if (it.second.second.GetElapsedSeconds() > 30) + remove.push_back(it.first); + } + + for (auto& i : remove) + { + CLog::Log(LOGDEBUG, "Visualization %s in slot %i has been IDLE for 30 secs, stopping..", m_addons[i].first->ID().c_str(), i); + m_addons.erase(i); + } +} diff --git a/xbmc/addons/Visualisation.h b/xbmc/addons/Visualisation.h index b0b4e6513efaf..93a551dfff53f 100644 --- a/xbmc/addons/Visualisation.h +++ b/xbmc/addons/Visualisation.h @@ -24,6 +24,7 @@ #include "include/xbmc_vis_types.h" #include "guilib/IRenderingCallback.h" #include "utils/rfft.h" +#include "utils/Stopwatch.h" #include #include @@ -61,8 +62,10 @@ namespace ADDON CVisualisation(const cp_extension_t *ext) : CAddonDll(ext) {} virtual void OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPerSample); virtual void OnAudioData(const float* pAudioData, int iAudioDataLength); - bool Create(int x, int y, int w, int h, void *device); - void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const std::string &strSongName); + bool Create(); + virtual bool Create(int x, int y, int w, int h, float pixelRatio); + void Start(int x, int y, int w, int h, float pixelRatio, + int iChannels, int iSamplesPerSec, int iBitsPerSample, const std::string &strSongName); void AudioData(const float *pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength); void Render(); void Stop(); @@ -113,4 +116,32 @@ namespace ADDON // track information std::string m_AlbumThumb; }; + + typedef std::shared_ptr VisualisationPtr; + + //! \brief Class managing visualisation instances. + class CVisualisationManager + { + public: + //! \brief Singleton accessor + static CVisualisationManager& GetInstance(); + + //! \brief Obtain an addon instance. + //! \param id ID of the addon to obtain. + //! \param slot Slot to put addon in. + VisualisationPtr GetAddon(const std::string& id, int slot=0); + + //! \brief Release an addon. + //! \param slot Slot to release. + void Release(int slot); + + //! \brief Clear out instances which have been idle for more than 30 secs. + void ClearOutIdle(); + protected: + //! \brief Protected construction. Use singleton accessor. + CVisualisationManager(); + + //! \brief Mapsslot -> (addon, idle timer). + std::map> m_addons; + }; } diff --git a/xbmc/addons/include/xbmc_vis_dll.h b/xbmc/addons/include/xbmc_vis_dll.h index c65f8449585a8..8ff6ff2ccc400 100644 --- a/xbmc/addons/include/xbmc_vis_dll.h +++ b/xbmc/addons/include/xbmc_vis_dll.h @@ -27,7 +27,8 @@ extern "C" { // Functions that your visualisation must implement - void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const char* szSongName); + void Start(int x, int y, int width, int height, float pixelRatio, + int iChannels, int iSamplesPerSec, int iBitsPerSample, const char* szSongName); void AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength); void Render(); bool OnAction(long action, const void *param); diff --git a/xbmc/addons/include/xbmc_vis_types.h b/xbmc/addons/include/xbmc_vis_types.h index e6b0ccd012a2e..fbc9d729cc99a 100644 --- a/xbmc/addons/include/xbmc_vis_types.h +++ b/xbmc/addons/include/xbmc_vis_types.h @@ -37,11 +37,6 @@ extern "C" struct VIS_PROPS { void *device; - int x; - int y; - int width; - int height; - float pixelRatio; const char *name; const char *presets; const char *profile; @@ -95,7 +90,9 @@ extern "C" struct Visualisation { - void (__cdecl* Start)(int iChannels, int iSamplesPerSec, int iBitsPerSample, const char* szSongName); + void (__cdecl* Start)(int x, int y, int width, int height, float pixelRatio, + int iChannels, int iSamplesPerSec, int iBitsPerSample, + const char* szSongName); void (__cdecl* AudioData)(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength); void (__cdecl* Render) (); void (__cdecl* GetInfo)(VIS_INFO *info); diff --git a/xbmc/guilib/GUIControlFactory.cpp b/xbmc/guilib/GUIControlFactory.cpp index 7c72c152f0725..61fcea8b636c7 100644 --- a/xbmc/guilib/GUIControlFactory.cpp +++ b/xbmc/guilib/GUIControlFactory.cpp @@ -1473,7 +1473,13 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl } break; case CGUIControl::GUICONTROL_VISUALISATION: - control = new CGUIVisualisationControl(parentID, id, posX, posY, width, height); + { + int slot=0; + XMLUtils::GetInt(pControlNode, "slot", slot); + std::string aid; + XMLUtils::GetString(pControlNode, "id", aid); + control = new CGUIVisualisationControl(parentID, id, posX, posY, width, height, slot, aid); + } break; case CGUIControl::GUICONTROL_RENDERADDON: control = new CGUIRenderingControl(parentID, id, posX, posY, width, height); diff --git a/xbmc/guilib/GUIRenderingControl.cpp b/xbmc/guilib/GUIRenderingControl.cpp index 143bb8fd71491..28bea71401d28 100644 --- a/xbmc/guilib/GUIRenderingControl.cpp +++ b/xbmc/guilib/GUIRenderingControl.cpp @@ -57,11 +57,8 @@ bool CGUIRenderingControl::InitCallback(IRenderingCallback *callback) if (x + w > g_graphicsContext.GetWidth()) w = g_graphicsContext.GetWidth() - x; if (y + h > g_graphicsContext.GetHeight()) h = g_graphicsContext.GetHeight() - y; - void *device = NULL; -#if HAS_DX - device = g_Windowing.Get3D11Device(); -#endif - if (callback->Create((int)(x+0.5f), (int)(y+0.5f), (int)(w+0.5f), (int)(h+0.5f), device)) + if (callback->Create((int)(x+0.5f), (int)(y+0.5f), (int)(w+0.5f), (int)(h+0.5f), + g_graphicsContext.GetResInfo().fPixelRatio)) m_callback = callback; else return false; diff --git a/xbmc/guilib/GUIVisualisationControl.cpp b/xbmc/guilib/GUIVisualisationControl.cpp index cde54c17dc326..89a22e1aeb7f5 100644 --- a/xbmc/guilib/GUIVisualisationControl.cpp +++ b/xbmc/guilib/GUIVisualisationControl.cpp @@ -26,6 +26,7 @@ #include "addons/Visualisation.h" #include "utils/log.h" #include "input/Key.h" +#include "settings/Settings.h" using namespace ADDON; @@ -33,8 +34,8 @@ using namespace ADDON; #define LABEL_ROW2 11 #define LABEL_ROW3 12 -CGUIVisualisationControl::CGUIVisualisationControl(int parentID, int controlID, float posX, float posY, float width, float height) - : CGUIRenderingControl(parentID, controlID, posX, posY, width, height), m_bAttemptedLoad(false) +CGUIVisualisationControl::CGUIVisualisationControl(int parentID, int controlID, float posX, float posY, float width, float height, int slot, const std::string& id) + : CGUIRenderingControl(parentID, controlID, posX, posY, width, height), m_bAttemptedLoad(false), m_slot(slot), m_id(id) { ControlType = GUICONTROL_VISUALISATION; } @@ -99,8 +100,9 @@ void CGUIVisualisationControl::Process(unsigned int currentTime, CDirtyRegionLis if (!m_addon && !m_bAttemptedLoad) { - AddonPtr addon; - if (ADDON::CAddonMgr::GetInstance().GetDefault(ADDON_VIZ, addon)) + std::string id = m_id.empty()?CSettings::GetInstance().GetString(CSettings::SETTING_MUSICPLAYER_VISUALISATION):m_id; + AddonPtr addon = ADDON::CVisualisationManager::GetInstance().GetAddon(id, m_slot); + if (addon) { m_addon = std::dynamic_pointer_cast(addon); if (m_addon) @@ -125,6 +127,8 @@ void CGUIVisualisationControl::FreeResources(bool immediately) g_windowManager.SendMessage(msg); CLog::Log(LOGDEBUG, "FreeVisualisation() started"); CGUIRenderingControl::FreeResources(immediately); + m_addon->Stop(); + CVisualisationManager::GetInstance().Release(m_slot); m_addon.reset(); CLog::Log(LOGDEBUG, "FreeVisualisation() done"); } diff --git a/xbmc/guilib/GUIVisualisationControl.h b/xbmc/guilib/GUIVisualisationControl.h index 1abdcffacfbba..b4873d304ea8e 100644 --- a/xbmc/guilib/GUIVisualisationControl.h +++ b/xbmc/guilib/GUIVisualisationControl.h @@ -25,7 +25,7 @@ class CGUIVisualisationControl : public CGUIRenderingControl { public: - CGUIVisualisationControl(int parentID, int controlID, float posX, float posY, float width, float height); + CGUIVisualisationControl(int parentID, int controlID, float posX, float posY, float width, float height, int slot, const std::string& id); CGUIVisualisationControl(const CGUIVisualisationControl &from); virtual CGUIVisualisationControl *Clone() const { return new CGUIVisualisationControl(*this); }; //TODO check for naughties virtual void FreeResources(bool immediately = false); @@ -35,4 +35,6 @@ class CGUIVisualisationControl : public CGUIRenderingControl private: bool m_bAttemptedLoad; ADDON::VizPtr m_addon; + int m_slot; + std::string m_id; }; diff --git a/xbmc/guilib/IRenderingCallback.h b/xbmc/guilib/IRenderingCallback.h index a12a563e60119..328105702102c 100644 --- a/xbmc/guilib/IRenderingCallback.h +++ b/xbmc/guilib/IRenderingCallback.h @@ -23,7 +23,7 @@ class IRenderingCallback { public: - virtual bool Create(int x, int y, int w, int h, void *device) = 0; + virtual bool Create(int x, int y, int w, int h, float pixelRatio) = 0; virtual void Render() = 0; virtual void Stop() = 0; virtual bool IsDirty() { return true; }