diff options
121 files changed, 2917 insertions, 520 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 67e10f71c6..bfd702f500 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,13 +35,9 @@ include(cmake/scripts/common/Macros.cmake) include(cmake/scripts/common/ProjectMacros.cmake) core_find_versions() include(cmake/scripts/${CORE_SYSTEM_NAME}/PathSetup.cmake) +include(cmake/scripts/common/CompilerSettings.cmake) include(ExternalProject) -# Languages and global compiler settings -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp") # general option(VERBOSE "Enable verbose output?" OFF) diff --git a/addons/repository.xbmc.org/addon.xml b/addons/repository.xbmc.org/addon.xml index c431c0fbca..a80f963e93 100644 --- a/addons/repository.xbmc.org/addon.xml +++ b/addons/repository.xbmc.org/addon.xml @@ -1,17 +1,17 @@ <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <addon id="repository.xbmc.org" name="Kodi Add-on repository" - version="3.1.5" + version="3.2.0" provider-name="Team Kodi"> <requires> <import addon="xbmc.addon" version="12.0.0"/> </requires> <extension point="xbmc.addon.repository"> <!-- Do not forget to bump add-on version when changing paths to force a repo refresh --> - <info>http://mirrors.kodi.tv/addons/leia/addons.xml.gz</info> - <checksum verify="sha256">http://mirrors.kodi.tv/addons/leia/addons.xml.gz?sha256</checksum> - <datadir>https://mirrors.kodi.tv/addons/leia</datadir> - <artdir>http://mirrors.kodi.tv/addons/leia</artdir> + <info>http://mirrors.kodi.tv/addons/matrix/addons.xml.gz</info> + <checksum verify="sha256">http://mirrors.kodi.tv/addons/matrix/addons.xml.gz?sha256</checksum> + <datadir>https://mirrors.kodi.tv/addons/matrix</datadir> + <artdir>http://mirrors.kodi.tv/addons/matrix</artdir> <hashes>sha256</hashes> </extension> <extension point="xbmc.addon.metadata"> diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po index 24c1e5d635..c7365ab87c 100644 --- a/addons/resource.language.en_gb/resources/strings.po +++ b/addons/resource.language.en_gb/resources/strings.po @@ -9587,10 +9587,10 @@ msgctxt "#19071" msgid "Update interval" msgstr "" -#. pvr settings "do not store epg data in local database" setting label +#. pvr settings "store epg data in database" setting label #: system/settings/settings.xml msgctxt "#19072" -msgid "Don't cache in local database" +msgid "Store data in database" msgstr "" #. pvr settings "delay channel switch" setting label @@ -18682,7 +18682,7 @@ msgstr "" #: system/settings/settings.xml msgctxt "#36223" -msgid "By default, the guide data is cached in a local database to speed up importing on startup." +msgid "Activate to store epg data in a database. This may speedup importing epg data on startup." msgstr "" #: system/settings/settings.xml diff --git a/addons/screensaver.xbmc.builtin.black/addon.xml b/addons/screensaver.xbmc.builtin.black/addon.xml.in index ed951a71e5..44ffa0600c 100644 --- a/addons/screensaver.xbmc.builtin.black/addon.xml +++ b/addons/screensaver.xbmc.builtin.black/addon.xml.in @@ -3,7 +3,7 @@ name="Black" version="1.0.32" provider-name="Team Kodi"> - <extension point="xbmc.ui.screensaver" library=""/> + <extension point="xbmc.ui.screensaver" library_@CORE_SYSTEM_NAME@="dummy.so"/> <extension point="xbmc.addon.metadata"> <summary lang="af_ZA">Sluimerskerm wat jou skerm swart maak.</summary> <summary lang="am_ET">መመልከቻ ማዳኛ የእርስዎን መመልከቻ ጥቁር ያደርገዋል </summary> diff --git a/addons/screensaver.xbmc.builtin.dim/addon.xml b/addons/screensaver.xbmc.builtin.dim/addon.xml.in index 963395d46a..c43cbd8330 100644 --- a/addons/screensaver.xbmc.builtin.dim/addon.xml +++ b/addons/screensaver.xbmc.builtin.dim/addon.xml.in @@ -3,7 +3,7 @@ name="Dim" version="1.0.54" provider-name="Team Kodi"> - <extension point="xbmc.ui.screensaver" library=""/> + <extension point="xbmc.ui.screensaver" library_@CORE_SYSTEM_NAME@="dummy.so"/> <extension point="xbmc.addon.metadata"> <summary lang="af_ZA">Sluimerskerm wat jou skerm verdof</summary> <summary lang="am_ET">መመልከቻ ማዳኛ የ እርስዎን መመልከቻ ፈዘዝ ያደርገዋል </summary> diff --git a/addons/skin.estouchy/media/epg_archive.png b/addons/skin.estouchy/media/epg_archive.png Binary files differnew file mode 100644 index 0000000000..bf02128d67 --- /dev/null +++ b/addons/skin.estouchy/media/epg_archive.png diff --git a/addons/skin.estouchy/xml/ViewsPVR.xml b/addons/skin.estouchy/xml/ViewsPVR.xml index a9290088c5..4b13e86584 100644 --- a/addons/skin.estouchy/xml/ViewsPVR.xml +++ b/addons/skin.estouchy/xml/ViewsPVR.xml @@ -324,6 +324,14 @@ <texture>epg_schedule.png</texture> <visible>ListItem.HasTimer</visible> </control> + <control type="image"> + <posx>5</posx> + <posy>45</posy> + <width>20</width> + <height>20</height> + <texture>epg_archive.png</texture> + <visible>![ListItem.IsRecording | ListItem.HasTimer] + ListItem.IsPlayable</visible> + </control> </itemlayout> <focusedlayout width="40" height="65"> <control type="image" id="2"> @@ -370,6 +378,14 @@ <texture>epg_schedule.png</texture> <visible>ListItem.HasTimer</visible> </control> + <control type="image"> + <posx>7</posx> + <posy>44</posy> + <width>15</width> + <height>15</height> + <texture>epg_archive.png</texture> + <visible>![ListItem.IsRecording | ListItem.HasTimer] + ListItem.IsPlayable</visible> + </control> </focusedlayout> </control> </include> diff --git a/addons/skin.estuary/media/icons/pvr/PVR-HasArchive.png b/addons/skin.estuary/media/icons/pvr/PVR-HasArchive.png Binary files differnew file mode 100644 index 0000000000..1eebcf8a71 --- /dev/null +++ b/addons/skin.estuary/media/icons/pvr/PVR-HasArchive.png diff --git a/addons/skin.estuary/media/windows/pvr/archive.png b/addons/skin.estuary/media/windows/pvr/archive.png Binary files differnew file mode 100644 index 0000000000..a832a88821 --- /dev/null +++ b/addons/skin.estuary/media/windows/pvr/archive.png diff --git a/addons/skin.estuary/xml/Home.xml b/addons/skin.estuary/xml/Home.xml index 410eceb59b..6964c72db6 100644 --- a/addons/skin.estuary/xml/Home.xml +++ b/addons/skin.estuary/xml/Home.xml @@ -527,7 +527,7 @@ <orientation>vertical</orientation> <pagecontrol>14010</pagecontrol> <visible>Integer.IsGreater(Container(14100).NumItems,0) | Container(14100).IsUpdating</visible> - <itemlayout width="330" height="396"> + <itemlayout width="330" height="401"> <control type="group"> <top>130</top> <include content="InfoWallMusicLayout"> @@ -536,7 +536,7 @@ </include> </control> </itemlayout> - <focusedlayout width="330" height="396"> + <focusedlayout width="330" height="401"> <control type="group"> <depth>DepthContentPopout</depth> <top>130</top> diff --git a/addons/skin.estuary/xml/Variables.xml b/addons/skin.estuary/xml/Variables.xml index 5ae334f527..3bcb9945f8 100644 --- a/addons/skin.estuary/xml/Variables.xml +++ b/addons/skin.estuary/xml/Variables.xml @@ -3,6 +3,7 @@ <variable name="PVRStatusImageVar"> <value condition="ListItem.IsRecording">windows/pvr/record.png</value> <value condition="ListItem.HasTimer | ListItem.HasTimerSchedule">windows/pvr/timer.png</value> + <value condition="ListItem.HasArchive">windows/pvr/archive.png</value> </variable> <variable name="AutoCompletionContentVar"> <value condition="System.HasAddon(plugin.program.autocompletion) + !System.HasHiddenInput">plugin://plugin.program.autocompletion?info=autocomplete&&id=$INFO[Control.GetLabel(312).index(1)]&&limit=9</value> @@ -338,6 +339,7 @@ <value condition="ListItem.IsCollection">overlays/set.png</value> <value condition="ListItem.IsPlaying">overlays/watched/OverlayPlaying-List.png</value> <value condition="ListItem.IsResumable">overlays/watched/resume.png</value> + <value condition="ListItem.HasArchive">windows/pvr/archive.png</value> <value condition="Integer.IsGreater(ListItem.Playcount,0)">$INFO[ListItem.Overlay]</value> </variable> <!-- Breadcrumbs --> @@ -430,6 +432,7 @@ <value condition="ListItem.HasTimer + !ListItem.TimerIsActive">icons/pvr/PVR-HasTimerDisabled.png</value> <value condition="ListItem.HasTimerSchedule">icons/pvr/PVR-HasTimerSchedule.png</value> <value condition="ListItem.HasTimer">icons/pvr/PVR-HasTimer.png</value> + <value condition="ListItem.IsPlayable">icons/pvr/PVR-HasArchive.png</value> </variable> <variable name="SeasonEpisodeLabel"> <value condition="String.IsEmpty(ListItem.EpisodeName)">$INFO[ListItem.Season,S]$INFO[ListItem.Episode,E]</value> diff --git a/cmake/addons/CMakeLists.txt b/cmake/addons/CMakeLists.txt index 5fc798b545..8f0420f945 100644 --- a/cmake/addons/CMakeLists.txt +++ b/cmake/addons/CMakeLists.txt @@ -80,6 +80,8 @@ if (CMAKE_SYSTEM_NAME STREQUAL WindowsStore) -DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}) endif() +include(${CORE_SOURCE_DIR}/cmake/scripts/common/CompilerSettings.cmake) + set(BUILD_ARGS -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DPACKAGE_CONFIG_PATH=${ADDON_DEPENDS_PATH}/lib/pkgconfig diff --git a/cmake/addons/depends/common/kodi-platform/kodi-platform.txt b/cmake/addons/depends/common/kodi-platform/kodi-platform.txt index c553769683..de8cd225fe 100644 --- a/cmake/addons/depends/common/kodi-platform/kodi-platform.txt +++ b/cmake/addons/depends/common/kodi-platform/kodi-platform.txt @@ -1 +1 @@ -kodi-platform https://github.com/xbmc/kodi-platform e8574b883ffa2131f2eeb96ff3724d60b21130f7 +kodi-platform https://github.com/xbmc/kodi-platform 915da086fa7b4ea72796052a04ed6de95501b95c diff --git a/cmake/scripts/common/CompilerSettings.cmake b/cmake/scripts/common/CompilerSettings.cmake new file mode 100644 index 0000000000..48cc69bd65 --- /dev/null +++ b/cmake/scripts/common/CompilerSettings.cmake @@ -0,0 +1,6 @@ +# Languages and global compiler settings +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp") + diff --git a/cmake/treedata/common/subdirs.txt b/cmake/treedata/common/subdirs.txt index 083e8ef314..cd82b5a0e4 100644 --- a/cmake/treedata/common/subdirs.txt +++ b/cmake/treedata/common/subdirs.txt @@ -11,6 +11,7 @@ xbmc/addons/kodi-addon-dev-kit/include/kodi/addon-instance addons_kodi-addon-dev xbmc/addons/kodi-addon-dev-kit/include/kodi/gui addons_kodi-addon-dev-kit_include_kodi_gui xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/controls addons_kodi-addon-dev-kit_include_kodi_gui_controls xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/dialogs addons_kodi-addon-dev-kit_include_kodi_gui_dialogs +xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/gl addons_kodi-addon-dev-kit_include_kodi_gui_gl xbmc/addons/kodi-addon-dev-kit/include/kodi/tools addons_kodi-addon-dev-kit_include_kodi_tools xbmc/addons/settings addons_settings xbmc/commons commons diff --git a/cmake/treedata/optional/common/gbm.txt b/cmake/treedata/optional/common/gbm.txt index 5d898d40b2..ff07e6fbc9 100644 --- a/cmake/treedata/optional/common/gbm.txt +++ b/cmake/treedata/optional/common/gbm.txt @@ -1,2 +1,3 @@ xbmc/cores/RetroPlayer/process/gbm cores/RetroPlayer/process/gbm # GBM +xbmc/cores/VideoPlayer/Process/gbm cores/VideoPlayer/Process/gbm # GBM xbmc/windowing/gbm windowing/gbm # GBM diff --git a/system/settings/settings.xml b/system/settings/settings.xml index b924e7d808..9df19c2d43 100755 --- a/system/settings/settings.xml +++ b/system/settings/settings.xml @@ -1391,7 +1391,7 @@ <default>false</default> <control type="toggle" /> </setting> - <setting id="epg.ignoredbforclient" type="boolean" label="19072" help="36223"> + <setting id="epg.storeepgindatabase" type="boolean" label="19072" help="36223"> <level>2</level> <default>false</default> <control type="toggle" /> diff --git a/system/shaders/GL/1.2/gl_convolution-4x4.glsl b/system/shaders/GL/1.2/gl_convolution-4x4.glsl index 0f580fea41..32d955d8fb 100644 --- a/system/shaders/GL/1.2/gl_convolution-4x4.glsl +++ b/system/shaders/GL/1.2/gl_convolution-4x4.glsl @@ -35,7 +35,7 @@ uniform sampler1D kernelTex; half4 weight(float pos) { -#if (HAS_FLOAT_TEXTURE) +#if defined(HAS_FLOAT_TEXTURE) return texture1D(kernelTex, pos); #else return texture1D(kernelTex, pos) * 2.0 - 1.0; diff --git a/system/shaders/GL/1.2/gl_convolution-6x6.glsl b/system/shaders/GL/1.2/gl_convolution-6x6.glsl index 057d6dfed8..01328a0c11 100644 --- a/system/shaders/GL/1.2/gl_convolution-6x6.glsl +++ b/system/shaders/GL/1.2/gl_convolution-6x6.glsl @@ -35,7 +35,7 @@ uniform sampler1D kernelTex; half3 weight(float pos) { -#if (HAS_FLOAT_TEXTURE) +#if defined(HAS_FLOAT_TEXTURE) return texture1D(kernelTex, pos).rgb; #else return texture1D(kernelTex, pos).rgb * 2.0 - 1.0; diff --git a/system/shaders/GL/1.5/gl_convolution-4x4.glsl b/system/shaders/GL/1.5/gl_convolution-4x4.glsl index 84e92c6967..144676ee27 100644 --- a/system/shaders/GL/1.5/gl_convolution-4x4.glsl +++ b/system/shaders/GL/1.5/gl_convolution-4x4.glsl @@ -18,7 +18,7 @@ uniform sampler1D kernelTex; half4 weight(float pos) { -#if (HAS_FLOAT_TEXTURE) +#if defined(HAS_FLOAT_TEXTURE) return texture(kernelTex, pos); #else return texture(kernelTex, pos) * 2.0 - 1.0; diff --git a/system/shaders/GL/1.5/gl_convolution-6x6.glsl b/system/shaders/GL/1.5/gl_convolution-6x6.glsl index 950192d496..4af3744ae1 100644 --- a/system/shaders/GL/1.5/gl_convolution-6x6.glsl +++ b/system/shaders/GL/1.5/gl_convolution-6x6.glsl @@ -18,7 +18,7 @@ uniform sampler1D kernelTex; half3 weight(float pos) { -#if (HAS_FLOAT_TEXTURE) +#if defined(HAS_FLOAT_TEXTURE) return texture(kernelTex, pos).rgb; #else return texture(kernelTex, pos).rgb * 2.0 - 1.0; diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index 1c4f5dfcd0..c0ae967e7e 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -3085,6 +3085,10 @@ void CApplication::OnPlayerCloseFile(const CFileItem &file, const CBookmark &boo bool playCountUpdate = false; float percent = 0.0f; + // Make sure we don't reset existing bookmark etc. on eg. player start failure + if (bookmark.timeInSeconds == 0.0f) + return; + if (m_stackHelper.GetRegisteredStack(fileItem) != nullptr && m_stackHelper.GetRegisteredStackTotalTimeMs(fileItem) > 0) { // regular stack case: we have to save the bookmark on the stack diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp index f44a5f5515..bc533ac54b 100644 --- a/xbmc/GUIInfoManager.cpp +++ b/xbmc/GUIInfoManager.cpp @@ -3685,6 +3685,22 @@ const infomap container_str[] = {{ "property", CONTAINER_PROPERTY }, /// @return **True** when the selected programme is being recorded (PVR). /// <p> /// } +/// \table_row3{ <b>`ListItem.IsPlayable`</b>, +/// \anchor ListItem_IsPlayable +/// _boolean_, +/// @return **True** when the selected programme can be played (PVR) +/// <p><hr> +/// @skinning_v19 **[New Boolean Condition]** \link ListItem_IsPlayable `ListItem.IsPlayable`\endlink +/// <p> +/// } +/// \table_row3{ <b>`ListItem.HasArchive`</b>, +/// \anchor ListItem_HasArchive +/// _boolean_, +/// @return **True** when the selected channel has a server-side back buffer (PVR) +/// <p><hr> +/// @skinning_v19 **[New Boolean Condition]** \link ListItem_HasArchive `ListItem.HasArchive`\endlink +/// <p> +/// } /// \table_row3{ <b>`ListItem.IsEncrypted`</b>, /// \anchor ListItem_IsEncrypted /// _boolean_, @@ -5814,6 +5830,8 @@ const infomap listitem_labels[]= {{ "thumb", LISTITEM_THUMB }, { "hasreminderrule", LISTITEM_HASREMINDERRULE }, { "hasrecording", LISTITEM_HASRECORDING }, { "isrecording", LISTITEM_ISRECORDING }, + { "isplayable", LISTITEM_ISPLAYABLE }, + { "hasarchive", LISTITEM_HASARCHIVE }, { "inprogress", LISTITEM_INPROGRESS }, { "isencrypted", LISTITEM_ISENCRYPTED }, { "progress", LISTITEM_PROGRESS }, diff --git a/xbmc/addons/PVRClient.cpp b/xbmc/addons/PVRClient.cpp index 364a7f3c42..1ec29dfe59 100644 --- a/xbmc/addons/PVRClient.cpp +++ b/xbmc/addons/PVRClient.cpp @@ -591,25 +591,13 @@ PVR_ERROR CPVRClient::RenameChannel(const CPVRChannelPtr &channel) }, m_clientCapabilities.SupportsChannelSettings()); } -PVR_ERROR CPVRClient::GetEPGForChannel(const std::shared_ptr<CPVREpgChannelData>& channelData, +PVR_ERROR CPVRClient::GetEPGForChannel(int iChannelUid, CPVREpg* epg, time_t start /* = 0 */, time_t end /* = 0 */, bool bSaveInDb /* = false */) { - return DoAddonCall(__FUNCTION__, [this, channelData, epg, start, end, bSaveInDb](const AddonInstance* addon) { - - //! @todo PVR Addon API Change: Change GetEPGForChannel param from 'PVR_CHANNEL channel' to 'int iUniqueId'. - PVR_CHANNEL addonChannel = {0}; - - // mandatory - addonChannel.iUniqueId = channelData->UniqueClientChannelId(); - addonChannel.bIsRadio = channelData->IsRadio(); - - // optional - strncpy(addonChannel.strChannelName, channelData->ChannelName().c_str(), sizeof(addonChannel.strChannelName) - 1); - strncpy(addonChannel.strIconPath, channelData->IconPath().c_str(), sizeof(addonChannel.strIconPath) - 1); - addonChannel.bIsHidden = channelData->IsHidden(); + return DoAddonCall(__FUNCTION__, [this, iChannelUid, epg, start, end, bSaveInDb](const AddonInstance* addon) { ADDON_HANDLE_STRUCT handle; handle.callerAddress = this; @@ -619,7 +607,7 @@ PVR_ERROR CPVRClient::GetEPGForChannel(const std::shared_ptr<CPVREpgChannelData> int iPVRTimeCorrection = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection; return addon->GetEPGForChannel(&handle, - addonChannel, + iChannelUid, start ? start - iPVRTimeCorrection : 0, end ? end - iPVRTimeCorrection : 0); }, m_clientCapabilities.SupportsEPG()); @@ -674,7 +662,6 @@ public: iFlags = kodiTag->Flags(); iGenreType = kodiTag->GenreType(); iGenreSubType = kodiTag->GenreSubType(); - bNotify = kodiTag->Notify(); strTitle = m_strTitle.c_str(); strPlotOutline = m_strPlotOutline.c_str(); strPlot = m_strPlot.c_str(); @@ -1306,6 +1293,14 @@ PVR_ERROR CPVRClient::SetSpeed(int speed) }); } +PVR_ERROR CPVRClient::FillBuffer(bool mode) +{ + return DoAddonCall(__FUNCTION__, [mode](const AddonInstance* addon) { + addon->FillBuffer(mode); + return PVR_ERROR_NO_ERROR; + }); +} + PVR_ERROR CPVRClient::CanPauseStream(bool &bCanPause) const { bCanPause = false; diff --git a/xbmc/addons/PVRClient.h b/xbmc/addons/PVRClient.h index 604ad2b166..8bf7b01839 100644 --- a/xbmc/addons/PVRClient.h +++ b/xbmc/addons/PVRClient.h @@ -401,14 +401,14 @@ namespace PVR /*! * @brief Request an EPG table for a channel from the client. - * @param channelData The data for the channel to get the EPG table for. + * @param iChannelUid The UID of the channel to get the EPG table for. * @param epg The table to write the data to. * @param start The start time to use. * @param end The end time to use. * @param bSaveInDb If true, tell the callback method to save any new entry in the database or not. see CAddonCallbacksPVR::PVRTransferEpgEntry() * @return PVR_ERROR_NO_ERROR if the table has been fetched successfully. */ - PVR_ERROR GetEPGForChannel(const std::shared_ptr<CPVREpgChannelData>& channelData, CPVREpg* epg, time_t start = 0, time_t end = 0, bool bSaveInDb = false); + PVR_ERROR GetEPGForChannel(int iChannelUid, CPVREpg* epg, time_t start = 0, time_t end = 0, bool bSaveInDb = false); /*! * Tell the client the time frame to use when notifying epg events back to Kodi. The client might push epg events asynchronously @@ -706,6 +706,14 @@ namespace PVR */ PVR_ERROR SetSpeed(int speed); + /*! + * @brief Notify the pvr addon/demuxer that Kodi wishes to fill demux queue + * @param mode for setting on/off + * @return PVR_ERROR_NO_ERROR on success, respective error code otherwise. + * @remarks Optional, and only used if addon has its own demuxer. + */ + PVR_ERROR FillBuffer(bool mode); + //@} /** @name PVR recording stream methods */ //@{ diff --git a/xbmc/addons/ScreenSaver.cpp b/xbmc/addons/ScreenSaver.cpp index e2653042ba..27af4be8e2 100644 --- a/xbmc/addons/ScreenSaver.cpp +++ b/xbmc/addons/ScreenSaver.cpp @@ -11,9 +11,6 @@ #include "windowing/GraphicContext.h" #include "windowing/WinSystem.h" #include "utils/log.h" -#ifdef TARGET_WINDOWS -#include "rendering/dx/DeviceResources.h" -#endif namespace ADDON { @@ -26,13 +23,9 @@ CScreenSaver::CScreenSaver(BinaryAddonBasePtr addonBase) m_profile = CSpecialProtocol::TranslatePath(Profile()); m_struct = {{0}}; -#ifdef TARGET_WINDOWS - m_struct.props.device = DX::DeviceResources::Get()->GetD3DContext(); -#else - m_struct.props.device = nullptr; -#endif m_struct.props.x = 0; m_struct.props.y = 0; + m_struct.props.device = CServiceBroker::GetWinSystem()->GetHWContext(); m_struct.props.width = CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(); m_struct.props.height = CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight(); m_struct.props.pixelRatio = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo().fPixelRatio; diff --git a/xbmc/addons/Visualization.cpp b/xbmc/addons/Visualization.cpp index 2075ca18b2..e33e3b9fc6 100644 --- a/xbmc/addons/Visualization.cpp +++ b/xbmc/addons/Visualization.cpp @@ -11,9 +11,6 @@ #include "filesystem/SpecialProtocol.h" #include "guilib/GUIWindowManager.h" #include "utils/log.h" -#if defined(TARGET_WINDOWS) -#include "rendering/dx/DeviceResources.h" -#endif namespace ADDON { @@ -27,15 +24,11 @@ CVisualization::CVisualization(ADDON::BinaryAddonBasePtr addonBase, float x, flo m_profilePath = CSpecialProtocol::TranslatePath(Profile()); m_struct = {{0}}; -#if defined(TARGET_WINDOWS) - m_struct.props.device = DX::DeviceResources::Get()->GetD3DContext(); -#else - m_struct.props.device = nullptr; -#endif m_struct.props.x = static_cast<int>(x); m_struct.props.y = static_cast<int>(y); m_struct.props.width = static_cast<int>(w); m_struct.props.height = static_cast<int>(h); + m_struct.props.device = CServiceBroker::GetWinSystem()->GetHWContext(); m_struct.props.pixelRatio = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo().fPixelRatio; m_struct.props.name = m_name.c_str(); m_struct.props.presets = m_presetsPath.c_str(); diff --git a/xbmc/addons/binary-addons/AddonDll.cpp b/xbmc/addons/binary-addons/AddonDll.cpp index 2fb2c2cd84..2dd19ccde4 100644 --- a/xbmc/addons/binary-addons/AddonDll.cpp +++ b/xbmc/addons/binary-addons/AddonDll.cpp @@ -28,6 +28,7 @@ #include "Util.h" // Global addon callback handle classes +#include "addons/interfaces/AudioEngine.h" #include "addons/interfaces/Filesystem.h" #include "addons/interfaces/General.h" #include "addons/interfaces/Network.h" @@ -559,6 +560,7 @@ bool CAddonDll::InitInterface(KODI_HANDLE firstKodiInstance) m_interface.toAddon = (KodiToAddonFuncTable_Addon*) calloc(1, sizeof(KodiToAddonFuncTable_Addon)); Interface_General::Init(&m_interface); + Interface_AudioEngine::Init(&m_interface); Interface_Filesystem::Init(&m_interface); Interface_Network::Init(&m_interface); Interface_GUIGeneral::Init(&m_interface); @@ -573,6 +575,7 @@ void CAddonDll::DeInitInterface() Interface_GUIGeneral::DeInit(&m_interface); Interface_Network::DeInit(&m_interface); Interface_Filesystem::DeInit(&m_interface); + Interface_AudioEngine::DeInit(&m_interface); Interface_General::DeInit(&m_interface); if (m_interface.libBasePath) diff --git a/xbmc/addons/interfaces/AudioEngine.cpp b/xbmc/addons/interfaces/AudioEngine.cpp new file mode 100644 index 0000000000..99dbdbd4ad --- /dev/null +++ b/xbmc/addons/interfaces/AudioEngine.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "AudioEngine.h" + +#include "ServiceBroker.h" +#include "addons/kodi-addon-dev-kit/include/kodi/AddonBase.h" +#include "cores/AudioEngine/Interfaces/AE.h" +#include "cores/AudioEngine/Interfaces/AEStream.h" +#include "cores/AudioEngine/Utils/AEChannelData.h" +#include "cores/AudioEngine/Utils/AEStreamData.h" +#include "utils/log.h" + +using namespace kodi; // addon-dev-kit namespace +using namespace kodi::audioengine; // addon-dev-kit namespace + +namespace ADDON +{ + +void Interface_AudioEngine::Init(AddonGlobalInterface* addonInterface) +{ + addonInterface->toKodi->kodi_audioengine = static_cast<AddonToKodiFuncTable_kodi_audioengine*>(malloc(sizeof(AddonToKodiFuncTable_kodi_audioengine))); + + // write KODI audio DSP specific add-on function addresses to callback table + addonInterface->toKodi->kodi_audioengine->make_stream = audioengine_make_stream; + addonInterface->toKodi->kodi_audioengine->free_stream = audioengine_free_stream; + addonInterface->toKodi->kodi_audioengine->get_current_sink_format = audioengine_get_current_sink_format; + + // AEStream add-on function callback table + addonInterface->toKodi->kodi_audioengine->aestream_get_space = aestream_get_space; + addonInterface->toKodi->kodi_audioengine->aestream_add_data = aestream_add_data; + addonInterface->toKodi->kodi_audioengine->aestream_get_delay = aestream_get_delay; + addonInterface->toKodi->kodi_audioengine->aestream_is_buffering = aestream_is_buffering; + addonInterface->toKodi->kodi_audioengine->aestream_get_cache_time = aestream_get_cache_time; + addonInterface->toKodi->kodi_audioengine->aestream_get_cache_total = aestream_get_cache_total; + addonInterface->toKodi->kodi_audioengine->aestream_pause = aestream_pause; + addonInterface->toKodi->kodi_audioengine->aestream_resume = aestream_resume; + addonInterface->toKodi->kodi_audioengine->aestream_drain = aestream_drain; + addonInterface->toKodi->kodi_audioengine->aestream_is_draining = aestream_is_draining; + addonInterface->toKodi->kodi_audioengine->aestream_is_drained = aestream_is_drained; + addonInterface->toKodi->kodi_audioengine->aestream_flush = aestream_flush; + addonInterface->toKodi->kodi_audioengine->aestream_get_volume = aestream_get_volume; + addonInterface->toKodi->kodi_audioengine->aestream_set_volume = aestream_set_volume; + addonInterface->toKodi->kodi_audioengine->aestream_get_amplification = aestream_get_amplification; + addonInterface->toKodi->kodi_audioengine->aestream_set_amplification = aestream_set_amplification; + addonInterface->toKodi->kodi_audioengine->aestream_get_frame_size = aestream_get_frame_size; + addonInterface->toKodi->kodi_audioengine->aestream_get_channel_count = aestream_get_channel_count; + addonInterface->toKodi->kodi_audioengine->aestream_get_sample_rate = aestream_get_sample_rate; + addonInterface->toKodi->kodi_audioengine->aestream_get_data_format = aestream_get_data_format; + addonInterface->toKodi->kodi_audioengine->aestream_get_resample_ratio = aestream_get_resample_ratio; + addonInterface->toKodi->kodi_audioengine->aestream_set_resample_ratio = aestream_set_resample_ratio; +} + +void Interface_AudioEngine::DeInit(AddonGlobalInterface* addonInterface) +{ + if (addonInterface->toKodi) /* <-- Safe check, needed so long old addon way is present */ + { + free(addonInterface->toKodi->kodi_audioengine); + addonInterface->toKodi->kodi_audioengine = nullptr; + } +} + +AEStreamHandle* Interface_AudioEngine::audioengine_make_stream(void* kodiBase, AudioEngineFormat* streamFormat, unsigned int options) +{ + if (!kodiBase || !streamFormat) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamFormat='%p')", + __FUNCTION__, kodiBase, static_cast<void*>(streamFormat)); + return nullptr; + } + + AEAudioFormat format; + format.m_dataFormat = streamFormat->m_dataFormat; + format.m_sampleRate = streamFormat->m_sampleRate; + format.m_channelLayout = streamFormat->m_channels; + + /* Translate addon options to kodi's options */ + int kodiOption = 0; + if (options & AUDIO_STREAM_FORCE_RESAMPLE) + kodiOption |= AESTREAM_FORCE_RESAMPLE; + if (options & AUDIO_STREAM_PAUSED) + kodiOption |= AESTREAM_PAUSED; + if (options & AUDIO_STREAM_AUTOSTART) + kodiOption |= AESTREAM_AUTOSTART; + + return CServiceBroker::GetActiveAE()->MakeStream(format, kodiOption); +} + +void Interface_AudioEngine::audioengine_free_stream(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return; + } + + CServiceBroker::GetActiveAE()->FreeStream(static_cast<IAEStream*>(streamHandle), true); +} + +bool Interface_AudioEngine::audioengine_get_current_sink_format(void* kodiBase, AudioEngineFormat *format) +{ + if (!kodiBase || !format) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', format='%p')", __FUNCTION__, kodiBase, static_cast<void*>(format)); + return false; + } + + AEAudioFormat sinkFormat; + if (!CServiceBroker::GetActiveAE()->GetCurrentSinkFormat(sinkFormat)) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - failed to get current sink format from AE!", __FUNCTION__); + return false; + } + + format->m_channelCount = sinkFormat.m_channelLayout.Count(); + for (unsigned int ch = 0; ch < format->m_channelCount; ++ch) + { + format->m_channels[ch] = sinkFormat.m_channelLayout[ch]; + } + + format->m_dataFormat = sinkFormat.m_dataFormat; + format->m_sampleRate = sinkFormat.m_sampleRate; + format->m_frames = sinkFormat.m_frames; + format->m_frameSize = sinkFormat.m_frameSize; + + return true; +} + +unsigned int Interface_AudioEngine::aestream_get_space(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return 0; + } + + return static_cast<IAEStream*>(streamHandle)->GetSpace(); +} + +unsigned int Interface_AudioEngine::aestream_add_data(void* kodiBase, AEStreamHandle* streamHandle, uint8_t* const *data, + unsigned int offset, unsigned int frames, double pts, bool hasDownmix, + double centerMixLevel) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return 0; + } + + IAEStream::ExtData extData; + extData.pts = pts; + extData.hasDownmix = hasDownmix; + extData.centerMixLevel = centerMixLevel; + return static_cast<IAEStream*>(streamHandle)->AddData(data, offset, frames, &extData); +} + +double Interface_AudioEngine::aestream_get_delay(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return -1.0; + } + + return static_cast<IAEStream*>(streamHandle)->GetDelay(); +} + +bool Interface_AudioEngine::aestream_is_buffering(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return false; + } + + return static_cast<IAEStream*>(streamHandle)->IsBuffering(); +} + +double Interface_AudioEngine::aestream_get_cache_time(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return -1.0; + } + + return static_cast<IAEStream*>(streamHandle)->GetCacheTime(); +} + +double Interface_AudioEngine::aestream_get_cache_total(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return -1.0; + } + + return static_cast<IAEStream*>(streamHandle)->GetCacheTotal(); +} + +void Interface_AudioEngine::aestream_pause(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return; + } + + static_cast<IAEStream*>(streamHandle)->Pause(); +} + +void Interface_AudioEngine::aestream_resume(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return; + } + + static_cast<IAEStream*>(streamHandle)->Resume(); +} + +void Interface_AudioEngine::aestream_drain(void* kodiBase, AEStreamHandle* streamHandle, bool wait) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return; + } + + static_cast<IAEStream*>(streamHandle)->Drain(wait); +} + +bool Interface_AudioEngine::aestream_is_draining(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return false; + } + + return static_cast<IAEStream*>(streamHandle)->IsDraining(); +} + +bool Interface_AudioEngine::aestream_is_drained(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return false; + } + + return static_cast<IAEStream*>(streamHandle)->IsDrained(); +} + +void Interface_AudioEngine::aestream_flush(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return; + } + + static_cast<IAEStream*>(streamHandle)->Flush(); +} + +float Interface_AudioEngine::aestream_get_volume(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return -1.0f; + } + + return static_cast<IAEStream*>(streamHandle)->GetVolume(); +} + +void Interface_AudioEngine::aestream_set_volume(void* kodiBase, AEStreamHandle* streamHandle, float volume) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return; + } + + static_cast<IAEStream*>(streamHandle)->SetVolume(volume); +} + +float Interface_AudioEngine::aestream_get_amplification(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return -1.0f; + } + + return static_cast<IAEStream*>(streamHandle)->GetAmplification(); +} + +void Interface_AudioEngine::aestream_set_amplification(void* kodiBase, AEStreamHandle* streamHandle, float amplify) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return; + } + + static_cast<IAEStream*>(streamHandle)->SetAmplification(amplify); +} + +unsigned int Interface_AudioEngine::aestream_get_frame_size(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return 0; + } + + return static_cast<IAEStream*>(streamHandle)->GetFrameSize(); +} + +unsigned int Interface_AudioEngine::aestream_get_channel_count(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return 0; + } + + return static_cast<IAEStream*>(streamHandle)->GetChannelCount(); +} + +unsigned int Interface_AudioEngine::aestream_get_sample_rate(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return 0; + } + + return static_cast<IAEStream*>(streamHandle)->GetSampleRate(); +} + +AEDataFormat Interface_AudioEngine::aestream_get_data_format(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return AE_FMT_INVALID; + } + + return static_cast<IAEStream*>(streamHandle)->GetDataFormat(); +} + +double Interface_AudioEngine::aestream_get_resample_ratio(void* kodiBase, AEStreamHandle* streamHandle) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return -1.0f; + } + + return static_cast<IAEStream*>(streamHandle)->GetResampleRatio(); +} + +void Interface_AudioEngine::aestream_set_resample_ratio(void* kodiBase, AEStreamHandle* streamHandle, double ratio) +{ + if (!kodiBase || !streamHandle) + { + CLog::Log(LOGERROR, "Interface_AudioEngine::{} - invalid stream data (kodiBase='%p', streamHandle='%p')", __FUNCTION__, kodiBase, streamHandle); + return; + } + + static_cast<IAEStream*>(streamHandle)->SetResampleRatio(ratio); +} + +} /* namespace ADDON */ diff --git a/xbmc/addons/interfaces/AudioEngine.h b/xbmc/addons/interfaces/AudioEngine.h new file mode 100644 index 0000000000..434cda6208 --- /dev/null +++ b/xbmc/addons/interfaces/AudioEngine.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "addons/kodi-addon-dev-kit/include/kodi/AudioEngine.h" + +namespace ADDON +{ + +struct Interface_AudioEngine +{ + static void Init(AddonGlobalInterface* addonInterface); + static void DeInit(AddonGlobalInterface* addonInterface); + + /** + * Creates and returns a new handle to an IAEStream in the format specified, this function should never fail + * @param[in] streamFormat Format to use for stream + * @param[in] options A bit field of stream options (see: enum AEStreamOptions) + * @return a new Handle to an IAEStream that will accept data in the requested format + */ + static AEStreamHandle* audioengine_make_stream(void *kodiBase, AudioEngineFormat* streamFormat, unsigned int options); + + /** + * This method will remove the specifyed stream from the engine. + * For OSX/IOS this is essential to reconfigure the audio output. + * @param[in] streamHandle The stream to be altered + */ + static void audioengine_free_stream(void *kodiBase, AEStreamHandle* streamHandle); + + /** + * Get the current sink data format + * + * @param[in] sinkFormat sink data format. For more details see AudioEngineFormat. + * @return Returns true on success, else false. + */ + static bool audioengine_get_current_sink_format(void* kodiBase, AudioEngineFormat *sinkFormat); + + /** + * Returns the amount of space available in the stream + * @return The number of bytes AddData will consume + */ + static unsigned int aestream_get_space(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Add planar or interleaved PCM data to the stream + * @param[in] data array of pointers to the planes + * @param[in] offset to frame in frames + * @param[in] frames number of frames + * @return The number of frames consumed + */ + static unsigned int aestream_add_data(void* kodiBase, AEStreamHandle* streamHandle, uint8_t* const *data, + unsigned int offset, unsigned int frames, double pts, bool hasDownmix, + double centerMixLevel); + + /** + * Returns the time in seconds that it will take + * for the next added packet to be heard from the speakers. + * @return seconds + */ + static double aestream_get_delay(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Returns if the stream is buffering + * @return True if the stream is buffering + */ + static bool aestream_is_buffering(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Returns the time in seconds that it will take + * to underrun the cache if no sample is added. + * @return seconds + */ + static double aestream_get_cache_time(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Returns the total time in seconds of the cache + * @return seconds + */ + static double aestream_get_cache_total(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Pauses the stream playback + */ + static void aestream_pause(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Resumes the stream after pausing + */ + static void aestream_resume(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Start draining the stream + * @note Once called AddData will not consume more data. + */ + static void aestream_drain(void* kodiBase, AEStreamHandle* streamHandle, bool wait); + + /** + * Returns true if the is stream draining + */ + static bool aestream_is_draining(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Returns true if the is stream has finished draining + */ + static bool aestream_is_drained(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Flush all buffers dropping the audio data + */ + static void aestream_flush(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Return the stream's current volume level + * @return The volume level between 0.0 and 1.0 + */ + static float aestream_get_volume(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Set the stream's volume level + * @param volume The new volume level between 0.0 and 1.0 + */ + static void aestream_set_volume(void* kodiBase, AEStreamHandle* streamHandle, float volume); + + /** + * Gets the stream's volume amplification in linear units. + * @return The volume amplification factor between 1.0 and 1000.0 + */ + static float aestream_get_amplification(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Sets the stream's volume amplification in linear units. + * @param amplify The volume amplification factor between 1.0 and 1000.0 + */ + static void aestream_set_amplification(void* kodiBase, AEStreamHandle* streamHandle, float amplify); + + /** + * Returns the size of one audio frame in bytes (channelCount * resolution) + * @return The size in bytes of one frame + */ + static unsigned int aestream_get_frame_size(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Returns the number of channels the stream is configured to accept + * @return The channel count + */ + static unsigned int aestream_get_channel_count(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Returns the stream's sample rate, if the stream is using a dynamic sample rate, this value will NOT reflect any changes made by calls to SetResampleRatio() + * @return The stream's sample rate (eg, 48000) + */ + static unsigned int aestream_get_sample_rate(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Return the data format the stream has been configured with + * @return The stream's data format (eg, AE_FMT_S16LE) + */ + static AEDataFormat aestream_get_data_format(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Return the resample ratio + * @note This will return an undefined value if the stream is not resampling + * @return the current resample ratio or undefined if the stream is not resampling + */ + static double aestream_get_resample_ratio(void* kodiBase, AEStreamHandle* streamHandle); + + /** + * Sets the resample ratio + * @note This function may return false if the stream is not resampling, if you wish to use this be sure to set the AESTREAM_FORCE_RESAMPLE option + * @param[in] ratio the new sample rate ratio, calculated by ((double)desiredRate / (double)GetSampleRate()) + */ + static void aestream_set_resample_ratio(void* kodiBase, AEStreamHandle* streamHandle, double ratio); +}; + +} /* namespace ADDON */ diff --git a/xbmc/addons/interfaces/CMakeLists.txt b/xbmc/addons/interfaces/CMakeLists.txt index a064cf2d5d..cd2f330a97 100644 --- a/xbmc/addons/interfaces/CMakeLists.txt +++ b/xbmc/addons/interfaces/CMakeLists.txt @@ -1,9 +1,11 @@ set(SOURCES AddonInterfaces.cpp + AudioEngine.cpp General.cpp Filesystem.cpp Network.cpp) set(HEADERS AddonInterfaces.h + AudioEngine.h General.h Filesystem.h Network.h) diff --git a/xbmc/addons/interfaces/GUI/General.cpp b/xbmc/addons/interfaces/GUI/General.cpp index ab5ede29c5..f03a040dd6 100644 --- a/xbmc/addons/interfaces/GUI/General.cpp +++ b/xbmc/addons/interfaces/GUI/General.cpp @@ -91,6 +91,7 @@ void Interface_GUIGeneral::Init(AddonGlobalInterface* addonInterface) addonInterface->toKodi->kodi_gui->general->get_video_resolution = get_video_resolution; addonInterface->toKodi->kodi_gui->general->get_current_window_dialog_id = get_current_window_dialog_id; addonInterface->toKodi->kodi_gui->general->get_current_window_id = get_current_window_id; + addonInterface->toKodi->kodi_gui->general->get_hw_context = get_hw_context; } void Interface_GUIGeneral::DeInit(AddonGlobalInterface* addonInterface) @@ -213,6 +214,11 @@ int Interface_GUIGeneral::get_current_window_id(void* kodiBase) return CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow(); } +void* Interface_GUIGeneral::get_hw_context(void* kodiBase) +{ + return CServiceBroker::GetWinSystem()->GetHWContext(); +} + //@} } /* namespace ADDON */ diff --git a/xbmc/addons/interfaces/GUI/General.h b/xbmc/addons/interfaces/GUI/General.h index fc1170cc5c..0042329b0f 100644 --- a/xbmc/addons/interfaces/GUI/General.h +++ b/xbmc/addons/interfaces/GUI/General.h @@ -48,6 +48,7 @@ namespace ADDON static int get_video_resolution(void* kodiBase); static int get_current_window_dialog_id(void* kodiBase); static int get_current_window_id(void* kodiBase); + static void* get_hw_context(void* kodiBase); //@} private: diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/AddonBase.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/AddonBase.h index db39f8693d..d7da3ac388 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/AddonBase.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/AddonBase.h @@ -11,6 +11,7 @@ #include <stdarg.h> /* va_list, va_start, va_arg, va_end */ #include <cstdlib> #include <cstring> +#include <memory> #include <stdexcept> #include <string> #include <vector> @@ -61,6 +62,7 @@ namespace kodi { namespace addon { class CAddonBase; }} namespace kodi { namespace addon { class IAddonInstance; }} +namespace kodi { namespace gui { struct IRenderHelper; }} extern "C" { @@ -334,6 +336,9 @@ public: return CreateInstance(instanceType, instanceID, instance, addonInstance); } + /* Background helper for GUI render systems, e.g. Screensaver or Visualization */ + std::shared_ptr<kodi::gui::IRenderHelper> m_renderHelper; + /* Global variables of class */ static AddonGlobalInterface* m_interface; // Interface function table to hold addresses on add-on and from kodi static std::string m_strGlobalApiVersion; diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/AudioEngine.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/AudioEngine.h new file mode 100644 index 0000000000..051d4955d6 --- /dev/null +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/AudioEngine.h @@ -0,0 +1,586 @@ +/* + * Copyright (C) 2005-2019 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonBase.h" + +#ifdef BUILD_KODI_ADDON +#include "AEChannelData.h" +#else +#include "cores/AudioEngine/Utils/AEChannelData.h" +#endif + +//============================================================================== +/// +/// \defgroup cpp_kodi_audioengine Interface - kodi::audioengine +/// \ingroup cpp +/// @brief **Audio engine functions** +/// +/// +/// It has the header \ref AudioEngine.h "#include <kodi/AudioEngine.h>" be included +/// to enjoy it. +/// +//------------------------------------------------------------------------------ + +//============================================================================== +/// +/// \defgroup cpp_kodi_audioengine_Defs Definitions, structures and enumerators +/// \ingroup cpp_kodi_audioengine +/// @brief **Library definition values** +/// +//------------------------------------------------------------------------------ + +extern "C" +{ + +//============================================================================ +/// \ingroup cpp_kodi_audioengine_Defs +/// @brief Bit options to pass to CAddonAEStream +/// +typedef enum AudioEngineStreamOptions +{ + /// force resample even if rates match + AUDIO_STREAM_FORCE_RESAMPLE = 1 << 0, + /// create the stream paused + AUDIO_STREAM_PAUSED = 1 << 1, + /// autostart the stream when enough data is buffered + AUDIO_STREAM_AUTOSTART = 1 << 2, +} AudioEngineStreamOptions; +//---------------------------------------------------------------------------- + +//============================================================================ +/// \defgroup cpp_kodi_audioengine_Defs_AudioEngineFormat struct AudioEngineFormat +/// \ingroup cpp_kodi_audioengine_Defs +/// @brief The audio format structure that fully defines a stream's audio +/// information +/// +//@{ +struct AudioEngineFormat +{ + /// The stream's data format (eg, AE_FMT_S16LE) + enum AEDataFormat m_dataFormat; + + /// The stream's sample rate (eg, 48000) + unsigned int m_sampleRate; + + /// The encoded streams sample rate if a bitstream, otherwise undefined + unsigned int m_encodedRate; + + /// The amount of used speaker channels + unsigned int m_channelCount; + + /// The stream's channel layout + enum AEChannel m_channels[AE_CH_MAX]; + + /// The number of frames per period + unsigned int m_frames; + + /// The size of one frame in bytes + unsigned int m_frameSize; + + AudioEngineFormat() + { + m_dataFormat = AE_FMT_INVALID; + m_sampleRate = 0; + m_encodedRate = 0; + m_frames = 0; + m_frameSize = 0; + m_channelCount = 0; + + for (unsigned int ch = 0; ch < AE_CH_MAX; ++ch) + { + m_channels[ch] = AE_CH_MAX; + } + } + + /// Function to compare the format structure with another + bool compareFormat(const AudioEngineFormat *fmt) + { + if (!fmt) + { + return false; + } + + if (m_dataFormat != fmt->m_dataFormat || + m_sampleRate != fmt->m_sampleRate || + m_encodedRate != fmt->m_encodedRate || + m_frames != fmt->m_frames || + m_frameSize != fmt->m_frameSize || + m_channelCount != fmt->m_channelCount) + { + return false; + } + + for (unsigned int ch = 0; ch < AE_CH_MAX; ++ch) + { + if (fmt->m_channels[ch] != m_channels[ch]) + { + return false; + } + } + + return true; + } +}; +//@} +//---------------------------------------------------------------------------- + +/* A stream handle pointer, which is only used internally by the addon stream handle */ +typedef void AEStreamHandle; + +/* + * Function address structure, not need to visible on dev kit doxygen + * documentation + */ +typedef struct AddonToKodiFuncTable_kodi_audioengine +{ + AEStreamHandle* (*make_stream)(void *kodiBase, AudioEngineFormat* format, unsigned int options); + void (*free_stream)(void *kodiBase, AEStreamHandle *stream); + bool (*get_current_sink_format)(void *kodiBase, AudioEngineFormat* sink_format); + + // Audio Engine Stream definitions + unsigned int (*aestream_get_space)(void *kodiBase, AEStreamHandle *handle); + unsigned int (*aestream_add_data)(void *kodiBase, AEStreamHandle *handle, uint8_t* const *data, + unsigned int offset, unsigned int frames, double pts, bool hasDownmix, + double centerMixLevel); + double (*aestream_get_delay)(void *kodiBase, AEStreamHandle *handle); + bool (*aestream_is_buffering)(void *kodiBase, AEStreamHandle *handle); + double (*aestream_get_cache_time)(void *kodiBase, AEStreamHandle *handle); + double (*aestream_get_cache_total)(void *kodiBase, AEStreamHandle *handle); + void (*aestream_pause)(void *kodiBase, AEStreamHandle *handle); + void (*aestream_resume)(void *kodiBase, AEStreamHandle *handle); + void (*aestream_drain)(void *kodiBase, AEStreamHandle *handle, bool wait); + bool (*aestream_is_draining)(void *kodiBase, AEStreamHandle *handle); + bool (*aestream_is_drained)(void *kodiBase, AEStreamHandle *handle); + void (*aestream_flush)(void *kodiBase, AEStreamHandle *handle); + float (*aestream_get_volume)(void *kodiBase, AEStreamHandle *handle); + void (*aestream_set_volume)(void *kodiBase, AEStreamHandle *handle, float volume); + float (*aestream_get_amplification)(void *kodiBase, AEStreamHandle *handle); + void (*aestream_set_amplification)(void *kodiBase, AEStreamHandle *handle, float amplify); + unsigned int (*aestream_get_frame_size)(void *kodiBase, AEStreamHandle *handle); + unsigned int (*aestream_get_channel_count)(void *kodiBase, AEStreamHandle *handle); + unsigned int (*aestream_get_sample_rate)(void *kodiBase, AEStreamHandle *handle); + AEDataFormat (*aestream_get_data_format)(void *kodiBase, AEStreamHandle *handle); + double (*aestream_get_resample_ratio)(void *kodiBase, AEStreamHandle *handle); + void (*aestream_set_resample_ratio)(void *kodiBase, AEStreamHandle *handle, double ratio); +} AddonToKodiFuncTable_kodi_audioengine; + +} /* extern "C" */ + +namespace kodi +{ +namespace audioengine +{ + +//============================================================================ +/// +/// \defgroup cpp_kodi_audioengine_CAddonAEStream class CAddonAEStream +/// \ingroup cpp_kodi_audioengine +/// @brief **Audio Engine Stream Class** +/// +/// +/// It has the header \ref AudioEngine.h "#include <kodi/AudioEngine.h>" be +/// included to enjoy it. +/// +//---------------------------------------------------------------------------- +class CAddonAEStream +{ +public: + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Contructs new class to an Kodi IAEStream in the format specified. + /// + /// @param[in] format The data format the incoming audio will be in + /// (e.g. \ref AE_FMT_S16LE) + /// @param[in] options [opt] A bit field of stream options (see: enum \ref AudioEngineStreamOptions) + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Audio engine format information:** + /// @code + /// /* + /// * Audio engine format information + /// * + /// * Only as example shown here! See always the original structure on related header. + /// */ + /// typedef struct AudioEngineFormat + /// { + /// enum AEDataFormat m_dataFormat; /* The stream's data format (eg, AE_FMT_S16LE) */ + /// unsigned int m_sampleRate; /* The stream's sample rate (eg, 48000) */ + /// unsigned int m_encodedRate; /* The encoded streams sample rate if a bitstream, otherwise undefined */ + /// unsigned int m_channelCount; /* The amount of used speaker channels */ + /// enum AEChannel m_channels[AE_CH_MAX]; /* The stream's channel layout */ + /// unsigned int m_frames; /* The number of frames per period */ + /// unsigned int m_frameSamples; /* The number of samples in one frame */ + /// unsigned int m_frameSize; /* The size of one frame in bytes */ + /// + /// /* Function to compare the format structure with another */ + /// bool compareFormat(const AudioEngineFormat *fmt); + /// } AudioEngineFormat; + /// @endcode + /// + /// ------------------------------------------------------------------------ + /// + /// **Bit options to pass to CAELib_Stream (on Kodi by <c>IAE::MakeStream</c>)** + /// + /// | enum AEStreamOptions | Value: | Description: + /// |----------------------------:|:------:|:----------------------------------- + /// | AUDIO_STREAM_FORCE_RESAMPLE | 1 << 0 | Force resample even if rates match + /// | AUDIO_STREAM_PAUSED | 1 << 1 | Create the stream paused + /// | AUDIO_STREAM_AUTOSTART | 1 << 2 | Autostart the stream when enough data is buffered + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.cpp} + /// + /// #include <kodi/AudioEngine.h> + /// + /// using namespace kodi::audioengine; + /// + /// ... + /// + /// AudioEngineFormat format; + /// format.m_dataFormat = AE_FMT_FLOAT; + /// format.m_channelCount = 2; + /// format.m_channels[0] = AE_CH_FL; + /// format.m_channels[1] = AE_CH_FR; + /// format.m_channels[2] = AE_CH_NULL; + /// format.m_sampleRate = 48000; + /// format.m_frameSize = sizeof(float)*format.m_channelCount; + /// format.m_frames = 512; + /// CAddonAEStream* stream = new CAddonAEStream(format, AE_STREAM_AUTOSTART); + /// + /// ~~~~~~~~~~~~~ + /// + CAddonAEStream(AudioEngineFormat format, unsigned int options = 0) + : m_kodiBase(::kodi::addon::CAddonBase::m_interface->toKodi->kodiBase), + m_cb(::kodi::addon::CAddonBase::m_interface->toKodi->kodi_audioengine) + { + m_StreamHandle = m_cb->make_stream(m_kodiBase, &format, options); + if (m_StreamHandle == nullptr) + { + kodi::Log(ADDON_LOG_FATAL, "CAddonAEStream: make_stream failed!"); + } + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Class destructor + /// + ~CAddonAEStream() + { + if (m_StreamHandle) + { + m_cb->free_stream(m_kodiBase, m_StreamHandle); + m_StreamHandle = nullptr; + } + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Returns the amount of space available in the stream + /// + /// @return The number of bytes AddData will consume + /// + unsigned int GetSpace() + { + return m_cb->aestream_get_space(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Add planar or interleaved PCM data to the stream + /// + /// @param[in] data array of pointers to the planes + /// @param[in] offset to frame in frames + /// @param[in] frames number of frames + /// @param[in] pts [opt] presentation timestamp, default is 0 + /// @param[in] hasDownmix [opt] set true if downmix is present, default is false + /// @param[in] centerMixLevel [opt] level to mix left and right to center default is 1.0 + /// @return The number of frames consumed + /// + unsigned int AddData(uint8_t* const *data, unsigned int offset, unsigned int frames, + double pts = 0, bool hasDownmix = false, double centerMixLevel = 1.0) + { + return m_cb->aestream_add_data(m_kodiBase, m_StreamHandle, data, offset, frames, pts, hasDownmix, centerMixLevel); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Returns the time in seconds that it will take for the next added + /// packet to be heard from the speakers. + /// + /// @return seconds + /// + double GetDelay() + { + return m_cb->aestream_get_delay(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Returns if the stream is buffering + /// + /// @return True if the stream is buffering + /// + bool IsBuffering() + { + return m_cb->aestream_is_buffering(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Returns the time in seconds of the stream's cached audio samples. + /// Engine buffers excluded. + /// + /// @return seconds + /// + double GetCacheTime() + { + return m_cb->aestream_get_cache_time(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Returns the total time in seconds of the cache + /// + /// @return seconds + /// + double GetCacheTotal() + { + return m_cb->aestream_get_cache_total(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Pauses the stream playback + /// + void Pause() + { + return m_cb->aestream_pause(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Resumes the stream after pausing + /// + void Resume() + { + return m_cb->aestream_resume(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Start draining the stream + /// + /// @param[in] wait [opt] Wait until drain is finished if set to + /// true, otherwise it returns direct + /// + /// @note Once called AddData will not consume more data. + /// + void Drain(bool wait = true) + { + return m_cb->aestream_drain(m_kodiBase, m_StreamHandle, wait); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Returns true if the is stream draining + /// + bool IsDraining() + { + return m_cb->aestream_is_draining(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Returns true if the is stream has finished draining + /// + bool IsDrained() + { + return m_cb->aestream_is_drained(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Flush all buffers dropping the audio data + /// + void Flush() + { + return m_cb->aestream_flush(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Return the stream's current volume level + /// + /// @return The volume level between 0.0 and 1.0 + /// + float GetVolume() + { + return m_cb->aestream_get_volume(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Set the stream's volume level + /// + /// @param[in] volume The new volume level between 0.0 and 1.0 + /// + void SetVolume(float volume) + { + return m_cb->aestream_set_volume(m_kodiBase, m_StreamHandle, volume); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Gets the stream's volume amplification in linear units. + /// + /// @return The volume amplification factor between 1.0 and 1000.0 + /// + float GetAmplification() + { + return m_cb->aestream_get_amplification(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Sets the stream's volume amplification in linear units. + /// + /// @param[in] amplify The volume amplification factor between + /// 1.0 and 1000.0 + /// + void SetAmplification(float amplify) + { + return m_cb->aestream_set_amplification(m_kodiBase, m_StreamHandle, amplify); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Returns the size of one audio frame in bytes (channelCount * resolution) + /// + /// @return The size in bytes of one frame + /// + unsigned int GetFrameSize() const + { + return m_cb->aestream_get_frame_size(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Returns the number of channels the stream is configured to accept + /// + /// @return The channel count + /// + unsigned int GetChannelCount() const + { + return m_cb->aestream_get_channel_count(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Returns the stream's sample rate, if the stream is using a dynamic + /// sample rate, this value will NOT reflect any changes made by calls to + /// SetResampleRatio() + /// + /// @return The stream's sample rate (eg, 48000) + /// + unsigned int GetSampleRate() const + { + return m_cb->aestream_get_sample_rate(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Return the data format the stream has been configured with + /// + /// @return The stream's data format (eg, AUDIOENGINE_FMT_S16LE) + /// + AEDataFormat GetDataFormat() const + { + return m_cb->aestream_get_data_format(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Return the resample ratio + /// + /// @note This will return an undefined value if the stream is not resampling + /// + /// @return the current resample ratio or undefined if the stream is not resampling + /// + double GetResampleRatio() + { + return m_cb->aestream_get_resample_ratio(m_kodiBase, m_StreamHandle); + } + //-------------------------------------------------------------------------- + + //========================================================================== + /// @ingroup cpp_kodi_audioengine_CAddonAEStream + /// @brief Sets the resample ratio + /// + /// @note This function may return false if the stream is not resampling, if + /// you wish to use this be sure to set the AESTREAM_FORCE_RESAMPLE option + /// + /// @param[in] ratio the new sample rate ratio, calculated by + /// ((double)desiredRate / (double)GetSampleRate()) + /// + void SetResampleRatio(double ratio) + { + m_cb->aestream_set_resample_ratio(m_kodiBase, m_StreamHandle, ratio); + } + //-------------------------------------------------------------------------- + +private: + void* m_kodiBase; + AddonToKodiFuncTable_kodi_audioengine* m_cb; + AEStreamHandle *m_StreamHandle; +}; + +//============================================================================ +/// @ingroup cpp_kodi_audioengine +/// @brief Get the current sink data format +/// +/// @param[in] format Current sink data format. For more details see AudioEngineFormat. +/// @return Returns true on success, else false. +/// +inline bool GetCurrentSinkFormat(AudioEngineFormat &format) +{ + using namespace kodi::addon; + return CAddonBase::m_interface->toKodi->kodi_audioengine->get_current_sink_format(CAddonBase::m_interface->toKodi->kodiBase, &format); +} +//---------------------------------------------------------------------------- + +} /* audioengine */ +} /* kodi */ diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/CMakeLists.txt b/xbmc/addons/kodi-addon-dev-kit/include/kodi/CMakeLists.txt index 2b98154966..6d644dbb3e 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/CMakeLists.txt +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/CMakeLists.txt @@ -1,4 +1,5 @@ set(HEADERS AddonBase.h + AudioEngine.h Filesystem.h General.h Network.h diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/addon-instance/Screensaver.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/addon-instance/Screensaver.h index c4f8005c1c..cc942ab82b 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/addon-instance/Screensaver.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/addon-instance/Screensaver.h @@ -9,6 +9,7 @@ #pragma once #include "../AddonBase.h" +#include "../gui/renderHelper.h" namespace kodi { namespace addon { class CInstanceScreensaver; }} @@ -418,19 +419,35 @@ namespace addon inline static bool ADDON_Start(AddonInstance_Screensaver* instance) { + instance->toAddon.addonInstance->m_renderHelper = kodi::gui::GetRenderHelper(); return instance->toAddon.addonInstance->Start(); } inline static void ADDON_Stop(AddonInstance_Screensaver* instance) { instance->toAddon.addonInstance->Stop(); + instance->toAddon.addonInstance->m_renderHelper = nullptr; } inline static void ADDON_Render(AddonInstance_Screensaver* instance) { + if (!instance->toAddon.addonInstance->m_renderHelper) + return; + instance->toAddon.addonInstance->m_renderHelper->Begin(); instance->toAddon.addonInstance->Render(); + instance->toAddon.addonInstance->m_renderHelper->End(); } + /* + * Background render helper holds here and in addon base. + * In addon base also to have for the others, and stored here for the worst + * case where this class is independent from base and base becomes closed + * before. + * + * This is on Kodi with GL unused and the calls to there are empty (no work) + * On Kodi with Direct X where angle is present becomes this used. + */ + std::shared_ptr<kodi::gui::IRenderHelper> m_renderHelper; AddonInstance_Screensaver* m_instanceData; }; diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/addon-instance/Visualization.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/addon-instance/Visualization.h index 6c2969373c..d7ad1f16aa 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/addon-instance/Visualization.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/addon-instance/Visualization.h @@ -14,6 +14,7 @@ */ #include "../AddonBase.h" +#include "../gui/renderHelper.h" namespace kodi { namespace addon { class CInstanceVisualization; }} @@ -665,12 +666,14 @@ namespace addon inline static bool ADDON_Start(const AddonInstance_Visualization* addon, int channels, int samplesPerSec, int bitsPerSample, const char* songName) { + addon->toAddon.addonInstance->m_renderHelper = kodi::gui::GetRenderHelper(); return addon->toAddon.addonInstance->Start(channels, samplesPerSec, bitsPerSample, songName); } inline static void ADDON_Stop(const AddonInstance_Visualization* addon) { addon->toAddon.addonInstance->Stop(); + addon->toAddon.addonInstance->m_renderHelper = nullptr; } inline static void ADDON_AudioData(const AddonInstance_Visualization* addon, const float* audioData, int audioDataLength, float *freqData, int freqDataLength) @@ -685,7 +688,11 @@ namespace addon inline static void ADDON_Render(const AddonInstance_Visualization* addon) { + if (!addon->toAddon.addonInstance->m_renderHelper) + return; + addon->toAddon.addonInstance->m_renderHelper->Begin(); addon->toAddon.addonInstance->Render(); + addon->toAddon.addonInstance->m_renderHelper->End(); } inline static void ADDON_GetInfo(const AddonInstance_Visualization* addon, VIS_INFO *info) @@ -745,6 +752,7 @@ namespace addon return addon->toAddon.addonInstance->IsLocked(); } + std::shared_ptr<kodi::gui::IRenderHelper> m_renderHelper; bool m_presetLockedByUser = false; AddonInstance_Visualization* m_instanceData; }; diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/CMakeLists.txt b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/CMakeLists.txt index 91cef7f5ad..834ec00a19 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/CMakeLists.txt +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/CMakeLists.txt @@ -1,7 +1,8 @@ set(HEADERS General.h ListItem.h Window.h - definitions.h) + definitions.h + renderHelper.h) if(NOT ENABLE_STATIC_LIBS) core_add_library(addons_kodi-addon-dev-kit_include_kodi_gui) diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/General.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/General.h index 30b6d89d85..a3b8bcb430 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/General.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/General.h @@ -106,7 +106,7 @@ namespace gui /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// .. - /// int wid = kodi::gui::GetCurrentWindowDialogId() + /// int wid = kodi::gui::GetCurrentWindowDialogId(); /// .. /// ~~~~~~~~~~~~~ /// @@ -130,7 +130,7 @@ namespace gui /// **Example:** /// ~~~~~~~~~~~~~{.cpp} /// .. - /// int wid = kodi::gui::GetCurrentWindowId() + /// int wid = kodi::gui::GetCurrentWindowId(); /// .. /// ~~~~~~~~~~~~~ /// @@ -141,5 +141,35 @@ namespace gui } //-------------------------------------------------------------------------- + //========================================================================== + /// + /// \ingroup cpp_kodi_gui + /// \brief To get hardware specific device context interface + /// + /// \return The currently active device context + /// + /// \warning This function is only be supported under Windows, on all other + /// OS it return `nullptr`! + /// + /// \note Returned Windows class pointer is `ID3D11DeviceContext1`. + /// + /// + ///------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.cpp} + /// #include <d3d11_1.h> + /// .. + /// ID3D11DeviceContext1* context = static_cast<ID3D11DeviceContext1*>(kodi::gui::GetHWContext()); + /// .. + /// ~~~~~~~~~~~~~ + /// + inline void* GetHWContext() + { + using namespace ::kodi::addon; + return CAddonBase::m_interface->toKodi->kodi_gui->general->get_hw_context(CAddonBase::m_interface->toKodi->kodiBase); + } + //-------------------------------------------------------------------------- + } /* namespace gui */ } /* namespace kodi */ diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/controls/Rendering.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/controls/Rendering.h index b3dadcd9e3..2ad7e1f0d2 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/controls/Rendering.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/controls/Rendering.h @@ -10,6 +10,7 @@ #include "../../AddonBase.h" #include "../Window.h" +#include "../renderHelper.h" namespace kodi { @@ -179,17 +180,23 @@ namespace controls */ static bool OnCreateCB(void* cbhdl, int x, int y, int w, int h, void* device) { + static_cast<CRendering*>(cbhdl)->m_renderHelper = kodi::gui::GetRenderHelper(); return static_cast<CRendering*>(cbhdl)->Create(x, y, w, h, device); } static void OnRenderCB(void* cbhdl) { + if (!static_cast<CRendering*>(cbhdl)->m_renderHelper) + return; + static_cast<CRendering*>(cbhdl)->m_renderHelper->Begin(); static_cast<CRendering*>(cbhdl)->Render(); + static_cast<CRendering*>(cbhdl)->m_renderHelper->End(); } static void OnStopCB(void* cbhdl) { static_cast<CRendering*>(cbhdl)->Stop(); + static_cast<CRendering*>(cbhdl)->m_renderHelper = nullptr; } static bool OnDirtyCB(void* cbhdl) @@ -197,6 +204,7 @@ namespace controls return static_cast<CRendering*>(cbhdl)->Dirty(); } + std::shared_ptr<kodi::gui::IRenderHelper> m_renderHelper; }; } /* namespace controls */ diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/definitions.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/definitions.h index 770a4160ed..b8b4cbf2b9 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/definitions.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/definitions.h @@ -26,6 +26,7 @@ typedef struct AddonToKodiFuncTable_kodi_gui_general int (*get_video_resolution)(void* kodiBase); int (*get_current_window_dialog_id)(void* kodiBase); int (*get_current_window_id)(void* kodiBase); + void* (*get_hw_context)(void* kodiBase); } AddonToKodiFuncTable_kodi_gui_general; typedef struct AddonToKodiFuncTable_kodi_gui_control_button diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/gl/CMakeLists.txt b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/gl/CMakeLists.txt new file mode 100644 index 0000000000..a9ab70c977 --- /dev/null +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/gl/CMakeLists.txt @@ -0,0 +1,7 @@ +set(HEADERS GL.h + GLonDX.h + Shader.h) + +if(NOT ENABLE_STATIC_LIBS) + core_add_library(addons_kodi-addon-dev-kit_include_kodi_gui_gl) +endif() diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/gl/GL.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/gl/GL.h new file mode 100644 index 0000000000..ad4fe3da0e --- /dev/null +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/gl/GL.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2005-2019 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +//============================================================================== +/// +/// \defgroup cpp_kodi_gui_gl Kodi OpenGL helpers +/// \ingroup cpp_kodi_gui +/// \brief Auxiliary functions for Open GL +/// +/// This group includes help for definitions, functions, and classes for +/// OpenGL. +/// +/// To use OpenGL for your system, add the \ref GL.h "#include <kodi/gui/gl/GL.h>". +/// +/// +///----------------------------------------------------------------------------- +/// +/// The \ref HAS_GL is declared if Open GL is required and \ref HAS_GLES if Open GL +/// Embedded Systems (ES) is required, with ES the version is additionally given +/// in the definition, this can be "2" or "3". +/// +/// +///----------------------------------------------------------------------------- +/// +/// Following \ref GL_TYPE_STRING define can be used, for example, to manage +/// different folders for GL and GLES and make the selection easier. +/// This are on OpenGL **"`GL`"** and on Open GL|ES **"`GLES`"**. +/// **Example:** +/// ~~~~~~~~~~~~~~~~~{.cpp} +/// kodi::GetAddonPath("resources/shaders/" GL_TYPE_STRING "/frag.glsl"); +/// ~~~~~~~~~~~~~~~~~ +/// +/// +///---------------------------------------------------------------------------- +/// +/// In addition, \ref BUFFER_OFFSET is declared in it which can be used to give an +/// offset on the array to GL. +/// **Example:** +/// ~~~~~~~~~~~~~~~~~{.cpp} +/// const struct PackedVertex { +/// float position[3]; // Position x, y, z +/// float color[4]; // Color r, g, b, a +/// } vertices[3] = { +/// { { -0.5f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } }, +/// { { 0.5f, -0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } }, +/// { { 0.0f, 0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } } +/// }; +/// +/// glVertexAttribPointer(m_aPosition, 3, GL_FLOAT, GL_FALSE, sizeof(PackedVertex), BUFFER_OFFSET(offsetof(PackedVertex, position))); +/// glEnableVertexAttribArray(m_aPosition); +/// +/// glVertexAttribPointer(m_aColor, 4, GL_FLOAT, GL_FALSE, sizeof(PackedVertex), BUFFER_OFFSET(offsetof(PackedVertex, color))); +/// glEnableVertexAttribArray(m_aColor); +/// ~~~~~~~~~~~~~~~~~ + +#if HAS_GL + #define GL_TYPE_STRING "GL" + // always define GL_GLEXT_PROTOTYPES before include gl headers + #if !defined(GL_GLEXT_PROTOTYPES) + #define GL_GLEXT_PROTOTYPES + #endif + #if defined(TARGET_LINUX) + #include <GL/gl.h> + #include <GL/glext.h> + #elif defined(TARGET_FREEBSD) + #include <GL/gl.h> + #elif defined(TARGET_DARWIN) + #include <OpenGL/gl3.h> + #include <OpenGL/gl3ext.h> + #elif defined(WIN32) + #error Use of GL under Windows is not possible + #endif +#elif HAS_GLES >= 2 + #define GL_TYPE_STRING "GLES" + #if defined(WIN32) + #if defined(HAS_ANGLE) + #include <angle_gl.h> + #else + #error Use of GLES only be available under Windows by the use of angle + #endif + #elif defined(TARGET_DARWIN) + #if HAS_GLES == 3 + #include <OpenGLES/ES3/gl.h> + #include <OpenGLES/ES3/glext.h> + #else + #include <OpenGLES/ES2/gl.h> + #include <OpenGLES/ES2/glext.h> + #endif + #else + #if HAS_GLES == 3 + #include <GLES3/gl3.h> + #include <GLES3/gl3ext.h> + #else + #include <GLES2/gl2.h> + #include <GLES2/gl2ext.h> + #endif + #endif +#endif + +#ifndef BUFFER_OFFSET +#define BUFFER_OFFSET(i) ((char *)nullptr + (i)) +#endif diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/gl/GLonDX.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/gl/GLonDX.h new file mode 100644 index 0000000000..01d222c9e3 --- /dev/null +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/gl/GLonDX.h @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2005-2019 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include <angle_gl.h> +#include <d3d11.h> +#include <d3dcompiler.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <kodi/AddonBase.h> +#include <kodi/gui/General.h> +#include <wrl/client.h> + +#pragma comment( lib, "d3dcompiler.lib" ) +#ifndef GL_CLIENT_VERSION +#define GL_CLIENT_VERSION 3 +#endif + +namespace kodi +{ +namespace gui +{ +namespace gl +{ + +class CGLonDX : public kodi::gui::IRenderHelper +{ +public: + explicit CGLonDX() : m_pContext(reinterpret_cast<ID3D11DeviceContext*>(kodi::gui::GetHWContext())) {} + ~CGLonDX() override { destruct(); } + + bool Init() override + { + EGLint egl_display_attrs[] = + { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE, + EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE, + EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE, EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE, + EGL_NONE + }; + EGLint egl_config_attrs[] = + { + EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, + EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE, + EGL_RENDERABLE_TYPE, GL_CLIENT_VERSION == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_NONE + }; + EGLint egl_context_attrs[] = + { + EGL_CONTEXT_CLIENT_VERSION, GL_CLIENT_VERSION, EGL_NONE + }; + + m_eglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, egl_display_attrs); + if (m_eglDisplay == EGL_NO_DISPLAY) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to get EGL display (%s)", eglGetErrorString()); + return false; + } + + if (eglInitialize(m_eglDisplay, nullptr, nullptr) != EGL_TRUE) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to init EGL display (%s)", eglGetErrorString()); + return false; + } + + EGLint numConfigs = 0; + if (eglChooseConfig(m_eglDisplay, egl_config_attrs, &m_eglConfig, 1, &numConfigs) != EGL_TRUE || numConfigs == 0) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to get EGL config (%s)", eglGetErrorString()); + return false; + } + + m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, nullptr, egl_context_attrs); + if (m_eglContext == EGL_NO_CONTEXT) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create EGL context (%s)", eglGetErrorString()); + return false; + } + + if (!createD3DResources()) + return false; + + if (eglMakeCurrent(m_eglDisplay, m_eglBuffer, m_eglBuffer, m_eglContext) != EGL_TRUE) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to make current EGL (%s)", eglGetErrorString()); + return false; + } + return true; + } + + void CheckGL(ID3D11DeviceContext* device) + { + if (m_pContext != device) + { + m_pSRView = nullptr; + m_pVShader = nullptr; + m_pPShader = nullptr; + m_pContext = device; + + if (m_eglBuffer != EGL_NO_SURFACE) + { + eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(m_eglDisplay, m_eglBuffer); + m_eglBuffer = EGL_NO_SURFACE; + } + + // create new resources + if (!createD3DResources()) + return; + + eglMakeCurrent(m_eglDisplay, m_eglBuffer, m_eglBuffer, m_eglContext); + } + } + + void Begin() override + { + // confirm on begin D3D context is correct + CheckGL(reinterpret_cast<ID3D11DeviceContext*>(kodi::gui::GetHWContext())); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + } + + void End() override + { + glFlush(); + + // set our primitive shaders + m_pContext->VSSetShader(m_pVShader.Get(), nullptr, 0); + m_pContext->PSSetShader(m_pPShader.Get(), nullptr, 0); + m_pContext->PSSetShaderResources(0, 1, m_pSRView.GetAddressOf()); + // draw texture + m_pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + m_pContext->IASetVertexBuffers(0, 0, nullptr, nullptr, nullptr); + m_pContext->IASetInputLayout(nullptr); + m_pContext->Draw(4, 0); + // unset shaders + m_pContext->PSSetShader(nullptr, nullptr, 0); + m_pContext->VSSetShader(nullptr, nullptr, 0); + // unbind our view + ID3D11ShaderResourceView* views[1] = {}; + m_pContext->PSSetShaderResources(0, 1, views); + } + +private: + enum ShaderType + { + VERTEX_SHADER, + PIXEL_SHADER + }; + + bool createD3DResources() + { + HANDLE sharedHandle; + Microsoft::WRL::ComPtr<ID3D11Device> pDevice; + Microsoft::WRL::ComPtr<ID3D11RenderTargetView> pRTView; + Microsoft::WRL::ComPtr<ID3D11Resource> pRTResource; + Microsoft::WRL::ComPtr<ID3D11Texture2D> pRTTexture; + Microsoft::WRL::ComPtr<ID3D11Texture2D> pOffScreenTexture; + Microsoft::WRL::ComPtr<IDXGIResource> dxgiResource; + + m_pContext->GetDevice(&pDevice); + m_pContext->OMGetRenderTargets(1, &pRTView, nullptr); + if (!pRTView) + return false; + + pRTView->GetResource(&pRTResource); + if (FAILED(pRTResource.As(&pRTTexture))) + return false; + + D3D11_TEXTURE2D_DESC texDesc; + pRTTexture->GetDesc(&texDesc); + texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; + texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; + if (FAILED(pDevice->CreateTexture2D(&texDesc, nullptr, &pOffScreenTexture))) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create intermediate texture"); + return false; + } + + CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(pOffScreenTexture.Get(), D3D11_SRV_DIMENSION_TEXTURE2D); + if (FAILED(pDevice->CreateShaderResourceView(pOffScreenTexture.Get(), &srvDesc, &m_pSRView))) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create shader view"); + return false; + } + + if (FAILED(pOffScreenTexture.As(&dxgiResource)) || + FAILED(dxgiResource->GetSharedHandle(&sharedHandle))) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable get shared handle for texture"); + return false; + } + + // initiate simple shaders + if (FAILED(d3dCreateShader(VERTEX_SHADER, vs_out_shader_text, &m_pVShader))) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create vertex shader view"); + return false; + } + + if (FAILED(d3dCreateShader(PIXEL_SHADER, ps_out_shader_text, &m_pPShader))) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create pixel shader view"); + return false; + } + + // create EGL buffer from D3D shared texture + EGLint egl_buffer_attrs[] = + { + EGL_WIDTH, static_cast<EGLint>(texDesc.Width), + EGL_HEIGHT, static_cast<EGLint>(texDesc.Height), + EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, + EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, + EGL_NONE + }; + + m_eglBuffer = eglCreatePbufferFromClientBuffer(m_eglDisplay, + EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, + sharedHandle, m_eglConfig, egl_buffer_attrs); + + if (m_eglBuffer == EGL_NO_SURFACE) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create EGL buffer (%s)", eglGetErrorString()); + return false; + } + return true; + } + + HRESULT d3dCreateShader(ShaderType shaderType, const std::string& source, IUnknown** ppShader) const + { + Microsoft::WRL::ComPtr<ID3DBlob> pBlob; + Microsoft::WRL::ComPtr<ID3DBlob> pErrors; + + auto hr = D3DCompile(source.c_str(), source.length(), nullptr, nullptr, nullptr, "main", + shaderType == PIXEL_SHADER ? "ps_4_0" : "vs_4_0", 0, 0, &pBlob, &pErrors); + + if (SUCCEEDED(hr)) + { + Microsoft::WRL::ComPtr<ID3D11Device> pDevice; + m_pContext->GetDevice(&pDevice); + + if (shaderType == PIXEL_SHADER) + { + hr = pDevice->CreatePixelShader(pBlob->GetBufferPointer(), pBlob->GetBufferSize(), nullptr, + reinterpret_cast<ID3D11PixelShader**>(ppShader)); + } + else + { + hr = pDevice->CreateVertexShader(pBlob->GetBufferPointer(), pBlob->GetBufferSize(), nullptr, + reinterpret_cast<ID3D11VertexShader**>(ppShader)); + } + + if (FAILED(hr)) + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to create %s shader", + shaderType == PIXEL_SHADER ? "pixel" : "vertex"); + } + } + else + { + Log(ADDON_LOG_ERROR, "GLonDX: unable to compile shader (%s)", pErrors->GetBufferPointer()); + } + return hr; + } + + static const char* eglGetErrorString() + { +#define CASE_STR( value ) case value: return #value + switch (eglGetError()) + { + CASE_STR(EGL_SUCCESS); + CASE_STR(EGL_NOT_INITIALIZED); + CASE_STR(EGL_BAD_ACCESS); + CASE_STR(EGL_BAD_ALLOC); + CASE_STR(EGL_BAD_ATTRIBUTE); + CASE_STR(EGL_BAD_CONTEXT); + CASE_STR(EGL_BAD_CONFIG); + CASE_STR(EGL_BAD_CURRENT_SURFACE); + CASE_STR(EGL_BAD_DISPLAY); + CASE_STR(EGL_BAD_SURFACE); + CASE_STR(EGL_BAD_MATCH); + CASE_STR(EGL_BAD_PARAMETER); + CASE_STR(EGL_BAD_NATIVE_PIXMAP); + CASE_STR(EGL_BAD_NATIVE_WINDOW); + CASE_STR(EGL_CONTEXT_LOST); + default: + return "Unknown"; + } +#undef CASE_STR + } + + void destruct() + { + if (m_eglDisplay != EGL_NO_DISPLAY) + { + eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (m_eglBuffer != EGL_NO_SURFACE) + { + eglDestroySurface(m_eglDisplay, m_eglBuffer); + m_eglBuffer = EGL_NO_SURFACE; + } + + if (m_eglContext != EGL_NO_CONTEXT) + { + eglDestroyContext(m_eglDisplay, m_eglContext); + m_eglContext = EGL_NO_CONTEXT; + } + + eglTerminate(m_eglDisplay); + m_eglDisplay = EGL_NO_DISPLAY; + } + + m_pSRView = nullptr; + m_pVShader = nullptr; + m_pPShader = nullptr; + m_pContext = nullptr; + } + + EGLConfig m_eglConfig = EGL_NO_CONFIG_KHR; + EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; + EGLContext m_eglContext = EGL_NO_CONTEXT; + EGLSurface m_eglBuffer = EGL_NO_SURFACE; + + ID3D11DeviceContext* m_pContext = nullptr; // don't hold context + Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_pSRView = nullptr; + Microsoft::WRL::ComPtr<ID3D11VertexShader> m_pVShader = nullptr; + Microsoft::WRL::ComPtr<ID3D11PixelShader> m_pPShader = nullptr; + +#define TO_STRING(...) #__VA_ARGS__ + std::string vs_out_shader_text = TO_STRING( + void main(uint id : SV_VertexId, out float2 tex : TEXCOORD0, out float4 pos : SV_POSITION) + { + tex = float2(id % 2, (id % 4) >> 1); + pos = float4((tex.x - 0.5f) * 2, -(tex.y - 0.5f) * 2, 0, 1); + }); + + std::string ps_out_shader_text = TO_STRING( + Texture2D texMain : register(t0); + SamplerState Sampler + { + Filter = MIN_MAG_MIP_LINEAR; + AddressU = CLAMP; + AddressV = CLAMP; + Comparison = NEVER; + }; + + float4 main(in float2 tex : TEXCOORD0) : SV_TARGET + { + return texMain.Sample(Sampler, tex); + }); +#undef TO_STRING +}; /* class CGLonDX */ + +} /* namespace gl */ + +using CRenderHelper = gl::CGLonDX; +} /* namespace gui */ +} /* namespace kodi */ diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/gl/Shader.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/gl/Shader.h new file mode 100644 index 0000000000..350395ad5b --- /dev/null +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/gl/Shader.h @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2005-2019 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "GL.h" + +#include <stdio.h> +#include <vector> +#include <string> + +#include <kodi/AddonBase.h> +#include <kodi/Filesystem.h> + +#define LOG_SIZE 1024 +#define GLchar char + +namespace kodi +{ +namespace gui +{ +namespace gl +{ + +//======================================================================== +/// CShader - base class +class ATTRIBUTE_HIDDEN CShader +{ +public: + CShader() = default; + virtual ~CShader() = default; + virtual bool Compile(const std::string& extraBegin = "", + const std::string& extraEnd = "") = 0; + virtual void Free() = 0; + virtual GLuint Handle() = 0; + + bool LoadSource(const std::string& file) + { + char buffer[16384]; + + kodi::vfs::CFile source; + if (!source.OpenFile(file)) + { + kodi::Log(ADDON_LOG_ERROR, "CShader::%s: Failed to open file '%s'", __FUNCTION__, file.c_str()); + return false; + } + size_t len = source.Read(buffer, sizeof(buffer)); + m_source.assign(buffer); + m_source[len] = 0; + source.Close(); + return true; + } + + bool OK() const { return m_compiled; } + +protected: + std::string m_source; + std::string m_lastLog; + bool m_compiled = false; +}; +//------------------------------------------------------------------------ + +//======================================================================== +/// CVertexShader +class ATTRIBUTE_HIDDEN CVertexShader : public CShader +{ +public: + CVertexShader() = default; + ~CVertexShader() override { Free(); } + + void Free() override + { + if (m_vertexShader) + glDeleteShader(m_vertexShader); + m_vertexShader = 0; + } + + bool Compile(const std::string& extraBegin = "", + const std::string& extraEnd = "") override + { + GLint params[4]; + + Free(); + + m_vertexShader = glCreateShader(GL_VERTEX_SHADER); + + GLsizei count = 0; + const char *sources[3]; + if (!extraBegin.empty()) + sources[count++] = extraBegin.c_str(); + if (!m_source.empty()) + sources[count++] = m_source.c_str(); + if (!extraEnd.empty()) + sources[count++] = extraEnd.c_str(); + + glShaderSource(m_vertexShader, count, sources, nullptr); + glCompileShader(m_vertexShader); + glGetShaderiv(m_vertexShader, GL_COMPILE_STATUS, params); + if (params[0] != GL_TRUE) + { + GLchar log[LOG_SIZE]; + glGetShaderInfoLog(m_vertexShader, LOG_SIZE, nullptr, log); + kodi::Log(ADDON_LOG_ERROR, "CVertexShader::%s: %s", __FUNCTION__, log); + fprintf(stderr, "CVertexShader::%s: %s\n", __FUNCTION__, log); + m_lastLog = log; + m_compiled = false; + } + else + { + GLchar log[LOG_SIZE]; + glGetShaderInfoLog(m_vertexShader, LOG_SIZE, nullptr, log); + m_lastLog = log; + m_compiled = true; + } + return m_compiled; + } + + GLuint Handle() override { return m_vertexShader; } + +protected: + GLuint m_vertexShader = 0; +}; +//------------------------------------------------------------------------ + +//======================================================================== +/// CPixelShader +class ATTRIBUTE_HIDDEN CPixelShader : public CShader +{ +public: + CPixelShader() = default; + ~CPixelShader() { Free(); } + void Free() override + { + if (m_pixelShader) + glDeleteShader(m_pixelShader); + m_pixelShader = 0; + } + + bool Compile(const std::string& extraBegin = "", + const std::string& extraEnd = "") override + { + GLint params[4]; + + Free(); + + m_pixelShader = glCreateShader(GL_FRAGMENT_SHADER); + + GLsizei count = 0; + const char *sources[3]; + if (!extraBegin.empty()) + sources[count++] = extraBegin.c_str(); + if (!m_source.empty()) + sources[count++] = m_source.c_str(); + if (!extraEnd.empty()) + sources[count++] = extraEnd.c_str(); + + glShaderSource(m_pixelShader, count, sources, 0); + glCompileShader(m_pixelShader); + glGetShaderiv(m_pixelShader, GL_COMPILE_STATUS, params); + if (params[0] != GL_TRUE) + { + GLchar log[LOG_SIZE]; + glGetShaderInfoLog(m_pixelShader, LOG_SIZE, nullptr, log); + kodi::Log(ADDON_LOG_ERROR, "CPixelShader::%s: %s", __FUNCTION__, log); + fprintf(stderr, "CPixelShader::%s: %s\n", __FUNCTION__, log); + m_lastLog = log; + m_compiled = false; + } + else + { + GLchar log[LOG_SIZE]; + glGetShaderInfoLog(m_pixelShader, LOG_SIZE, nullptr, log); + m_lastLog = log; + m_compiled = true; + } + return m_compiled; + } + + GLuint Handle() override { return m_pixelShader; } + +protected: + GLuint m_pixelShader = 0; +}; +//------------------------------------------------------------------------ + +//======================================================================== +/// CShaderProgram +class ATTRIBUTE_HIDDEN CShaderProgram +{ +public: + CShaderProgram() = default; + CShaderProgram(const std::string &vert, const std::string &frag) + { + LoadShaderFiles(vert, frag); + } + + virtual ~CShaderProgram() + { + ShaderFree(); + } + + bool LoadShaderFiles(const std::string &vert, const std::string &frag) + { + if (!kodi::vfs::FileExists(vert) || !m_pVP.LoadSource(vert)) + { + kodi::Log(ADDON_LOG_ERROR, "%s: Failed to load '%s'", __func__, vert.c_str()); + return false; + } + + if (!kodi::vfs::FileExists(frag) || !m_pFP.LoadSource(frag)) + { + kodi::Log(ADDON_LOG_ERROR, "%s: Failed to load '%s'", __func__, frag.c_str()); + return false; + } + + return true; + } + + bool CompileAndLink(const std::string& vertexExtraBegin = "", + const std::string& vertexExtraEnd = "", + const std::string& fragmentExtraBegin = "", + const std::string& fragmentExtraEnd = "") + { + GLint params[4]; + + // free resources + ShaderFree(); + m_ok = false; + + // compiled vertex shader + if (!m_pVP.Compile(vertexExtraBegin, vertexExtraEnd)) + { + kodi::Log(ADDON_LOG_ERROR, "GL: Error compiling vertex shader"); + return false; + } + + // compile pixel shader + if (!m_pFP.Compile(fragmentExtraBegin, fragmentExtraEnd)) + { + m_pVP.Free(); + kodi::Log(ADDON_LOG_ERROR, "GL: Error compiling fragment shader"); + return false; + } + + // create program object + m_shaderProgram = glCreateProgram(); + if (!m_shaderProgram) + { + kodi::Log(ADDON_LOG_ERROR, "CShaderProgram::%s: Failed to create GL program", __FUNCTION__); + ShaderFree(); + return false; + } + + // attach the vertex shader + glAttachShader(m_shaderProgram, m_pVP.Handle()); + glAttachShader(m_shaderProgram, m_pFP.Handle()); + + // link the program + glLinkProgram(m_shaderProgram); + glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, params); + if (params[0] != GL_TRUE) + { + GLchar log[LOG_SIZE]; + glGetProgramInfoLog(m_shaderProgram, LOG_SIZE, nullptr, log); + kodi::Log(ADDON_LOG_ERROR, "CShaderProgram::%s: %s", __FUNCTION__, log); + fprintf(stderr, "CShaderProgram::%s: %s\n", __FUNCTION__, log); + ShaderFree(); + return false; + } + + m_validated = false; + m_ok = true; + OnCompiledAndLinked(); + return true; + } + + bool EnableShader() + { + if (ShaderOK()) + { + glUseProgram(m_shaderProgram); + if (OnEnabled()) + { + if (!m_validated) + { + // validate the program + GLint params[4]; + glValidateProgram(m_shaderProgram); + glGetProgramiv(m_shaderProgram, GL_VALIDATE_STATUS, params); + if (params[0] != GL_TRUE) + { + GLchar log[LOG_SIZE]; + glGetProgramInfoLog(m_shaderProgram, LOG_SIZE, nullptr, log); + kodi::Log(ADDON_LOG_ERROR, "CShaderProgram::%s: %s", __FUNCTION__, log); + fprintf(stderr, "CShaderProgram::%s: %s\n", __FUNCTION__, log); + } + m_validated = true; + } + return true; + } + else + { + glUseProgram(0); + return false; + } + return true; + } + return false; + } + + void DisableShader() + { + if (ShaderOK()) + { + glUseProgram(0); + OnDisabled(); + } + } + + ATTRIBUTE_FORCEINLINE bool ShaderOK() const { return m_ok; } + ATTRIBUTE_FORCEINLINE CVertexShader& VertexShader() { return m_pVP; } + ATTRIBUTE_FORCEINLINE CPixelShader& PixelShader() { return m_pFP; } + ATTRIBUTE_FORCEINLINE GLuint ProgramHandle() { return m_shaderProgram; } + + virtual void OnCompiledAndLinked() {}; + virtual bool OnEnabled() { return false; }; + virtual void OnDisabled() {}; + +private: + void ShaderFree() + { + if (m_shaderProgram) + glDeleteProgram(m_shaderProgram); + m_shaderProgram = 0; + m_ok = false; + } + + CVertexShader m_pVP; + CPixelShader m_pFP; + GLuint m_shaderProgram = 0; + bool m_ok = false; + bool m_validated = false; +}; +//------------------------------------------------------------------------ + +} /* namespace gl */ +} /* namespace gui */ +} /* namespace kodi */ diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/renderHelper.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/renderHelper.h new file mode 100644 index 0000000000..87d7b19ab2 --- /dev/null +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/gui/renderHelper.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2019 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "../AddonBase.h" + +namespace kodi +{ +namespace gui +{ +struct IRenderHelper +{ + virtual ~IRenderHelper() = default; + virtual bool Init() = 0; + virtual void Begin() = 0; + virtual void End() = 0; +}; /* class IRenderHelper */ +} /* namespace gui */ +} /* namespace kodi */ + +#if defined(WIN32) && defined(HAS_ANGLE) +#include "gl/GLonDX.h" +#else +/* + * Default background GUI render helper class + */ +namespace kodi +{ +namespace gui +{ +struct CRenderHelperStub : public IRenderHelper +{ + bool Init() override { return true; } + void Begin() override { } + void End() override { } +}; /* class CRenderHelperStub */ + +using CRenderHelper = CRenderHelperStub; +} /* namespace gui */ +} /* namespace kodi */ +#endif + +namespace kodi +{ +namespace gui +{ + +/* + * Create render background handler, e.g. becomes on "Windows" Angle used + * to emulate GL. + * + * This only be used internal and not from addon's direct. + * + * Function defines here and not in CAddonBase because of a hen and egg problem. + */ +inline std::shared_ptr<IRenderHelper> GetRenderHelper() +{ + using namespace ::kodi::addon; + if (CAddonBase::m_interface->addonBase->m_renderHelper) + return CAddonBase::m_interface->addonBase->m_renderHelper; + + const std::shared_ptr<kodi::gui::IRenderHelper> renderHelper(new CRenderHelper()); + if (!renderHelper->Init()) + return nullptr; + + CAddonBase::m_interface->addonBase->m_renderHelper = renderHelper; // Hold on base for other types + return renderHelper; +} + +} /* namespace gui */ +} /* namespace kodi */ diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h index a37dbe014c..4ba563f68c 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h @@ -44,14 +44,14 @@ #define ADDON_GLOBAL_VERSION_GENERAL_XML_ID "kodi.binary.global.general" #define ADDON_GLOBAL_VERSION_GENERAL_DEPENDS "General.h" -#define ADDON_GLOBAL_VERSION_GUI "5.12.0" +#define ADDON_GLOBAL_VERSION_GUI "5.12.1" #define ADDON_GLOBAL_VERSION_GUI_MIN "5.10.0" #define ADDON_GLOBAL_VERSION_GUI_XML_ID "kodi.binary.global.gui" #define ADDON_GLOBAL_VERSION_GUI_DEPENDS "libKODI_guilib.h" \ "gui/" -#define ADDON_GLOBAL_VERSION_AUDIOENGINE "1.0.1" -#define ADDON_GLOBAL_VERSION_AUDIOENGINE_MIN "1.0.1" +#define ADDON_GLOBAL_VERSION_AUDIOENGINE "1.0.2" +#define ADDON_GLOBAL_VERSION_AUDIOENGINE_MIN "1.0.2" #define ADDON_GLOBAL_VERSION_AUDIOENGINE_XML_ID "kodi.binary.global.audioengine" #define ADDON_GLOBAL_VERSION_AUDIOENGINE_DEPENDS "AudioEngine.h" @@ -98,8 +98,8 @@ #define ADDON_INSTANCE_VERSION_PERIPHERAL_DEPENDS "addon-instance/Peripheral.h" \ "addon-instance/PeripheralUtils.h" -#define ADDON_INSTANCE_VERSION_PVR "5.10.4" -#define ADDON_INSTANCE_VERSION_PVR_MIN "5.10.0" +#define ADDON_INSTANCE_VERSION_PVR "6.0.0" +#define ADDON_INSTANCE_VERSION_PVR_MIN "6.0.0" #define ADDON_INSTANCE_VERSION_PVR_XML_ID "kodi.binary.instance.pvr" #define ADDON_INSTANCE_VERSION_PVR_DEPENDS "xbmc_pvr_dll.h" \ "xbmc_pvr_types.h" \ diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_epg_types.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_epg_types.h index 7b11ed8347..30503d5690 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_epg_types.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_epg_types.h @@ -100,7 +100,6 @@ extern "C" { time_t firstAired; /*!< @brief (optional) first aired in UTC */ int iParentalRating; /*!< @brief (optional) parental rating */ int iStarRating; /*!< @brief (optional) star rating */ - bool bNotify; /*!< @brief (optional) notify the user when this event starts */ int iSeriesNumber; /*!< @brief (optional) series number */ int iEpisodeNumber; /*!< @brief (optional) episode number */ int iEpisodePartNumber; /*!< @brief (optional) episode part number */ diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_pvr_dll.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_pvr_dll.h index 42a2abd779..26e9099a71 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_pvr_dll.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_pvr_dll.h @@ -78,14 +78,14 @@ extern "C" * Request the EPG for a channel from the backend. * EPG entries are added to Kodi by calling TransferEpgEntry() on the callback. * @param handle Handle to pass to the callback method. - * @param channel The channel to get the EPG table for. + * @param iChannelUid The UID of the channel to get the EPG table for. * @param iStart Get events after this time (UTC). * @param iEnd Get events before this time (UTC). * @return PVR_ERROR_NO_ERROR if the table has been fetched successfully. * @remarks Required if bSupportsEPG is set to true. * Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. */ - PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL& channel, time_t iStart, time_t iEnd); + PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, int iChannelUid, time_t iStart, time_t iEnd); /* * Check if the given EPG tag can be recorded. @@ -641,16 +641,17 @@ extern "C" void SetSpeed(int speed); /*! - * Get the hostname of the pvr backend server - * @return hostname as ip address or alias. If backend does not utilize a server, return empty string. + * Notify the pvr addon/demuxer that Kodi wishes to fill demux queue + * @param mode The requested filling mode + * @remarks Optional, and only used if addon has its own demuxer. */ - const char* GetBackendHostname(); + void FillBuffer(bool mode); /*! - * Check if timeshift is active - * @return true if timeshift is active + * Get the hostname of the pvr backend server + * @return hostname as ip address or alias. If backend does not utilize a server, return empty string. */ - bool IsTimeshifting(); + const char* GetBackendHostname(); /*! * Check for real-time streaming @@ -753,6 +754,7 @@ extern "C" pClient->toAddon.CanSeekStream = CanSeekStream; pClient->toAddon.SeekTime = SeekTime; pClient->toAddon.SetSpeed = SetSpeed; + pClient->toAddon.FillBuffer = FillBuffer; pClient->toAddon.OpenRecordedStream = OpenRecordedStream; pClient->toAddon.CloseRecordedStream = CloseRecordedStream; @@ -767,7 +769,6 @@ extern "C" pClient->toAddon.GetBackendHostname = GetBackendHostname; - pClient->toAddon.IsTimeshifting = IsTimeshifting; pClient->toAddon.IsRealTimeStream = IsRealTimeStream; pClient->toAddon.SetEPGTimeFrame = SetEPGTimeFrame; diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_pvr_types.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_pvr_types.h index 4752023bac..999d3b391b 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_pvr_types.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_pvr_types.h @@ -55,8 +55,8 @@ struct DemuxPacket; #define PVR_ADDON_TIMERTYPE_ARRAY_SIZE 32 #define PVR_ADDON_TIMERTYPE_VALUES_ARRAY_SIZE 512 #define PVR_ADDON_TIMERTYPE_VALUES_ARRAY_SIZE_SMALL 128 -#define PVR_ADDON_TIMERTYPE_STRING_LENGTH 64 -#define PVR_ADDON_ATTRIBUTE_DESC_LENGTH 64 +#define PVR_ADDON_TIMERTYPE_STRING_LENGTH 128 +#define PVR_ADDON_ATTRIBUTE_DESC_LENGTH 128 #define PVR_ADDON_ATTRIBUTE_VALUES_ARRAY_SIZE 512 #define PVR_ADDON_DESCRAMBLE_INFO_STRING_LENGTH 64 @@ -311,12 +311,10 @@ extern "C" { bool bSupportsRecordingsRename; /*!< @brief true if the backend supports renaming recordings. */ bool bSupportsRecordingsLifetimeChange; /*!< @brief true if the backend supports changing lifetime for recordings. */ bool bSupportsDescrambleInfo; /*!< @brief true if the backend supports descramble information for playing channels. */ + bool bSupportsAsyncEPGTransfer; /*!< @brief true if this addon-on supports asynchronous transfer of epg events to Kodi using the callback function EpgEventStateChange. */ unsigned int iRecordingsLifetimesSize; /*!< @brief (required) Count of possible values for PVR_RECORDING.iLifetime. 0 means lifetime is not supported for recordings or no own value definition wanted, but to use Kodi defaults of 1..365. */ PVR_ATTRIBUTE_INT_VALUE recordingsLifetimeValues[PVR_ADDON_ATTRIBUTE_VALUES_ARRAY_SIZE]; /*!< @brief (optional) Array containing the possible values for PVR_RECORDING.iLifetime. Must be filled if iLifetimesSize > 0 */ - - // TODO: cleanup: move this member up after the other bools with the next incompatible pvr addon api change. - bool bSupportsAsyncEPGTransfer; /*!< @brief true if this addon-on supports asynchronous transfer of epg events to Kodi using the callback function EpgEventStateChange. */ } ATTRIBUTE_PACKED PVR_ADDON_CAPABILITIES; /*! @@ -403,6 +401,7 @@ extern "C" { unsigned int iEncryptionSystem; /*!< @brief (optional) the encryption ID or CaID of this channel */ char strIconPath[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) path to the channel icon (if present) */ bool bIsHidden; /*!< @brief (optional) true if this channel is marked as hidden */ + bool bHasArchive; /*!< @brief (optional) true if this channel has a server-side back buffer */ } ATTRIBUTE_PACKED PVR_CHANNEL; typedef struct PVR_CHANNEL_GROUP @@ -635,7 +634,7 @@ extern "C" { const char* (__cdecl* GetConnectionString)(void); PVR_ERROR (__cdecl* GetDriveSpace)(long long*, long long*); PVR_ERROR (__cdecl* MenuHook)(const PVR_MENUHOOK&, const PVR_MENUHOOK_DATA&); - PVR_ERROR (__cdecl* GetEPGForChannel)(ADDON_HANDLE, const PVR_CHANNEL&, time_t, time_t); + PVR_ERROR (__cdecl* GetEPGForChannel)(ADDON_HANDLE, int, time_t, time_t); PVR_ERROR (__cdecl* IsEPGTagRecordable)(const EPG_TAG*, bool*); PVR_ERROR (__cdecl* IsEPGTagPlayable)(const EPG_TAG*, bool*); PVR_ERROR (__cdecl* GetEPGTagEdl)(const EPG_TAG*, PVR_EDL_ENTRY[], int*); @@ -691,8 +690,8 @@ extern "C" { bool (__cdecl* CanSeekStream)(void); bool (__cdecl* SeekTime)(double, bool, double*); void (__cdecl* SetSpeed)(int); + void (__cdecl* FillBuffer)(bool); const char* (__cdecl* GetBackendHostname)(void); - bool (__cdecl* IsTimeshifting)(void); bool (__cdecl* IsRealTimeStream)(void); PVR_ERROR (__cdecl* SetEPGTimeFrame)(int); void (__cdecl* OnSystemSleep)(void); diff --git a/xbmc/addons/settings/AddonSettings.cpp b/xbmc/addons/settings/AddonSettings.cpp index 00fb3bb4fd..1b86a72b9e 100644 --- a/xbmc/addons/settings/AddonSettings.cpp +++ b/xbmc/addons/settings/AddonSettings.cpp @@ -22,6 +22,7 @@ #include "guilib/LocalizeStrings.h" #include "messaging/ApplicationMessenger.h" #include "settings/SettingAddon.h" +#include "settings/SettingConditions.h" #include "settings/SettingControl.h" #include "settings/SettingDateTime.h" #include "settings/SettingPath.h" @@ -392,6 +393,13 @@ void CAddonSettings::InitializeControls() void CAddonSettings::InitializeConditions() { + CSettingConditions::Initialize(); + + // add basic conditions + const std::set<std::string>& simpleConditions = CSettingConditions::GetSimpleConditions(); + for (const auto& condition : simpleConditions) + GetSettingsManager()->AddCondition(condition); + GetSettingsManager()->AddDynamicCondition("InfoBool", InfoBool); } diff --git a/xbmc/cores/RetroPlayer/buffers/RenderBufferPoolOpenGL.cpp b/xbmc/cores/RetroPlayer/buffers/RenderBufferPoolOpenGL.cpp index b774c91ab3..3ef9356b7b 100644 --- a/xbmc/cores/RetroPlayer/buffers/RenderBufferPoolOpenGL.cpp +++ b/xbmc/cores/RetroPlayer/buffers/RenderBufferPoolOpenGL.cpp @@ -18,10 +18,7 @@ using namespace RETRO; bool CRenderBufferPoolOpenGL::IsCompatible(const CRenderVideoSettings &renderSettings) const { - if (!CRPRendererOpenGL::SupportsScalingMethod(renderSettings.GetScalingMethod())) - return false; - - return true; + return CRPRendererOpenGL::SupportsScalingMethod(renderSettings.GetScalingMethod()); } IRenderBuffer *CRenderBufferPoolOpenGL::CreateRenderBuffer(void *header /* = nullptr */) diff --git a/xbmc/cores/RetroPlayer/buffers/RenderBufferPoolOpenGLES.cpp b/xbmc/cores/RetroPlayer/buffers/RenderBufferPoolOpenGLES.cpp index e90a55bdd2..3e9c763ddf 100644 --- a/xbmc/cores/RetroPlayer/buffers/RenderBufferPoolOpenGLES.cpp +++ b/xbmc/cores/RetroPlayer/buffers/RenderBufferPoolOpenGLES.cpp @@ -23,10 +23,7 @@ CRenderBufferPoolOpenGLES::CRenderBufferPoolOpenGLES(CRenderContext &context) bool CRenderBufferPoolOpenGLES::IsCompatible(const CRenderVideoSettings &renderSettings) const { - if (!CRPRendererOpenGLES::SupportsScalingMethod(renderSettings.GetScalingMethod())) - return false; - - return true; + return CRPRendererOpenGLES::SupportsScalingMethod(renderSettings.GetScalingMethod()); } IRenderBuffer *CRenderBufferPoolOpenGLES::CreateRenderBuffer(void *header /* = nullptr */) diff --git a/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.cpp b/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.cpp index 48535180cb..9295d85107 100644 --- a/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.cpp +++ b/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPBaseRenderer.cpp @@ -36,10 +36,7 @@ CRPBaseRenderer::~CRPBaseRenderer() bool CRPBaseRenderer::IsCompatible(const CRenderVideoSettings &settings) const { - if (!m_bufferPool->IsCompatible(settings)) - return false; - - return true; + return m_bufferPool->IsCompatible(settings); } bool CRPBaseRenderer::Configure(AVPixelFormat format) @@ -70,10 +67,7 @@ void CRPBaseRenderer::FrameMove() bool CRPBaseRenderer::IsVisible() const { - if (m_renderFrameCount <= m_lastRender + VISIBLE_DURATION_FRAME_COUNT) - return true; - - return false; + return m_renderFrameCount <= m_lastRender + VISIBLE_DURATION_FRAME_COUNT; } void CRPBaseRenderer::SetBuffer(IRenderBuffer *buffer) diff --git a/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPRendererGuiTexture.cpp b/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPRendererGuiTexture.cpp index 5485358cad..01f30710da 100644 --- a/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPRendererGuiTexture.cpp +++ b/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPRendererGuiTexture.cpp @@ -53,10 +53,7 @@ CRenderBufferPoolGuiTexture::CRenderBufferPoolGuiTexture(SCALINGMETHOD scalingMe bool CRenderBufferPoolGuiTexture::IsCompatible(const CRenderVideoSettings &renderSettings) const { - if (renderSettings.GetScalingMethod() != m_scalingMethod) - return false; - - return true; + return renderSettings.GetScalingMethod() == m_scalingMethod; } IRenderBuffer *CRenderBufferPoolGuiTexture::CreateRenderBuffer(void *header /* = nullptr */) @@ -73,15 +70,10 @@ CRPRendererGuiTexture::CRPRendererGuiTexture(const CRenderSettings &renderSettin bool CRPRendererGuiTexture::Supports(RENDERFEATURE feature) const { - if (feature == RENDERFEATURE::STRETCH || - feature == RENDERFEATURE::ZOOM || - feature == RENDERFEATURE::PIXEL_RATIO || - feature == RENDERFEATURE::ROTATION) - { - return true; - } - - return false; + return feature == RENDERFEATURE::STRETCH || + feature == RENDERFEATURE::ZOOM || + feature == RENDERFEATURE::PIXEL_RATIO || + feature == RENDERFEATURE::ROTATION; } void CRPRendererGuiTexture::RenderInternal(bool clear, uint8_t alpha) diff --git a/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGL.cpp b/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGL.cpp index 6bb90b1887..8e91704440 100644 --- a/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGL.cpp +++ b/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGL.cpp @@ -127,26 +127,16 @@ void CRPRendererOpenGL::FlushInternal() bool CRPRendererOpenGL::Supports(RENDERFEATURE feature) const { - if (feature == RENDERFEATURE::STRETCH || - feature == RENDERFEATURE::ZOOM || - feature == RENDERFEATURE::PIXEL_RATIO || - feature == RENDERFEATURE::ROTATION) - { - return true; - } - - return false; + return feature == RENDERFEATURE::STRETCH || + feature == RENDERFEATURE::ZOOM || + feature == RENDERFEATURE::PIXEL_RATIO || + feature == RENDERFEATURE::ROTATION; } bool CRPRendererOpenGL::SupportsScalingMethod(SCALINGMETHOD method) { - if (method == SCALINGMETHOD::NEAREST || - method == SCALINGMETHOD::LINEAR) - { - return true; - } - - return false; + return method == SCALINGMETHOD::NEAREST || + method == SCALINGMETHOD::LINEAR; } void CRPRendererOpenGL::ClearBackBuffer() diff --git a/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGLES.cpp b/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGLES.cpp index a613f7674a..057a00541f 100644 --- a/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGLES.cpp +++ b/xbmc/cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGLES.cpp @@ -92,26 +92,16 @@ void CRPRendererOpenGLES::FlushInternal() bool CRPRendererOpenGLES::Supports(RENDERFEATURE feature) const { - if (feature == RENDERFEATURE::STRETCH || - feature == RENDERFEATURE::ZOOM || - feature == RENDERFEATURE::PIXEL_RATIO || - feature == RENDERFEATURE::ROTATION) - { - return true; - } - - return false; + return feature == RENDERFEATURE::STRETCH || + feature == RENDERFEATURE::ZOOM || + feature == RENDERFEATURE::PIXEL_RATIO || + feature == RENDERFEATURE::ROTATION; } bool CRPRendererOpenGLES::SupportsScalingMethod(SCALINGMETHOD method) { - if (method == SCALINGMETHOD::NEAREST || - method == SCALINGMETHOD::LINEAR) - { - return true; - } - - return false; + return method == SCALINGMETHOD::NEAREST || + method == SCALINGMETHOD::LINEAR; } void CRPRendererOpenGLES::ClearBackBuffer() diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp index 382b163a71..48796e944b 100644 --- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp +++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp @@ -10,6 +10,7 @@ #include "ServiceBroker.h" #include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h" +#include "cores/VideoPlayer/Process/gbm/VideoBufferDRMPRIME.h" #include "settings/Settings.h" #include "settings/SettingsComponent.h" #include "settings/lib/Setting.h" @@ -24,137 +25,6 @@ extern "C" { using namespace KODI::WINDOWING::GBM; -//------------------------------------------------------------------------------ -// Video Buffers -//------------------------------------------------------------------------------ - -CVideoBufferDRMPRIME::CVideoBufferDRMPRIME(IVideoBufferPool& pool, int id) - : CVideoBuffer(id) -{ - m_pFrame = av_frame_alloc(); -} - -CVideoBufferDRMPRIME::~CVideoBufferDRMPRIME() -{ - Unref(); - av_frame_free(&m_pFrame); -} - -void CVideoBufferDRMPRIME::SetRef(AVFrame* frame) -{ - av_frame_move_ref(m_pFrame, frame); -} - -void CVideoBufferDRMPRIME::Unref() -{ - av_frame_unref(m_pFrame); -} - -int CVideoBufferDRMPRIME::GetColorEncoding() const -{ - switch (m_pFrame->colorspace) - { - case AVCOL_SPC_BT2020_CL: - case AVCOL_SPC_BT2020_NCL: - return DRM_COLOR_YCBCR_BT2020; - case AVCOL_SPC_SMPTE170M: - case AVCOL_SPC_BT470BG: - case AVCOL_SPC_FCC: - return DRM_COLOR_YCBCR_BT601; - case AVCOL_SPC_BT709: - return DRM_COLOR_YCBCR_BT709; - case AVCOL_SPC_RESERVED: - case AVCOL_SPC_UNSPECIFIED: - default: - if (m_pFrame->width > 1024 || m_pFrame->height >= 600) - return DRM_COLOR_YCBCR_BT709; - else - return DRM_COLOR_YCBCR_BT601; - } -} - -int CVideoBufferDRMPRIME::GetColorRange() const -{ - switch (m_pFrame->color_range) - { - case AVCOL_RANGE_JPEG: - return DRM_COLOR_YCBCR_FULL_RANGE; - case AVCOL_RANGE_MPEG: - default: - return DRM_COLOR_YCBCR_LIMITED_RANGE; - } -} - -//------------------------------------------------------------------------------ - -class CVideoBufferPoolDRMPRIME - : public IVideoBufferPool -{ -public: - ~CVideoBufferPoolDRMPRIME(); - void Return(int id) override; - CVideoBuffer* Get() override; - -protected: - CCriticalSection m_critSection; - std::vector<CVideoBufferDRMPRIME*> m_all; - std::deque<int> m_used; - std::deque<int> m_free; -}; - -CVideoBufferPoolDRMPRIME::~CVideoBufferPoolDRMPRIME() -{ - for (auto buf : m_all) - delete buf; -} - -CVideoBuffer* CVideoBufferPoolDRMPRIME::Get() -{ - CSingleLock lock(m_critSection); - - CVideoBufferDRMPRIME* buf = nullptr; - if (!m_free.empty()) - { - int idx = m_free.front(); - m_free.pop_front(); - m_used.push_back(idx); - buf = m_all[idx]; - } - else - { - int id = m_all.size(); - buf = new CVideoBufferDRMPRIME(*this, id); - m_all.push_back(buf); - m_used.push_back(id); - } - - buf->Acquire(GetPtr()); - return buf; -} - -void CVideoBufferPoolDRMPRIME::Return(int id) -{ - CSingleLock lock(m_critSection); - - m_all[id]->Unref(); - auto it = m_used.begin(); - while (it != m_used.end()) - { - if (*it == id) - { - m_used.erase(it); - break; - } - else - ++it; - } - m_free.push_back(id); -} - -//------------------------------------------------------------------------------ -// main class -//------------------------------------------------------------------------------ - CDVDVideoCodecDRMPRIME::CDVDVideoCodecDRMPRIME(CProcessInfo& processInfo) : CDVDVideoCodec(processInfo) { @@ -220,11 +90,17 @@ static const AVCodec* FindDecoder(CDVDStreamInfo& hints) return nullptr; } -static enum AVPixelFormat GetFormat(struct AVCodecContext* avctx, const enum AVPixelFormat* fmt) +enum AVPixelFormat CDVDVideoCodecDRMPRIME::GetFormat(struct AVCodecContext* avctx, const enum AVPixelFormat* fmt) { for (int n = 0; fmt[n] != AV_PIX_FMT_NONE; n++) + { if (fmt[n] == AV_PIX_FMT_DRM_PRIME) + { + CDVDVideoCodecDRMPRIME* ctx = static_cast<CDVDVideoCodecDRMPRIME*>(avctx->opaque); + ctx->UpdateProcessInfo(avctx, fmt[n]); return fmt[n]; + } + } return AV_PIX_FMT_NONE; } @@ -259,6 +135,7 @@ bool CDVDVideoCodecDRMPRIME::Open(CDVDStreamInfo& hints, CDVDCodecOptions& optio } m_pCodecContext->pix_fmt = AV_PIX_FMT_DRM_PRIME; + m_pCodecContext->opaque = static_cast<void*>(this); m_pCodecContext->get_format = GetFormat; m_pCodecContext->codec_tag = hints.codec_tag; m_pCodecContext->coded_width = hints.width; @@ -281,27 +158,25 @@ bool CDVDVideoCodecDRMPRIME::Open(CDVDStreamInfo& hints, CDVDCodecOptions& optio return false; } - if (m_pCodecContext->pix_fmt != AV_PIX_FMT_DRM_PRIME) - { - CLog::Log(LOGNOTICE, "CDVDVideoCodecDRMPRIME::%s - unexpected pix fmt %s", __FUNCTION__, av_get_pix_fmt_name(m_pCodecContext->pix_fmt)); - avcodec_free_context(&m_pCodecContext); - return false; - } - - const char* pixFmtName = av_get_pix_fmt_name(m_pCodecContext->pix_fmt); - m_processInfo.SetVideoPixelFormat(pixFmtName ? pixFmtName : ""); - m_processInfo.SetVideoDimensions(hints.width, hints.height); + UpdateProcessInfo(m_pCodecContext, m_pCodecContext->pix_fmt); m_processInfo.SetVideoDeintMethod("none"); m_processInfo.SetVideoDAR(hints.aspect); - if (pCodec->name) - m_name = std::string("ff-") + pCodec->name; + return true; +} + +void CDVDVideoCodecDRMPRIME::UpdateProcessInfo(struct AVCodecContext* avctx, const enum AVPixelFormat pix_fmt) +{ + const char* pixFmtName = av_get_pix_fmt_name(pix_fmt); + m_processInfo.SetVideoPixelFormat(pixFmtName ? pixFmtName : ""); + m_processInfo.SetVideoDimensions(avctx->coded_width, avctx->coded_height); + + if (avctx->codec && avctx->codec->name) + m_name = std::string("ff-") + avctx->codec->name; else m_name = "ffmpeg"; - m_processInfo.SetVideoDecoderName(m_name, true); - - return true; + m_processInfo.SetVideoDecoderName(m_name, pix_fmt == AV_PIX_FMT_DRM_PRIME); } bool CDVDVideoCodecDRMPRIME::AddData(const DemuxPacket& packet) @@ -412,9 +287,18 @@ CDVDVideoCodec::VCReturn CDVDVideoCodecDRMPRIME::GetPicture(VideoPicture* pVideo SetPictureParams(pVideoPicture); - CVideoBufferDRMPRIME* buffer = dynamic_cast<CVideoBufferDRMPRIME*>(m_videoBufferPool->Get()); - buffer->SetRef(m_pFrame); - pVideoPicture->videoBuffer = buffer; + if (m_pFrame->format == AV_PIX_FMT_DRM_PRIME) + { + CVideoBufferDRMPRIME* buffer = dynamic_cast<CVideoBufferDRMPRIME*>(m_videoBufferPool->Get()); + buffer->SetRef(m_pFrame); + pVideoPicture->videoBuffer = buffer; + } + + if (!pVideoPicture->videoBuffer) + { + CLog::Log(LOGERROR, "CDVDVideoCodecDRMPRIME::{} - videoBuffer:nullptr format:{}", __FUNCTION__, av_get_pix_fmt_name(static_cast<AVPixelFormat>(m_pFrame->format))); + return VC_ERROR; + } return VC_PICTURE; } diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h index cb2929e545..8d399eb5bc 100644 --- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h +++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h @@ -13,45 +13,6 @@ #include "cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h" #include "cores/VideoPlayer/Process/VideoBuffer.h" -extern "C" { -#include <libavutil/frame.h> -#include <libavutil/hwcontext_drm.h> -} - -// Color enums is copied from linux include/drm/drm_color_mgmt.h (strangely not part of uapi) -enum drm_color_encoding { - DRM_COLOR_YCBCR_BT601, - DRM_COLOR_YCBCR_BT709, - DRM_COLOR_YCBCR_BT2020, -}; -enum drm_color_range { - DRM_COLOR_YCBCR_LIMITED_RANGE, - DRM_COLOR_YCBCR_FULL_RANGE, -}; - -class CVideoBufferPoolDRMPRIME; - -class CVideoBufferDRMPRIME - : public CVideoBuffer -{ -public: - CVideoBufferDRMPRIME(IVideoBufferPool& pool, int id); - ~CVideoBufferDRMPRIME(); - void SetRef(AVFrame* frame); - void Unref(); - - uint32_t m_fb_id = 0; - uint32_t m_handles[AV_DRM_MAX_PLANES] = {0}; - - AVDRMFrameDescriptor* GetDescriptor() const { return reinterpret_cast<AVDRMFrameDescriptor*>(m_pFrame->data[0]); } - uint32_t GetWidth() const { return m_pFrame->width; } - uint32_t GetHeight() const { return m_pFrame->height; } - int GetColorEncoding() const; - int GetColorRange() const; -protected: - AVFrame* m_pFrame = nullptr; -}; - class CDVDVideoCodecDRMPRIME : public CDVDVideoCodec { @@ -73,10 +34,12 @@ public: protected: void Drain(); void SetPictureParams(VideoPicture* pVideoPicture); + void UpdateProcessInfo(struct AVCodecContext* avctx, const enum AVPixelFormat fmt); + static enum AVPixelFormat GetFormat(struct AVCodecContext* avctx, const enum AVPixelFormat* fmt); std::string m_name; int m_codecControlFlags = 0; AVCodecContext* m_pCodecContext = nullptr; AVFrame* m_pFrame = nullptr; - std::shared_ptr<CVideoBufferPoolDRMPRIME> m_videoBufferPool; + std::shared_ptr<IVideoBufferPool> m_videoBufferPool; }; diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemux.h b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemux.h index a8a6dd9b24..65803dc868 100644 --- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemux.h +++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemux.h @@ -278,6 +278,11 @@ public: virtual void SetSpeed(int iSpeed) { } /* + * Let demuxer know if we want to fill demux queue + */ + virtual void FillBuffer(bool mode) { } + + /* * returns the total time in msec */ virtual int GetStreamLength() { return 0; } diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp index 48e8275c22..994c28c278 100644 --- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp +++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp @@ -631,6 +631,14 @@ void CDVDDemuxClient::SetSpeed (int speed) } } +void CDVDDemuxClient::FillBuffer(bool mode) +{ + if (m_IDemux) + { + m_IDemux->FillBuffer(mode); + } +} + void CDVDDemuxClient::EnableStream(int id, bool enable) { if (m_IDemux) diff --git a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.h b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.h index c96a936d34..9f4ef7e374 100644 --- a/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.h +++ b/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.h @@ -32,6 +32,7 @@ public: DemuxPacket* Read() override; bool SeekTime(double time, bool backwards = false, double* startpts = NULL) override; void SetSpeed(int iSpeed) override; + void FillBuffer(bool mode) override; CDemuxStream* GetStream(int iStreamId) const override; std::vector<CDemuxStream*> GetStreams() const override; int GetNrOfStreams() const override; diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStream.h b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStream.h index 8dc39442c9..bb48694025 100644 --- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStream.h +++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStream.h @@ -128,6 +128,7 @@ public: virtual bool OpenStream(int iStreamId) { return false; }; virtual int GetNrOfStreams() const = 0; virtual void SetSpeed(int iSpeed) = 0; + virtual void FillBuffer(bool mode) {}; virtual bool SeekTime(double time, bool backward = false, double* startpts = NULL) = 0; virtual void AbortDemux() = 0; virtual void FlushDemux() = 0; diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.cpp index ac2fa2fbc8..d706bc30d7 100644 --- a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.cpp +++ b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.cpp @@ -217,6 +217,12 @@ void CInputStreamPVRBase::SetSpeed(int Speed) m_client->SetSpeed(Speed); } +void CInputStreamPVRBase::FillBuffer(bool mode) +{ + if (m_client) + m_client->FillBuffer(mode); +} + bool CInputStreamPVRBase::SeekTime(double timems, bool backwards, double *startpts) { if (m_client) diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.h b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.h index c339599ccd..66e6762354 100644 --- a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.h +++ b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamPVRBase.h @@ -58,6 +58,7 @@ public: std::vector<CDemuxStream*> GetStreams() const override; int GetNrOfStreams() const override; void SetSpeed(int iSpeed) override; + void FillBuffer(bool mode) override; bool SeekTime(double time, bool backward = false, double* startpts = NULL) override; void AbortDemux() override; void FlushDemux() override; diff --git a/xbmc/cores/VideoPlayer/Process/gbm/CMakeLists.txt b/xbmc/cores/VideoPlayer/Process/gbm/CMakeLists.txt new file mode 100644 index 0000000000..d1398d77a3 --- /dev/null +++ b/xbmc/cores/VideoPlayer/Process/gbm/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SOURCES ProcessInfoGBM.cpp + VideoBufferDRMPRIME.cpp) + +set(HEADERS ProcessInfoGBM.h + VideoBufferDRMPRIME.h) + +core_add_library(processGBM) diff --git a/xbmc/cores/VideoPlayer/Process/gbm/ProcessInfoGBM.cpp b/xbmc/cores/VideoPlayer/Process/gbm/ProcessInfoGBM.cpp new file mode 100644 index 0000000000..0228319397 --- /dev/null +++ b/xbmc/cores/VideoPlayer/Process/gbm/ProcessInfoGBM.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "ProcessInfoGBM.h" + +using namespace VIDEOPLAYER; + +CProcessInfo* CProcessInfoGBM::Create() +{ + return new CProcessInfoGBM(); +} + +void CProcessInfoGBM::Register() +{ + CProcessInfo::RegisterProcessControl("gbm", CProcessInfoGBM::Create); +} + +CProcessInfoGBM::CProcessInfoGBM() +{ +} + +EINTERLACEMETHOD CProcessInfoGBM::GetFallbackDeintMethod() +{ +#if defined(__arm__) + return EINTERLACEMETHOD::VS_INTERLACEMETHOD_DEINTERLACE_HALF; +#else + return CProcessInfo::GetFallbackDeintMethod(); +#endif +} diff --git a/xbmc/cores/VideoPlayer/Process/gbm/ProcessInfoGBM.h b/xbmc/cores/VideoPlayer/Process/gbm/ProcessInfoGBM.h new file mode 100644 index 0000000000..479e4fde4c --- /dev/null +++ b/xbmc/cores/VideoPlayer/Process/gbm/ProcessInfoGBM.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "cores/IPlayer.h" +#include "cores/VideoPlayer/Process/ProcessInfo.h" + +namespace VIDEOPLAYER +{ + +class CProcessInfoGBM : public CProcessInfo +{ +public: + CProcessInfoGBM(); + static CProcessInfo* Create(); + static void Register(); + EINTERLACEMETHOD GetFallbackDeintMethod() override; +}; + +} // namespace VIDEOPLAYER diff --git a/xbmc/cores/VideoPlayer/Process/gbm/VideoBufferDRMPRIME.cpp b/xbmc/cores/VideoPlayer/Process/gbm/VideoBufferDRMPRIME.cpp new file mode 100644 index 0000000000..719f0185bc --- /dev/null +++ b/xbmc/cores/VideoPlayer/Process/gbm/VideoBufferDRMPRIME.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2017-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "VideoBufferDRMPRIME.h" + +#include "threads/SingleLock.h" + +extern "C" +{ +#include <libavcodec/avcodec.h> +#include <libavutil/pixdesc.h> +} + +IVideoBufferDRMPRIME::IVideoBufferDRMPRIME(int id) + : CVideoBuffer(id) +{ +} + +CVideoBufferDRMPRIME::CVideoBufferDRMPRIME(IVideoBufferPool& pool, int id) + : IVideoBufferDRMPRIME(id) +{ + m_pFrame = av_frame_alloc(); +} + +CVideoBufferDRMPRIME::~CVideoBufferDRMPRIME() +{ + Unref(); + av_frame_free(&m_pFrame); +} + +void CVideoBufferDRMPRIME::SetRef(AVFrame* frame) +{ + av_frame_move_ref(m_pFrame, frame); +} + +void CVideoBufferDRMPRIME::Unref() +{ + av_frame_unref(m_pFrame); +} + +int CVideoBufferDRMPRIME::GetColorEncoding() const +{ + switch (m_pFrame->colorspace) + { + case AVCOL_SPC_BT2020_CL: + case AVCOL_SPC_BT2020_NCL: + return DRM_COLOR_YCBCR_BT2020; + case AVCOL_SPC_SMPTE170M: + case AVCOL_SPC_BT470BG: + case AVCOL_SPC_FCC: + return DRM_COLOR_YCBCR_BT601; + case AVCOL_SPC_BT709: + return DRM_COLOR_YCBCR_BT709; + case AVCOL_SPC_RESERVED: + case AVCOL_SPC_UNSPECIFIED: + default: + if (m_pFrame->width > 1024 || m_pFrame->height >= 600) + return DRM_COLOR_YCBCR_BT709; + else + return DRM_COLOR_YCBCR_BT601; + } +} + +int CVideoBufferDRMPRIME::GetColorRange() const +{ + switch (m_pFrame->color_range) + { + case AVCOL_RANGE_JPEG: + return DRM_COLOR_YCBCR_FULL_RANGE; + case AVCOL_RANGE_MPEG: + default: + return DRM_COLOR_YCBCR_LIMITED_RANGE; + } +} + +bool CVideoBufferDRMPRIME::IsValid() const +{ + AVDRMFrameDescriptor* descriptor = GetDescriptor(); + return descriptor && descriptor->nb_layers; +} + +CVideoBufferPoolDRMPRIME::~CVideoBufferPoolDRMPRIME() +{ + for (auto buf : m_all) + delete buf; +} + +CVideoBuffer* CVideoBufferPoolDRMPRIME::Get() +{ + CSingleLock lock(m_critSection); + + CVideoBufferDRMPRIME* buf = nullptr; + if (!m_free.empty()) + { + int idx = m_free.front(); + m_free.pop_front(); + m_used.push_back(idx); + buf = m_all[idx]; + } + else + { + int id = m_all.size(); + buf = new CVideoBufferDRMPRIME(*this, id); + m_all.push_back(buf); + m_used.push_back(id); + } + + buf->Acquire(GetPtr()); + return buf; +} + +void CVideoBufferPoolDRMPRIME::Return(int id) +{ + CSingleLock lock(m_critSection); + + m_all[id]->Unref(); + auto it = m_used.begin(); + while (it != m_used.end()) + { + if (*it == id) + { + m_used.erase(it); + break; + } + else + ++it; + } + m_free.push_back(id); +} diff --git a/xbmc/cores/VideoPlayer/Process/gbm/VideoBufferDRMPRIME.h b/xbmc/cores/VideoPlayer/Process/gbm/VideoBufferDRMPRIME.h new file mode 100644 index 0000000000..643395a681 --- /dev/null +++ b/xbmc/cores/VideoPlayer/Process/gbm/VideoBufferDRMPRIME.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "cores/VideoPlayer/Process/VideoBuffer.h" + +extern "C" +{ +#include <libavutil/frame.h> +#include <libavutil/hwcontext_drm.h> +} + +// Color enums is copied from linux include/drm/drm_color_mgmt.h (strangely not part of uapi) +enum drm_color_encoding +{ + DRM_COLOR_YCBCR_BT601, + DRM_COLOR_YCBCR_BT709, + DRM_COLOR_YCBCR_BT2020, +}; +enum drm_color_range +{ + DRM_COLOR_YCBCR_LIMITED_RANGE, + DRM_COLOR_YCBCR_FULL_RANGE, +}; + +class IVideoBufferDRMPRIME : public CVideoBuffer +{ +public: + IVideoBufferDRMPRIME() = delete; + virtual ~IVideoBufferDRMPRIME() = default; + + virtual AVDRMFrameDescriptor* GetDescriptor() const = 0; + virtual uint32_t GetWidth() const = 0; + virtual uint32_t GetHeight() const = 0; + virtual int GetColorEncoding() const + { + return DRM_COLOR_YCBCR_BT709; + }; + virtual int GetColorRange() const + { + return DRM_COLOR_YCBCR_LIMITED_RANGE; + }; + + virtual bool IsValid() const + { + return true; + }; + virtual bool Map() + { + return true; + }; + virtual void Unmap() {}; + + uint32_t m_fb_id = 0; + uint32_t m_handles[AV_DRM_MAX_PLANES] = {}; + +protected: + explicit IVideoBufferDRMPRIME(int id); +}; + +class CVideoBufferDRMPRIME : public IVideoBufferDRMPRIME +{ +public: + CVideoBufferDRMPRIME(IVideoBufferPool& pool, int id); + ~CVideoBufferDRMPRIME(); + void SetRef(AVFrame* frame); + void Unref(); + + AVDRMFrameDescriptor* GetDescriptor() const override + { + return reinterpret_cast<AVDRMFrameDescriptor*>(m_pFrame->data[0]); + } + uint32_t GetWidth() const override + { + return m_pFrame->width; + } + uint32_t GetHeight() const override + { + return m_pFrame->height; + } + int GetColorEncoding() const override; + int GetColorRange() const override; + + bool IsValid() const override; + +protected: + AVFrame* m_pFrame = nullptr; +}; + +class CVideoBufferPoolDRMPRIME : public IVideoBufferPool +{ +public: + ~CVideoBufferPoolDRMPRIME(); + void Return(int id) override; + CVideoBuffer* Get() override; + +protected: + CCriticalSection m_critSection; + std::vector<CVideoBufferDRMPRIME*> m_all; + std::deque<int> m_used; + std::deque<int> m_free; +}; diff --git a/xbmc/cores/VideoPlayer/VideoPlayer.cpp b/xbmc/cores/VideoPlayer/VideoPlayer.cpp index 4ea7b45812..c62a26b860 100644 --- a/xbmc/cores/VideoPlayer/VideoPlayer.cpp +++ b/xbmc/cores/VideoPlayer/VideoPlayer.cpp @@ -1447,6 +1447,24 @@ void CVideoPlayer::Process() // make sure we run subtitle process here m_VideoPlayerSubtitle->Process(m_clock.GetClock() + m_State.time_offset - m_VideoPlayerVideo->GetSubtitleDelay(), m_State.time_offset); + // tell demuxer if we want to fill buffers + if (m_demuxerSpeed != DVD_PLAYSPEED_PAUSE) + { + int audioLevel = 90; + int videoLevel = 90; + bool fillBuffer = false; + if (m_CurrentAudio.id >= 0) + audioLevel = m_VideoPlayerAudio->GetLevel(); + if (m_CurrentVideo.id >= 0) + videoLevel = m_processInfo->GetLevelVQ(); + if (videoLevel < 85 && audioLevel < 85) + { + fillBuffer = true; + } + if (m_pDemuxer) + m_pDemuxer->FillBuffer(fillBuffer); + } + // if the queues are full, no need to read more if ((!m_VideoPlayerAudio->AcceptsData() && m_CurrentAudio.id >= 0) || (!m_VideoPlayerVideo->AcceptsData() && m_CurrentVideo.id >= 0)) @@ -1472,11 +1490,6 @@ void CVideoPlayer::Process() m_demuxerSpeed = m_playSpeed; } - // always yield to players if they have data levels > 50 percent - if((m_VideoPlayerAudio->GetLevel() > 50 || m_CurrentAudio.id < 0) && - (m_processInfo->GetLevelVQ() > 50 || m_CurrentVideo.id < 0)) - Sleep(0); - DemuxPacket* pPacket = NULL; CDemuxStream *pStream = NULL; ReadPacket(pPacket, pStream); @@ -3983,6 +3996,8 @@ void CVideoPlayer::FlushBuffers(double pts, bool accurate, bool sync) m_clock.Discontinuity(pts); UpdatePlayState(0); + m_demuxerSpeed = DVD_PLAYSPEED_NORMAL; + if (m_omxplayer_mode) { m_OmxPlayerState.av_clock.OMXFlush(); diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DRMPRIMEEGL.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DRMPRIMEEGL.cpp index 91809f8ebe..17dd7e1e19 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DRMPRIMEEGL.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DRMPRIMEEGL.cpp @@ -15,11 +15,14 @@ void CDRMPRIMETexture::Init(EGLDisplay eglDisplay) m_eglImage.reset(new CEGLImage(eglDisplay)); } -bool CDRMPRIMETexture::Map(CVideoBufferDRMPRIME *buffer) +bool CDRMPRIMETexture::Map(IVideoBufferDRMPRIME* buffer) { if (m_primebuffer) return true; + if (!buffer->Map()) + return false; + m_texWidth = buffer->GetWidth(); m_texHeight = buffer->GetHeight(); @@ -74,6 +77,8 @@ void CDRMPRIMETexture::Unmap() glDeleteTextures(1, &m_texture); + m_primebuffer->Unmap(); + m_primebuffer->Release(); m_primebuffer = nullptr; } diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DRMPRIMEEGL.h b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DRMPRIMEEGL.h index abc4890acb..55cb2be330 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DRMPRIMEEGL.h +++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DRMPRIMEEGL.h @@ -8,15 +8,16 @@ #pragma once -#include "cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h" +#include "cores/VideoPlayer/Process/gbm/VideoBufferDRMPRIME.h" #include "utils/EGLImage.h" +#include "utils/Geometry.h" #include "system_gl.h" class CDRMPRIMETexture { public: - bool Map(CVideoBufferDRMPRIME *buffer); + bool Map(IVideoBufferDRMPRIME* buffer); void Unmap(); void Init(EGLDisplay eglDisplay); @@ -24,7 +25,7 @@ public: CSizeInt GetTextureSize() { return { m_texWidth, m_texHeight }; } protected: - CVideoBufferDRMPRIME *m_primebuffer{nullptr}; + IVideoBufferDRMPRIME* m_primebuffer{nullptr}; std::unique_ptr<CEGLImage> m_eglImage; const GLenum m_textureTarget{GL_TEXTURE_EXTERNAL_OES}; diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIME.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIME.cpp index 295b269c82..df0fb6fd9c 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIME.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIME.cpp @@ -8,20 +8,21 @@ #include "RendererDRMPRIME.h" -#include "cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h" +#include "ServiceBroker.h" +#include "cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h" +#include "cores/VideoPlayer/Process/gbm/VideoBufferDRMPRIME.h" #include "cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.h" #include "cores/VideoPlayer/VideoRenderers/RenderCapture.h" #include "cores/VideoPlayer/VideoRenderers/RenderFactory.h" #include "cores/VideoPlayer/VideoRenderers/RenderFlags.h" -#include "settings/lib/Setting.h" #include "settings/DisplaySettings.h" #include "settings/Settings.h" #include "settings/SettingsComponent.h" +#include "settings/lib/Setting.h" #include "utils/log.h" +#include "windowing/GraphicContext.h" #include "windowing/gbm/DRMAtomic.h" #include "windowing/gbm/WinSystemGbm.h" -#include "windowing/GraphicContext.h" -#include "ServiceBroker.h" using namespace KODI::WINDOWING::GBM; @@ -34,7 +35,7 @@ CRendererDRMPRIME::~CRendererDRMPRIME() CBaseRenderer* CRendererDRMPRIME::Create(CVideoBuffer* buffer) { - if (buffer && dynamic_cast<CVideoBufferDRMPRIME*>(buffer) && + if (buffer && dynamic_cast<IVideoBufferDRMPRIME*>(buffer) && CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(SETTING_VIDEOPLAYER_USEPRIMERENDERER) == 0) { CWinSystemGbm* winSystem = dynamic_cast<CWinSystemGbm*>(CServiceBroker::GetWinSystem()); @@ -132,7 +133,7 @@ bool CRendererDRMPRIME::NeedBuffer(int index) if (m_iLastRenderBuffer == index) return true; - CVideoBufferDRMPRIME* buffer = dynamic_cast<CVideoBufferDRMPRIME*>(m_buffers[index].videoBuffer); + IVideoBufferDRMPRIME* buffer = dynamic_cast<IVideoBufferDRMPRIME*>(m_buffers[index].videoBuffer); if (buffer && buffer->m_fb_id) return true; @@ -162,12 +163,8 @@ void CRendererDRMPRIME::RenderUpdate(int index, int index2, bool clear, unsigned return; } - CVideoBufferDRMPRIME* buffer = dynamic_cast<CVideoBufferDRMPRIME*>(m_buffers[index].videoBuffer); - if (!buffer) - return; - - AVDRMFrameDescriptor* descriptor = buffer->GetDescriptor(); - if (!descriptor || !descriptor->nb_layers) + IVideoBufferDRMPRIME* buffer = dynamic_cast<IVideoBufferDRMPRIME*>(m_buffers[index].videoBuffer); + if (!buffer || !buffer->IsValid()) return; if (!m_videoLayerBridge) diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIMEGLES.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIMEGLES.cpp index c812217d35..bbe8409cba 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIMEGLES.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIMEGLES.cpp @@ -25,7 +25,7 @@ CRendererDRMPRIMEGLES::~CRendererDRMPRIMEGLES() CBaseRenderer* CRendererDRMPRIMEGLES::Create(CVideoBuffer* buffer) { - if (buffer && dynamic_cast<CVideoBufferDRMPRIME*>(buffer)) + if (buffer && dynamic_cast<IVideoBufferDRMPRIME*>(buffer)) return new CRendererDRMPRIMEGLES(); return nullptr; @@ -99,9 +99,9 @@ bool CRendererDRMPRIMEGLES::UploadTexture(int index) { CPictureBuffer &buf = m_buffers[index]; - CVideoBufferDRMPRIME *buffer = dynamic_cast<CVideoBufferDRMPRIME*>(buf.videoBuffer); + IVideoBufferDRMPRIME* buffer = dynamic_cast<IVideoBufferDRMPRIME*>(buf.videoBuffer); - if (!buffer) + if (!buffer || !buffer->IsValid()) { CLog::Log(LOGNOTICE, "CRendererDRMPRIMEGLES::%s - no buffer", __FUNCTION__); return false; diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.cpp index 1e7acae2c1..3277d3667c 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.cpp @@ -8,7 +8,7 @@ #include "VideoLayerBridgeDRMPRIME.h" -#include "cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.h" +#include "cores/VideoPlayer/Process/gbm/VideoBufferDRMPRIME.h" #include "utils/log.h" #include "windowing/gbm/DRMUtils.h" @@ -33,7 +33,7 @@ void CVideoLayerBridgeDRMPRIME::Disable() m_DRM->AddProperty(plane, "CRTC_ID", 0); } -void CVideoLayerBridgeDRMPRIME::Acquire(CVideoBufferDRMPRIME* buffer) +void CVideoLayerBridgeDRMPRIME::Acquire(IVideoBufferDRMPRIME* buffer) { // release the buffer that is no longer presented on screen Release(m_prev_buffer); @@ -46,7 +46,7 @@ void CVideoLayerBridgeDRMPRIME::Acquire(CVideoBufferDRMPRIME* buffer) m_buffer->Acquire(); } -void CVideoLayerBridgeDRMPRIME::Release(CVideoBufferDRMPRIME* buffer) +void CVideoLayerBridgeDRMPRIME::Release(IVideoBufferDRMPRIME* buffer) { if (!buffer) return; @@ -55,11 +55,14 @@ void CVideoLayerBridgeDRMPRIME::Release(CVideoBufferDRMPRIME* buffer) buffer->Release(); } -bool CVideoLayerBridgeDRMPRIME::Map(CVideoBufferDRMPRIME* buffer) +bool CVideoLayerBridgeDRMPRIME::Map(IVideoBufferDRMPRIME* buffer) { if (buffer->m_fb_id) return true; + if (!buffer->Map()) + return false; + AVDRMFrameDescriptor* descriptor = buffer->GetDescriptor(); uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}, flags = 0; uint64_t modifier[4] = {0}; @@ -108,7 +111,7 @@ bool CVideoLayerBridgeDRMPRIME::Map(CVideoBufferDRMPRIME* buffer) return true; } -void CVideoLayerBridgeDRMPRIME::Unmap(CVideoBufferDRMPRIME* buffer) +void CVideoLayerBridgeDRMPRIME::Unmap(IVideoBufferDRMPRIME* buffer) { if (buffer->m_fb_id) { @@ -125,9 +128,11 @@ void CVideoLayerBridgeDRMPRIME::Unmap(CVideoBufferDRMPRIME* buffer) buffer->m_handles[i] = 0; } } + + buffer->Unmap(); } -void CVideoLayerBridgeDRMPRIME::Configure(CVideoBufferDRMPRIME* buffer) +void CVideoLayerBridgeDRMPRIME::Configure(IVideoBufferDRMPRIME* buffer) { struct plane* plane = m_DRM->GetVideoPlane(); if (m_DRM->SupportsProperty(plane, "COLOR_ENCODING") && @@ -138,7 +143,7 @@ void CVideoLayerBridgeDRMPRIME::Configure(CVideoBufferDRMPRIME* buffer) } } -void CVideoLayerBridgeDRMPRIME::SetVideoPlane(CVideoBufferDRMPRIME* buffer, const CRect& destRect) +void CVideoLayerBridgeDRMPRIME::SetVideoPlane(IVideoBufferDRMPRIME* buffer, const CRect& destRect) { if (!Map(buffer)) { diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.h b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.h index b29488b1a3..ade17ac0c3 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.h +++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.h @@ -24,7 +24,7 @@ namespace GBM } } -class CVideoBufferDRMPRIME; +class IVideoBufferDRMPRIME; class CVideoLayerBridgeDRMPRIME : public KODI::WINDOWING::GBM::CVideoLayerBridge @@ -34,19 +34,19 @@ public: ~CVideoLayerBridgeDRMPRIME(); void Disable() override; - virtual void Configure(CVideoBufferDRMPRIME* buffer); - virtual void SetVideoPlane(CVideoBufferDRMPRIME* buffer, const CRect& destRect); + virtual void Configure(IVideoBufferDRMPRIME* buffer); + virtual void SetVideoPlane(IVideoBufferDRMPRIME* buffer, const CRect& destRect); virtual void UpdateVideoPlane(); protected: std::shared_ptr<KODI::WINDOWING::GBM::CDRMUtils> m_DRM; private: - void Acquire(CVideoBufferDRMPRIME* buffer); - void Release(CVideoBufferDRMPRIME* buffer); - bool Map(CVideoBufferDRMPRIME* buffer); - void Unmap(CVideoBufferDRMPRIME* buffer); + void Acquire(IVideoBufferDRMPRIME* buffer); + void Release(IVideoBufferDRMPRIME* buffer); + bool Map(IVideoBufferDRMPRIME* buffer); + void Unmap(IVideoBufferDRMPRIME* buffer); - CVideoBufferDRMPRIME* m_buffer = nullptr; - CVideoBufferDRMPRIME* m_prev_buffer = nullptr; + IVideoBufferDRMPRIME* m_buffer = nullptr; + IVideoBufferDRMPRIME* m_prev_buffer = nullptr; }; diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/VideoFilterShaderGL.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/VideoFilterShaderGL.cpp index f800997ffe..70aa734537 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/VideoFilterShaderGL.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/VideoFilterShaderGL.cpp @@ -84,9 +84,7 @@ ConvolutionFilterShader::ConvolutionFilterShader(ESCALINGMETHOD method, bool str } if (m_floattex) - defines = "#define HAS_FLOAT_TEXTURE 1\n"; - else - defines = "#define HAS_FLOAT_TEXTURE 0\n"; + defines = "#define HAS_FLOAT_TEXTURE\n"; //don't compile in stretch support when it's not needed if (stretch) diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/VideoFilterShaderGLES.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/VideoFilterShaderGLES.cpp index ce1885042f..a9aa0d6f8e 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/VideoFilterShaderGLES.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/VideoFilterShaderGLES.cpp @@ -99,12 +99,11 @@ ConvolutionFilterShader::ConvolutionFilterShader(ESCALINGMETHOD method) if (m_floattex) { m_internalformat = GL_RGBA16F_EXT; - defines = "#define HAS_FLOAT_TEXTURE 1\n"; + defines = "#define HAS_FLOAT_TEXTURE\n"; } else { m_internalformat = GL_RGBA; - defines = "#define HAS_FLOAT_TEXTURE 0\n"; } CLog::Log(LOGDEBUG, "GL: ConvolutionFilterShader: using %s defines:\n%s", shadername.c_str(), defines.c_str()); diff --git a/xbmc/cores/paplayer/VideoPlayerCodec.cpp b/xbmc/cores/paplayer/VideoPlayerCodec.cpp index 4edadf0454..ae0fd31558 100644 --- a/xbmc/cores/paplayer/VideoPlayerCodec.cpp +++ b/xbmc/cores/paplayer/VideoPlayerCodec.cpp @@ -213,10 +213,10 @@ bool VideoPlayerCodec::Init(const CFileItem &file, unsigned int filecache) m_bCanSeek = false; if (m_pInputStream->Seek(0, SEEK_POSSIBLE)) { - if (Seek(1)) + if (m_pDemuxer->SeekTime(1, true)) { // rewind stream to beginning - Seek(0); + m_pDemuxer->SeekTime(0, true); m_bCanSeek = true; } else @@ -510,7 +510,7 @@ CAEStreamInfo::DataType VideoPlayerCodec::GetPassthroughStreamType(AVCodecID cod break; case AV_CODEC_ID_DTS: - format.m_streamInfo.m_type = CAEStreamInfo::STREAM_TYPE_DTSHD_CORE; + format.m_streamInfo.m_type = CAEStreamInfo::STREAM_TYPE_DTSHD; format.m_streamInfo.m_sampleRate = samplerate; break; @@ -520,6 +520,12 @@ CAEStreamInfo::DataType VideoPlayerCodec::GetPassthroughStreamType(AVCodecID cod bool supports = CServiceBroker::GetActiveAE()->SupportsRaw(format); + if (!supports && codecId == AV_CODEC_ID_DTS) + { + format.m_streamInfo.m_type = CAEStreamInfo::STREAM_TYPE_DTSHD_CORE; + supports = CServiceBroker::GetActiveAE()->SupportsRaw(format); + } + if (supports) return format.m_streamInfo.m_type; else diff --git a/xbmc/dialogs/GUIDialogKeyboardTouch.cpp b/xbmc/dialogs/GUIDialogKeyboardTouch.cpp index 6451ff8b88..a79caed044 100644 --- a/xbmc/dialogs/GUIDialogKeyboardTouch.cpp +++ b/xbmc/dialogs/GUIDialogKeyboardTouch.cpp @@ -8,7 +8,7 @@ #include "GUIDialogKeyboardTouch.h" #if defined(TARGET_DARWIN_IOS) -#include "platform/darwin/ios/IOSKeyboard.h" +#include "platform/darwin/ios-common/IOSKeyboard.h" #endif CGUIDialogKeyboardTouch::CGUIDialogKeyboardTouch() diff --git a/xbmc/guilib/guiinfo/GUIInfoLabels.h b/xbmc/guilib/guiinfo/GUIInfoLabels.h index b6df15b3cc..d5c906b89c 100644 --- a/xbmc/guilib/guiinfo/GUIInfoLabels.h +++ b/xbmc/guilib/guiinfo/GUIInfoLabels.h @@ -870,6 +870,8 @@ #define LISTITEM_PROPERTY (LISTITEM_START + 182) #define LISTITEM_EPG_EVENT_ICON (LISTITEM_START + 183) #define LISTITEM_HASREMINDERRULE (LISTITEM_START + 184) +#define LISTITEM_HASARCHIVE (LISTITEM_START + 185) +#define LISTITEM_ISPLAYABLE (LISTITEM_START + 186) #define LISTITEM_END (LISTITEM_START + 2500) diff --git a/xbmc/interfaces/json-rpc/schema/types.json b/xbmc/interfaces/json-rpc/schema/types.json index 6bed040301..17282f61c8 100644 --- a/xbmc/interfaces/json-rpc/schema/types.json +++ b/xbmc/interfaces/json-rpc/schema/types.json @@ -977,7 +977,7 @@ "firstaired", "hastimer", "isactive", "parentalrating", "wasactive", "thumbnail", "rating", "originaltitle", "cast", "director", "writer", "year", "imdbnumber", "hastimerrule", - "hasrecording", "recording", "isseries" ] + "hasrecording", "recording", "isseries", "isplayable" ] } }, "PVR.Details.Broadcast": { @@ -1012,7 +1012,8 @@ "hastimerrule": { "type": "boolean" }, "hasrecording": { "type": "boolean" }, "recording": { "type": "string" }, - "isseries": { "type": "boolean" } + "isseries": { "type": "boolean" }, + "isplayable": { "type": "boolean" } } }, "PVR.Fields.Channel": { @@ -1020,7 +1021,7 @@ "items": { "type": "string", "enum": [ "thumbnail", "channeltype", "hidden", "locked", "channel", "lastplayed", "broadcastnow", "broadcastnext", "uniqueid", "icon", "channelnumber", - "subchannelnumber", "isrecording" ] + "subchannelnumber", "isrecording", "hasarchive" ] } }, "PVR.Details.Channel": { @@ -1039,7 +1040,8 @@ "icon": { "type": "string" }, "channelnumber": { "type": "integer" }, "subchannelnumber": { "type": "integer" }, - "isrecording": { "type": "boolean" } + "isrecording": { "type": "boolean" }, + "hasarchive": { "type": "boolean" } } }, "PVR.Details.ChannelGroup": { diff --git a/xbmc/interfaces/json-rpc/schema/version.txt b/xbmc/interfaces/json-rpc/schema/version.txt index 4f3c1d9eec..a028c7d043 100644 --- a/xbmc/interfaces/json-rpc/schema/version.txt +++ b/xbmc/interfaces/json-rpc/schema/version.txt @@ -1 +1 @@ -JSONRPC_VERSION 10.4.0 +JSONRPC_VERSION 10.5.0 diff --git a/xbmc/interfaces/legacy/File.h b/xbmc/interfaces/legacy/File.h index 9eb18a05c2..cc3efc0fc7 100644 --- a/xbmc/interfaces/legacy/File.h +++ b/xbmc/interfaces/legacy/File.h @@ -111,7 +111,7 @@ namespace XBMCAddon /// ~~~~~~~~~~~~~{.py} /// .. /// f = xbmcvfs.File(file) - /// b = f.read() + /// b = f.readBytes() /// f.close() /// .. /// ~~~~~~~~~~~~~ @@ -182,11 +182,12 @@ namespace XBMCAddon /// Seek to position in file. /// /// @param seekBytes position in the file - /// @param iWhence where in a file to seek from[0 beginning, + /// @param iWhence [opt] where in a file to seek from[0 beginning, /// 1 current , 2 end position] /// /// ///----------------------------------------------------------------------- + /// @python_v19 Function changed. param **iWhence** is now optional. /// /// **Example:** /// ~~~~~~~~~~~~~{.py} @@ -199,7 +200,34 @@ namespace XBMCAddon /// seek(...); #else - inline long long seek(long long seekBytes, int iWhence) { DelayedCallGuard dg(languageHook); return file->Seek(seekBytes,iWhence); } + inline long long seek(long long seekBytes, int iWhence = SEEK_SET) { DelayedCallGuard dg(languageHook); return file->Seek(seekBytes,iWhence); } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_file + /// @brief \python_func{ tell() } + ///----------------------------------------------------------------------- + /// Get the current position in the file. + /// + /// @return The file position + /// + /// + ///----------------------------------------------------------------------- + /// @python_v19 New function added + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// f = xbmcvfs.File(file) + /// s = f.tell() + /// f.close() + /// .. + /// ~~~~~~~~~~~~~ + /// + tell(); +#else + inline long long tell() { DelayedCallGuard dg(languageHook); return file->GetPosition(); } #endif #ifdef DOXYGEN_SHOULD_USE_THIS diff --git a/xbmc/music/dialogs/GUIDialogMusicInfo.cpp b/xbmc/music/dialogs/GUIDialogMusicInfo.cpp index 72f52fe658..dde8506d0f 100644 --- a/xbmc/music/dialogs/GUIDialogMusicInfo.cpp +++ b/xbmc/music/dialogs/GUIDialogMusicInfo.cpp @@ -755,22 +755,6 @@ void CGUIDialogMusicInfo::OnGetArt() item->SetLabel(g_localizeStrings.Get(13512)); items.Add(item); } - else if (m_item->HasArt("thumb")) - { - // For missing art of that type add the thumb (when it exists and not a fallback) - CGUIListItem::ArtMap::const_iterator i = primeArt.find("thumb"); - if (i != primeArt.end()) - { - CFileItemPtr item(new CFileItem("thumb://Thumb", false)); - item->SetArt("thumb", m_item->GetArt("thumb")); - if (m_bArtistInfo) - item->SetIconImage("DefaultArtistCover.png"); - else - item->SetIconImage("DefaultAlbumCover.png"); - item->SetLabel(g_localizeStrings.Get(21371)); - items.Add(item); - } - } // Grab the thumbnails of this art type scraped from the web std::vector<std::string> remotethumbs; diff --git a/xbmc/platform/darwin/ios-common/CMakeLists.txt b/xbmc/platform/darwin/ios-common/CMakeLists.txt index 8b78aabc9a..f9e35bed32 100644 --- a/xbmc/platform/darwin/ios-common/CMakeLists.txt +++ b/xbmc/platform/darwin/ios-common/CMakeLists.txt @@ -1,5 +1,9 @@ -set(SOURCES AnnounceReceiver.mm) +set(SOURCES AnnounceReceiver.mm + IOSKeyboard.mm + IOSKeyboardView.mm) -set(HEADERS AnnounceReceiver.h) +set(HEADERS AnnounceReceiver.h + IOSKeyboard.h + IOSKeyboardView.h) core_add_library(platform_ios-common) diff --git a/xbmc/platform/darwin/ios/IOSKeyboard.h b/xbmc/platform/darwin/ios-common/IOSKeyboard.h index 798b39845e..75b17e8e0e 100644 --- a/xbmc/platform/darwin/ios/IOSKeyboard.h +++ b/xbmc/platform/darwin/ios-common/IOSKeyboard.h @@ -13,11 +13,11 @@ class CIOSKeyboard : public CGUIKeyboard { public: - CIOSKeyboard():m_pCharCallback(NULL),m_bCanceled(false){} + CIOSKeyboard():m_pCharCallback(nullptr),m_bCanceled(false){} virtual bool ShowAndGetInput(char_callback_t pCallback, const std::string &initialString, std::string &typedString, const std::string &heading, bool bHiddenInput); virtual void Cancel(); void fireCallback(const std::string &str); - void invalidateCallback() {m_pCharCallback = NULL;} + void invalidateCallback() {m_pCharCallback = nullptr;} virtual bool SetTextToKeyboard(const std::string &text, bool closeKeyboard = false); private: diff --git a/xbmc/platform/darwin/ios/IOSKeyboard.mm b/xbmc/platform/darwin/ios-common/IOSKeyboard.mm index daf5c97318..692ab74b9e 100644 --- a/xbmc/platform/darwin/ios/IOSKeyboard.mm +++ b/xbmc/platform/darwin/ios-common/IOSKeyboard.mm @@ -6,11 +6,11 @@ * See LICENSES/README.md for more information. */ -#include "XBMCController.h" -#include "IOSKeyboard.h" -#include "IOSKeyboardView.h" -#include "XBMCDebugHelpers.h" #include "platform/darwin/DarwinUtils.h" +#include "platform/darwin/NSLogDebugHelpers.h" +#include "platform/darwin/ios/XBMCController.h" +#include "platform/darwin/ios-common/IOSKeyboard.h" +#include "platform/darwin/ios-common/IOSKeyboardView.h" #import "platform/darwin/AutoPool.h" diff --git a/xbmc/platform/darwin/ios/IOSKeyboardView.h b/xbmc/platform/darwin/ios-common/IOSKeyboardView.h index 8c2dd2eec5..5ec2971f5f 100644 --- a/xbmc/platform/darwin/ios/IOSKeyboardView.h +++ b/xbmc/platform/darwin/ios-common/IOSKeyboardView.h @@ -7,7 +7,7 @@ */ #import <UIKit/UIKit.h> -#include "IOSKeyboard.h" +#include "platform/darwin/ios-common/IOSKeyboard.h" @interface KeyboardView : UIView <UITextFieldDelegate> { diff --git a/xbmc/platform/darwin/ios/IOSKeyboardView.mm b/xbmc/platform/darwin/ios-common/IOSKeyboardView.mm index 40adc404b9..86f18ecfc2 100644 --- a/xbmc/platform/darwin/ios/IOSKeyboardView.mm +++ b/xbmc/platform/darwin/ios-common/IOSKeyboardView.mm @@ -10,11 +10,11 @@ #include "threads/Event.h" #include "Application.h" -#import "IOSKeyboardView.h" -#import "IOSScreenManager.h" -#import "XBMCController.h" -#import "XBMCDebugHelpers.h" -#include "IOSKeyboard.h" +#import "platform/darwin/NSLogDebugHelpers.h" +#import "platform/darwin/ios/IOSScreenManager.h" +#import "platform/darwin/ios/XBMCController.h" +#import "platform/darwin/ios-common/IOSKeyboard.h" +#import "platform/darwin/ios-common/IOSKeyboardView.h" static CEvent keyboardFinishedEvent; diff --git a/xbmc/platform/darwin/ios/CMakeLists.txt b/xbmc/platform/darwin/ios/CMakeLists.txt index 368345d23f..188c3e7ca6 100644 --- a/xbmc/platform/darwin/ios/CMakeLists.txt +++ b/xbmc/platform/darwin/ios/CMakeLists.txt @@ -1,17 +1,12 @@ set(SOURCES IOSEAGLView.mm IOSExternalTouchController.mm - IOSKeyboard.mm - IOSKeyboardView.mm IOSScreenManager.mm XBMCController.mm) set(HEADERS IOSEAGLView.h IOSExternalTouchController.h - IOSKeyboard.h - IOSKeyboardView.h IOSScreenManager.h XBMCApplication.h - XBMCController.h - XBMCDebugHelpers.h) + XBMCController.h) core_add_library(platform_ios) diff --git a/xbmc/platform/darwin/ios/IOSEAGLView.mm b/xbmc/platform/darwin/ios/IOSEAGLView.mm index c764524ba2..df9b63c3c2 100644 --- a/xbmc/platform/darwin/ios/IOSEAGLView.mm +++ b/xbmc/platform/darwin/ios/IOSEAGLView.mm @@ -31,7 +31,7 @@ #import "platform/darwin/AutoPool.h" #import "platform/darwin/DarwinUtils.h" #import "platform/darwin/ios-common/AnnounceReceiver.h" -#import "XBMCDebugHelpers.h" +#import "platform/darwin/NSLogDebugHelpers.h" using namespace KODI::MESSAGING; diff --git a/xbmc/platform/darwin/ios/XBMCApplication.mm b/xbmc/platform/darwin/ios/XBMCApplication.mm index a44831d1ce..8b44e2a4a1 100644 --- a/xbmc/platform/darwin/ios/XBMCApplication.mm +++ b/xbmc/platform/darwin/ios/XBMCApplication.mm @@ -12,7 +12,7 @@ #import "XBMCApplication.h" #import "XBMCController.h" #import "IOSScreenManager.h" -#import "XBMCDebugHelpers.h" +#import "platform/darwin/NSLogDebugHelpers.h" #import <objc/runtime.h> @implementation XBMCApplicationDelegate diff --git a/xbmc/platform/darwin/ios/XBMCController.mm b/xbmc/platform/darwin/ios/XBMCController.mm index b84b18fd25..5c14627b7f 100644 --- a/xbmc/platform/darwin/ios/XBMCController.mm +++ b/xbmc/platform/darwin/ios/XBMCController.mm @@ -48,7 +48,7 @@ using namespace KODI::MESSAGING; #import "XBMCController.h" #import "IOSScreenManager.h" #import "XBMCApplication.h" -#import "XBMCDebugHelpers.h" +#import "platform/darwin/NSLogDebugHelpers.h" #import "platform/darwin/AutoPool.h" XBMCController *g_xbmcController; diff --git a/xbmc/platform/darwin/ios/XBMCDebugHelpers.h b/xbmc/platform/darwin/ios/XBMCDebugHelpers.h deleted file mode 100644 index 3997701c34..0000000000 --- a/xbmc/platform/darwin/ios/XBMCDebugHelpers.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * XBMCDebugHelpers.h - * xbmclauncher - * - * Created by Stephan Diederich on 21.09.08. - * Copyright 2008 University Heidelberg. All rights reserved. - * - * Copyright (C) 2008-2018 Team Kodi - * This file is part of Kodi - https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSES/README.md for more information. - */ - -#define DEBUG -#ifdef DEBUG -#define LOG(s, ...) NSLog(@"[DEBUG] " s, ##__VA_ARGS__) -#define ILOG(s, ...) NSLog(@"[INFO] " s, ##__VA_ARGS__) -#define ELOG(s, ...) NSLog(@"[ERROR] " s, ##__VA_ARGS__) -#define DLOG(s, ...) LOG(s, ##__VA_ARGS__) -#else -#define LOG(s, ...) -#define ILOG(s, ...) NSLog(@"[INFO] " s, ##__VA_ARGS__) -#define ELOG(s, ...) NSLog(@"[ERROR] " s, ##__VA_ARGS__) -#define DLOG(s, ...) LOG(s, ##__VA_ARGS__) -#endif - -#define PRINT_SIGNATURE() LOG(@"%s", __PRETTY_FUNCTION__) diff --git a/xbmc/platform/linux/input/LibInputKeyboard.cpp b/xbmc/platform/linux/input/LibInputKeyboard.cpp index 270f574675..9bbc9de434 100644 --- a/xbmc/platform/linux/input/LibInputKeyboard.cpp +++ b/xbmc/platform/linux/input/LibInputKeyboard.cpp @@ -137,7 +137,7 @@ static const std::map<xkb_keysym_t, XBMCKey> xkbMap = // Media keys { XKB_KEY_XF86Eject, XBMCK_EJECT }, - // XBMCK_STOP clashes with XBMCK_MEDIA_STOP + { XKB_KEY_Cancel, XBMCK_STOP }, { XKB_KEY_XF86AudioRecord, XBMCK_RECORD }, // XBMCK_REWIND clashes with XBMCK_MEDIA_REWIND { XKB_KEY_XF86Phone, XBMCK_PHONE }, diff --git a/xbmc/pvr/PVRDatabase.cpp b/xbmc/pvr/PVRDatabase.cpp index ee11feda1b..8e85d5cd8c 100644 --- a/xbmc/pvr/PVRDatabase.cpp +++ b/xbmc/pvr/PVRDatabase.cpp @@ -93,7 +93,8 @@ void CPVRDatabase::CreateTables() "sEPGScraper varchar(32), " "iLastWatched integer, " "iClientId integer, " //! @todo use mapping table - "idEpg integer" + "idEpg integer, " + "bHasArchive bool" ")" ); @@ -191,6 +192,9 @@ void CPVRDatabase::UpdateTables(int iVersion) if (iVersion < 33) m_pDS->exec(sqlCreateTimersTable); + + if (iVersion < 34) + m_pDS->exec("ALTER TABLE channels ADD bHasArchive bool"); } /********** Client methods **********/ @@ -279,11 +283,11 @@ bool CPVRDatabase::Delete(const CPVRChannel &channel) int CPVRDatabase::Get(CPVRChannelGroup &results, bool bCompressDB) { int iReturn(0); - bool bIgnoreEpgDB = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_EPG_IGNOREDBFORCLIENT); + bool bUseEpgDB = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_EPG_STOREEPGINDATABASE); std::string strQuery = PrepareSQL("SELECT channels.idChannel, channels.iUniqueId, channels.bIsRadio, channels.bIsHidden, channels.bIsUserSetIcon, channels.bIsUserSetName, " "channels.sIconPath, channels.sChannelName, channels.bIsVirtual, channels.bEPGEnabled, channels.sEPGScraper, channels.iLastWatched, channels.iClientId, channels.bIsLocked, " - "map_channelgroups_channels.iChannelNumber, map_channelgroups_channels.iSubChannelNumber, channels.idEpg " + "map_channelgroups_channels.iChannelNumber, map_channelgroups_channels.iSubChannelNumber, channels.idEpg, channels.bHasArchive " "FROM map_channelgroups_channels " "LEFT JOIN channels ON channels.idChannel = map_channelgroups_channels.idChannel " "WHERE map_channelgroups_channels.idGroup = %u", results.GroupID()); @@ -310,7 +314,8 @@ int CPVRDatabase::Get(CPVRChannelGroup &results, bool bCompressDB) channel->m_strEPGScraper = m_pDS->fv("sEPGScraper").get_asString(); channel->m_iLastWatched = static_cast<time_t>(m_pDS->fv("iLastWatched").get_asInt()); channel->m_iClientId = m_pDS->fv("iClientId").get_asInt(); - channel->m_iEpgId = bIgnoreEpgDB ? -1 : m_pDS->fv("idEpg").get_asInt(); + channel->m_iEpgId = bUseEpgDB ? m_pDS->fv("idEpg").get_asInt() : -1; + channel->m_bHasArchive = m_pDS->fv("bHasArchive").get_asBool(); channel->UpdateEncryptionName(); PVRChannelGroupMember newMember(channel, @@ -798,11 +803,11 @@ bool CPVRDatabase::Persist(CPVRChannel &channel, bool bCommit) strQuery = PrepareSQL("INSERT INTO channels (" "iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsUserSetName, bIsLocked, " "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, " - "idEpg) " - "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i)", + "idEpg, bHasArchive) " + "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, %i)", channel.UniqueID(), (channel.IsRadio() ? 1 :0), (channel.IsHidden() ? 1 : 0), (channel.IsUserSetIcon() ? 1 : 0), (channel.IsUserSetName() ? 1 : 0), (channel.IsLocked() ? 1 : 0), channel.IconPath().c_str(), channel.ChannelName().c_str(), 0, (channel.EPGEnabled() ? 1 : 0), channel.EPGScraper().c_str(), static_cast<unsigned int>(channel.LastWatched()), channel.ClientID(), - channel.EpgID()); + channel.EpgID(), channel.HasArchive()); } else { @@ -810,12 +815,12 @@ bool CPVRDatabase::Persist(CPVRChannel &channel, bool bCommit) strQuery = PrepareSQL("REPLACE INTO channels (" "iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsUserSetName, bIsLocked, " "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, " - "idChannel, idEpg) " - "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %s, %i)", + "idChannel, idEpg, bHasArchive) " + "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %s, %i, %i)", channel.UniqueID(), (channel.IsRadio() ? 1 :0), (channel.IsHidden() ? 1 : 0), (channel.IsUserSetIcon() ? 1 : 0), (channel.IsUserSetName() ? 1 : 0), (channel.IsLocked() ? 1 : 0), channel.IconPath().c_str(), channel.ChannelName().c_str(), 0, (channel.EPGEnabled() ? 1 : 0), channel.EPGScraper().c_str(), static_cast<unsigned int>(channel.LastWatched()), channel.ClientID(), strValue.c_str(), - channel.EpgID()); + channel.EpgID(), channel.HasArchive()); } if (QueueInsertQuery(strQuery)) diff --git a/xbmc/pvr/PVRDatabase.h b/xbmc/pvr/PVRDatabase.h index efae17af19..a801ea6d2c 100644 --- a/xbmc/pvr/PVRDatabase.h +++ b/xbmc/pvr/PVRDatabase.h @@ -51,7 +51,7 @@ namespace PVR * @brief Get the minimal database version that is required to operate correctly. * @return The minimal database version. */ - int GetSchemaVersion() const override { return 33; } + int GetSchemaVersion() const override { return 34; } /*! * @brief Get the default sqlite database filename. diff --git a/xbmc/pvr/PVRGUIInfo.cpp b/xbmc/pvr/PVRGUIInfo.cpp index b6ae88c218..9be9d36067 100644 --- a/xbmc/pvr/PVRGUIInfo.cpp +++ b/xbmc/pvr/PVRGUIInfo.cpp @@ -1107,6 +1107,20 @@ bool CPVRGUIInfo::GetListItemAndPlayerBool(const CFileItem *item, const CGUIInfo { switch (info.m_info) { + case LISTITEM_HASARCHIVE: + if (item->IsPVRChannel()) + { + bValue = item->GetPVRChannelInfoTag()->HasArchive(); + return true; + } + break; + case LISTITEM_ISPLAYABLE: + if (item->IsEPG()) + { + bValue = item->GetEPGInfoTag()->IsPlayable(); + return true; + } + break; case LISTITEM_ISRECORDING: if (item->IsPVRChannel()) { diff --git a/xbmc/pvr/channels/PVRChannel.cpp b/xbmc/pvr/channels/PVRChannel.cpp index b33ac83e63..385a0a0cf1 100644 --- a/xbmc/pvr/channels/PVRChannel.cpp +++ b/xbmc/pvr/channels/PVRChannel.cpp @@ -46,6 +46,7 @@ CPVRChannel::CPVRChannel(bool bRadio /* = false */) m_bIsLocked = false; m_iLastWatched = 0; m_bChanged = false; + m_bHasArchive = false; m_iEpgId = -1; m_bEPGEnabled = true; @@ -78,6 +79,7 @@ CPVRChannel::CPVRChannel(const PVR_CHANNEL &channel, unsigned int iClientId) m_strEPGScraper = "client"; m_iEpgId = -1; m_bChanged = false; + m_bHasArchive = channel.bHasArchive; if (m_strChannelName.empty()) m_strChannelName = StringUtils::Format("%s %d", g_localizeStrings.Get(19029).c_str(), m_iUniqueId); @@ -113,6 +115,7 @@ void CPVRChannel::Serialize(CVariant& value) const epg->Serialize(value["broadcastnext"]); value["isrecording"] = false; // compat + value["hasarchive"] = m_bHasArchive; } /********** XBMC related channel methods **********/ @@ -178,12 +181,14 @@ bool CPVRChannel::UpdateFromClient(const CPVRChannelPtr &channel) if (m_clientChannelNumber != channel->m_clientChannelNumber || m_strInputFormat != channel->InputFormat() || m_iClientEncryptionSystem != channel->EncryptionSystem() || - m_strClientChannelName != channel->ClientChannelName()) + m_strClientChannelName != channel->ClientChannelName() || + m_bHasArchive != channel->HasArchive()) { m_clientChannelNumber = channel->m_clientChannelNumber; m_strInputFormat = channel->InputFormat(); m_iClientEncryptionSystem = channel->EncryptionSystem(); m_strClientChannelName = channel->ClientChannelName(); + m_bHasArchive = channel->HasArchive(); UpdateEncryptionName(); SetChanged(); @@ -301,6 +306,12 @@ void CPVRChannel::SetRadioRDSInfoTag(const std::shared_ptr<CPVRRadioRDSInfoTag>& m_rdsTag = tag; } +bool CPVRChannel::HasArchive(void) const +{ + CSingleLock lock(m_critSection); + return m_bHasArchive; +} + bool CPVRChannel::SetIconPath(const std::string &strIconPath, bool bIsUserSetIcon /* = false */) { CSingleLock lock(m_critSection); diff --git a/xbmc/pvr/channels/PVRChannel.h b/xbmc/pvr/channels/PVRChannel.h index cf040c3e58..bd74a4e5ba 100644 --- a/xbmc/pvr/channels/PVRChannel.h +++ b/xbmc/pvr/channels/PVRChannel.h @@ -151,6 +151,11 @@ namespace PVR void SetRadioRDSInfoTag(const std::shared_ptr<CPVRRadioRDSInfoTag>& tag); /*! + * @return True if this channel has archive support, false otherwise + */ + bool HasArchive(void) const; + + /*! * @return The path to the icon for this channel. */ std::string IconPath(void) const; @@ -440,6 +445,7 @@ namespace PVR bool m_bChanged; /*!< true if anything in this entry was changed that needs to be persisted */ CPVRChannelNumber m_channelNumber; /*!< the number this channel has in the currently selected channel group */ std::shared_ptr<CPVRRadioRDSInfoTag> m_rdsTag; /*! < the radio rds data, if available for the channel. */ + bool m_bHasArchive; /*!< true if this channel supports archive */ //@} /*! @name EPG related channel data diff --git a/xbmc/pvr/channels/PVRChannelGroup.cpp b/xbmc/pvr/channels/PVRChannelGroup.cpp index ac140fcae2..a3ebd15eb9 100644 --- a/xbmc/pvr/channels/PVRChannelGroup.cpp +++ b/xbmc/pvr/channels/PVRChannelGroup.cpp @@ -926,7 +926,7 @@ std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVRChannelGroup::GetEPGAll(bool bI channel = (*it).channel; if (!channel->IsHidden()) { - bool bEmpty = false; + bool bEmpty = true; CPVREpgPtr epg = channel->GetEPG(); if (epg) diff --git a/xbmc/pvr/channels/PVRChannelGroupInternal.cpp b/xbmc/pvr/channels/PVRChannelGroupInternal.cpp index 015fa291ba..4941292f67 100644 --- a/xbmc/pvr/channels/PVRChannelGroupInternal.cpp +++ b/xbmc/pvr/channels/PVRChannelGroupInternal.cpp @@ -244,15 +244,19 @@ bool CPVRChannelGroupInternal::AddAndUpdateChannels(const CPVRChannelGroup &chan if (existingChannel.channel->UpdateFromClient(it->second.channel)) { bReturn = true; - CLog::LogFC(LOGDEBUG, LOGPVR, "Updated %s channel '%s' from PVR client", m_bRadio ? "radio" : "TV", it->second.channel->ChannelName().c_str()); + CLog::LogFC(LOGDEBUG, LOGPVR, "Updated {} channel '{}' from PVR client", m_bRadio ? "radio" : "TV", it->second.channel->ChannelName()); } } else { /* new channel */ UpdateFromClient(it->second.channel, bUseBackendChannelNumbers ? it->second.channel->ClientChannelNumber() : CPVRChannelNumber()); + if (it->second.channel->CreateEPG()) + { + CLog::LogFC(LOGDEBUG, LOGPVR, "Created EPG for {} channel '{}' from PVR client", m_bRadio ? "radio" : "TV", it->second.channel->ChannelName()); + } bReturn = true; - CLog::LogFC(LOGDEBUG, LOGPVR,"Added %s channel '%s' from PVR client", m_bRadio ? "radio" : "TV", it->second.channel->ChannelName().c_str()); + CLog::LogFC(LOGDEBUG, LOGPVR, "Added {} channel '{}' from PVR client", m_bRadio ? "radio" : "TV", it->second.channel->ChannelName()); } } diff --git a/xbmc/pvr/epg/Epg.cpp b/xbmc/pvr/epg/Epg.cpp index 42ec788c20..a72065be89 100644 --- a/xbmc/pvr/epg/Epg.cpp +++ b/xbmc/pvr/epg/Epg.cpp @@ -241,7 +241,7 @@ CPVREpgInfoTagPtr CPVREpg::GetTagBetween(const CDateTime &beginTime, const CDate if (tag) { m_tags.insert(std::make_pair(tag->StartAsUTC(), tag)); - UpdateEntry(tag, !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_EPG_IGNOREDBFORCLIENT)); + UpdateEntry(tag, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_EPG_STOREEPGINDATABASE)); } } @@ -258,11 +258,12 @@ void CPVREpg::AddEntry(const CPVREpgInfoTag &tag) newTag = it->second; else { - newTag = std::make_shared<CPVREpgInfoTag>(m_channelData, m_iEpgID); + newTag.reset(new CPVREpgInfoTag()); m_tags.insert(std::make_pair(tag.StartAsUTC(), newTag)); } newTag->Update(tag); + newTag->SetChannelData(m_channelData); newTag->SetEpgID(m_iEpgID); } @@ -348,13 +349,14 @@ bool CPVREpg::UpdateEntry(const CPVREpgInfoTagPtr &tag, bool bUpdateDatabase) } else { - infoTag = std::make_shared<CPVREpgInfoTag>(m_channelData, m_iEpgID); + infoTag.reset(new CPVREpgInfoTag()); infoTag->SetUniqueBroadcastID(tag->UniqueBroadcastID()); m_tags.insert(std::make_pair(tag->StartAsUTC(), infoTag)); bNewTag = true; } infoTag->Update(*tag, bNewTag); + infoTag->SetChannelData(m_channelData); infoTag->SetEpgID(m_iEpgID); if (bUpdateDatabase) @@ -627,7 +629,7 @@ bool CPVREpg::UpdateFromScraper(time_t start, time_t end, bool bForceUpdate) { CLog::LogFC(LOGDEBUG, LOGEPG, "Updating EPG for channel '%s' from client '%i'", m_channelData->ChannelName().c_str(), m_channelData->ClientId()); - return (client->GetEPGForChannel(m_channelData, this, start, end) == PVR_ERROR_NO_ERROR); + return (client->GetEPGForChannel(m_channelData->UniqueClientChannelId(), this, start, end) == PVR_ERROR_NO_ERROR); } } else @@ -698,7 +700,7 @@ bool CPVREpg::LoadFromClients(time_t start, time_t end, bool bForceUpdate) const std::shared_ptr<CPVREpg> tmpEpg = std::make_shared<CPVREpg>(m_iEpgID, m_strName, m_strScraperName, m_channelData); if (tmpEpg->UpdateFromScraper(start, end, bForceUpdate)) - bReturn = UpdateEntries(*tmpEpg, !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_EPG_IGNOREDBFORCLIENT)); + bReturn = UpdateEntries(*tmpEpg, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_EPG_STOREEPGINDATABASE)); return bReturn; } diff --git a/xbmc/pvr/epg/EpgContainer.cpp b/xbmc/pvr/epg/EpgContainer.cpp index 7abc52a013..b6cc66700b 100644 --- a/xbmc/pvr/epg/EpgContainer.cpp +++ b/xbmc/pvr/epg/EpgContainer.cpp @@ -95,7 +95,7 @@ CPVREpgContainer::CPVREpgContainer(void) : CThread("EPGUpdater"), m_database(new CPVREpgDatabase), m_settings({ - CSettings::SETTING_EPG_IGNOREDBFORCLIENT, + CSettings::SETTING_EPG_STOREEPGINDATABASE, CSettings::SETTING_EPG_EPGUPDATE, CSettings::SETTING_EPG_FUTURE_DAYSTODISPLAY, CSettings::SETTING_EPG_PAST_DAYSTODISPLAY, @@ -261,7 +261,7 @@ void CPVREpgContainer::LoadFromDB(void) { CSingleLock lock(m_critSection); - if (m_bLoaded || IgnoreDB()) + if (m_bLoaded || !UseDatabase()) return; bool bLoaded = true; @@ -298,7 +298,7 @@ void CPVREpgContainer::LoadFromDB(void) bool CPVREpgContainer::PersistAll(void) { - bool bReturn = IgnoreDB(); + bool bReturn = !UseDatabase(); if (!bReturn) { @@ -589,7 +589,7 @@ bool CPVREpgContainer::RemoveOldEntries(void) epgEntry.second->Cleanup(cleanupTime); /* remove the old entries from the database */ - if (!IgnoreDB()) + if (UseDatabase()) m_database->DeleteEpgEntries(cleanupTime); CSingleLock lock(m_critSection); @@ -615,7 +615,7 @@ bool CPVREpgContainer::DeleteEpg(const CPVREpgPtr &epg, bool bDeleteFromDatabase m_channelUidToEpgMap.erase(epgEntry1); CLog::LogFC(LOGDEBUG, LOGEPG, "Deleting EPG table %s (%d)", epg->Name().c_str(), epg->EpgID()); - if (bDeleteFromDatabase && !IgnoreDB()) + if (bDeleteFromDatabase && UseDatabase()) m_database->Delete(*epgEntry->second); epgEntry->second->UnregisterObserver(this); @@ -624,9 +624,9 @@ bool CPVREpgContainer::DeleteEpg(const CPVREpgPtr &epg, bool bDeleteFromDatabase return true; } -bool CPVREpgContainer::IgnoreDB() const +bool CPVREpgContainer::UseDatabase() const { - return m_settings.GetBoolValue(CSettings::SETTING_EPG_IGNOREDBFORCLIENT); + return m_settings.GetBoolValue(CSettings::SETTING_EPG_STOREEPGINDATABASE); } bool CPVREpgContainer::InterruptUpdate(void) const @@ -686,7 +686,7 @@ bool CPVREpgContainer::UpdateEPG(bool bOnlyPending /* = false */) /* load or update all EPG tables */ unsigned int iCounter = 0; - const std::shared_ptr<CPVREpgDatabase> database = IgnoreDB() ? nullptr : GetEpgDatabase(); + const std::shared_ptr<CPVREpgDatabase> database = UseDatabase() ? GetEpgDatabase() : nullptr; for (const auto& epgEntry : m_epgIdToEpgMap) { if (InterruptUpdate()) diff --git a/xbmc/pvr/epg/EpgContainer.h b/xbmc/pvr/epg/EpgContainer.h index 47fce4baa2..2d09c49e6c 100644 --- a/xbmc/pvr/epg/EpgContainer.h +++ b/xbmc/pvr/epg/EpgContainer.h @@ -147,9 +147,9 @@ namespace PVR /*! * @brief Check whether data should be persisted to the EPG database. - * @return True if data should not be persisted to the EPG database, false otherwise. + * @return True if data should be persisted to the EPG database, false otherwise. */ - bool IgnoreDB() const; + bool UseDatabase() const; /*! * @brief Notify EPG container that there are pending manual EPG updates diff --git a/xbmc/pvr/epg/EpgDatabase.cpp b/xbmc/pvr/epg/EpgDatabase.cpp index 8d08d45ee4..83e8fcf482 100644 --- a/xbmc/pvr/epg/EpgDatabase.cpp +++ b/xbmc/pvr/epg/EpgDatabase.cpp @@ -87,7 +87,7 @@ void CPVREpgDatabase::CreateTables(void) "iFirstAired integer, " "iParentalRating integer, " "iStarRating integer, " - "bNotify bool, " + "bNotify bool, " // Unused. Could be removed, but beware: sqlite does not support 'ALTER TABLE x DROP COLUMN y'. "iSeriesId integer, " "iEpisodeId integer, " "iEpisodePart integer, " @@ -275,7 +275,6 @@ std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::Get(const CPVREpg newTag->m_genre = newTag->Tokenize(m_pDS->fv("sGenre").get_asString()); newTag->m_iParentalRating = m_pDS->fv("iParentalRating").get_asInt(); newTag->m_iStarRating = m_pDS->fv("iStarRating").get_asInt(); - newTag->m_bNotify = m_pDS->fv("bNotify").get_asBool(); newTag->m_iEpisodeNumber = m_pDS->fv("iEpisodeId").get_asInt(); newTag->m_iEpisodePart = m_pDS->fv("iEpisodePart").get_asInt(); newTag->m_strEpisodeName = m_pDS->fv("sEpisodeName").get_asString().c_str(); @@ -390,7 +389,7 @@ int CPVREpgDatabase::Persist(const CPVREpgInfoTag &tag, bool bSingleUpdate /* = tag.OriginalTitle().c_str(), tag.DeTokenize(tag.Cast()).c_str(), tag.DeTokenize(tag.Directors()).c_str(), tag.DeTokenize(tag.Writers()).c_str(), tag.Year(), tag.IMDBNumber().c_str(), tag.Icon().c_str(), tag.GenreType(), tag.GenreSubType(), strGenre.c_str(), - static_cast<unsigned int>(iFirstAired), tag.ParentalRating(), tag.StarRating(), tag.Notify(), + static_cast<unsigned int>(iFirstAired), tag.ParentalRating(), tag.StarRating(), false /* unused */, tag.SeriesNumber(), tag.EpisodeNumber(), tag.EpisodePart(), tag.EpisodeName().c_str(), tag.Flags(), tag.SeriesLink().c_str(), tag.UniqueBroadcastID()); } @@ -406,7 +405,7 @@ int CPVREpgDatabase::Persist(const CPVREpgInfoTag &tag, bool bSingleUpdate /* = tag.OriginalTitle().c_str(), tag.DeTokenize(tag.Cast()).c_str(), tag.DeTokenize(tag.Directors()).c_str(), tag.DeTokenize(tag.Writers()).c_str(), tag.Year(), tag.IMDBNumber().c_str(), tag.Icon().c_str(), tag.GenreType(), tag.GenreSubType(), strGenre.c_str(), - static_cast<unsigned int>(iFirstAired), tag.ParentalRating(), tag.StarRating(), tag.Notify(), + static_cast<unsigned int>(iFirstAired), tag.ParentalRating(), tag.StarRating(), false /* unused */, tag.SeriesNumber(), tag.EpisodeNumber(), tag.EpisodePart(), tag.EpisodeName().c_str(), tag.Flags(), tag.SeriesLink().c_str(), tag.UniqueBroadcastID(), iBroadcastId); } diff --git a/xbmc/pvr/epg/EpgInfoTag.cpp b/xbmc/pvr/epg/EpgInfoTag.cpp index 8c37ffe3b7..fee757055e 100644 --- a/xbmc/pvr/epg/EpgInfoTag.cpp +++ b/xbmc/pvr/epg/EpgInfoTag.cpp @@ -41,8 +41,7 @@ CPVREpgInfoTag::CPVREpgInfoTag(const std::shared_ptr<CPVREpgChannelData>& channe } CPVREpgInfoTag::CPVREpgInfoTag(const EPG_TAG& data, int iClientId, const std::shared_ptr<CPVREpgChannelData>& channelData, int iEpgID) -: m_bNotify(data.bNotify), - m_iParentalRating(data.iParentalRating), +: m_iParentalRating(data.iParentalRating), m_iStarRating(data.iStarRating), m_iSeriesNumber(data.iSeriesNumber), m_iEpisodeNumber(data.iEpisodeNumber), @@ -116,8 +115,7 @@ bool CPVREpgInfoTag::operator ==(const CPVREpgInfoTag& right) const return true; CSingleLock lock(m_critSection); - return (m_bNotify == right.m_bNotify && - m_iDatabaseID == right.m_iDatabaseID && + return (m_iDatabaseID == right.m_iDatabaseID && m_iGenreType == right.m_iGenreType && m_iGenreSubType == right.m_iGenreSubType && m_iParentalRating == right.m_iParentalRating && @@ -468,11 +466,6 @@ int CPVREpgInfoTag::StarRating(void) const return m_iStarRating; } -bool CPVREpgInfoTag::Notify(void) const -{ - return m_bNotify; -} - int CPVREpgInfoTag::SeriesNumber(void) const { return m_iSeriesNumber; @@ -528,7 +521,6 @@ bool CPVREpgInfoTag::Update(const CPVREpgInfoTag &tag, bool bUpdateBroadcastId / m_firstAired != tag.m_firstAired || m_iParentalRating != tag.m_iParentalRating || m_iStarRating != tag.m_iStarRating || - m_bNotify != tag.m_bNotify || m_iEpisodeNumber != tag.m_iEpisodeNumber || m_iEpisodePart != tag.m_iEpisodePart || m_iSeriesNumber != tag.m_iSeriesNumber || @@ -580,7 +572,6 @@ bool CPVREpgInfoTag::Update(const CPVREpgInfoTag &tag, bool bUpdateBroadcastId / m_firstAired = tag.m_firstAired; m_iParentalRating = tag.m_iParentalRating; m_iStarRating = tag.m_iStarRating; - m_bNotify = tag.m_bNotify; m_iEpisodeNumber = tag.m_iEpisodeNumber; m_iEpisodePart = tag.m_iEpisodePart; m_iSeriesNumber = tag.m_iSeriesNumber; diff --git a/xbmc/pvr/epg/EpgInfoTag.h b/xbmc/pvr/epg/EpgInfoTag.h index a61c45930b..1486c2e5f3 100644 --- a/xbmc/pvr/epg/EpgInfoTag.h +++ b/xbmc/pvr/epg/EpgInfoTag.h @@ -292,12 +292,6 @@ namespace PVR int StarRating(void) const; /*! - * @brief Notify on start if true. - * @return Notify on start. - */ - bool Notify(void) const; - - /*! * @brief The series number of this event. * @return The series number. */ @@ -435,7 +429,6 @@ namespace PVR */ CDateTime GetCurrentPlayingTime(void) const; - bool m_bNotify = false; /*!< notify on start */ int m_iDatabaseID = -1; /*!< database ID */ int m_iGenreType = 0; /*!< genre type */ int m_iGenreSubType = 0; /*!< genre subtype */ diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp index 35997d5c0c..a119bc983c 100644 --- a/xbmc/settings/AdvancedSettings.cpp +++ b/xbmc/settings/AdvancedSettings.cpp @@ -429,7 +429,7 @@ void CAdvancedSettings::Initialize() m_databaseVideo.Reset(); m_pictureExtensions = ".png|.jpg|.jpeg|.bmp|.gif|.ico|.tif|.tiff|.tga|.pcx|.cbz|.zip|.rss|.webp|.jp2|.apng"; - m_musicExtensions = ".nsv|.m4a|.flac|.aac|.strm|.pls|.rm|.rma|.mpa|.wav|.wma|.ogg|.mp3|.mp2|.m3u|.gdm|.imf|.m15|.sfx|.uni|.ac3|.dts|.cue|.aif|.aiff|.wpl|.xspf|.ape|.mac|.mpc|.mp+|.mpp|.shn|.zip|.wv|.dsp|.xsp|.xwav|.waa|.wvs|.wam|.gcm|.idsp|.mpdsp|.mss|.spt|.rsd|.sap|.cmc|.cmr|.dmc|.mpt|.mpd|.rmt|.tmc|.tm8|.tm2|.oga|.url|.pxml|.tta|.rss|.wtv|.mka|.tak|.opus|.dff|.dsf|.m4b"; + m_musicExtensions = ".nsv|.m4a|.flac|.aac|.strm|.pls|.rm|.rma|.mpa|.wav|.wma|.ogg|.mp3|.mp2|.m3u|.gdm|.imf|.m15|.sfx|.uni|.ac3|.dts|.cue|.aif|.aiff|.wpl|.xspf|.ape|.mac|.mpc|.mp+|.mpp|.shn|.zip|.wv|.dsp|.xsp|.xwav|.waa|.wvs|.wam|.gcm|.idsp|.mpdsp|.mss|.spt|.rsd|.sap|.cmc|.cmr|.dmc|.mpt|.mpd|.rmt|.tmc|.tm8|.tm2|.oga|.url|.pxml|.tta|.rss|.wtv|.mka|.tak|.opus|.dff|.dsf|.m4b|.dtshd"; m_videoExtensions = ".m4v|.3g2|.3gp|.nsv|.tp|.ts|.ty|.strm|.pls|.rm|.rmvb|.mpd|.m3u|.m3u8|.ifo|.mov|.qt|.divx|.xvid|.bivx|.vob|.nrg|.img|.iso|.udf|.pva|.wmv|.asf|.asx|.ogm|.m2v|.avi|.bin|.dat|.mpg|.mpeg|.mp4|.mkv|.mk3d|.avc|.vp3|.svq3|.nuv|.viv|.dv|.fli|.flv|.001|.wpl|.xspf|.zip|.vdr|.dvr-ms|.xsp|.mts|.m2t|.m2ts|.evo|.ogv|.sdp|.avs|.rec|.url|.pxml|.vc1|.h264|.rcv|.rss|.mpls|.webm|.bdmv|.wtv|.trp|.f4v"; m_subtitlesExtensions = ".utf|.utf8|.utf-8|.sub|.srt|.smi|.rt|.txt|.ssa|.text|.ssa|.aqt|.jss|.ass|.idx|.ifo|.zip"; m_discStubExtensions = ".disc"; @@ -1050,10 +1050,10 @@ void CAdvancedSettings::ParseSettingsFile(const std::string &file) { std::string strFrom, strTo; TiXmlNode* pFrom = pSubstitute->FirstChild("from"); - if (pFrom) + if (pFrom && !pFrom->NoChildren()) strFrom = CSpecialProtocol::TranslatePath(pFrom->FirstChild()->Value()).c_str(); TiXmlNode* pTo = pSubstitute->FirstChild("to"); - if (pTo) + if (pTo && !pTo->NoChildren()) strTo = pTo->FirstChild()->Value(); if (!strFrom.empty() && !strTo.empty()) diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp index 0ae68f34d4..20b28c7bd7 100644 --- a/xbmc/settings/Settings.cpp +++ b/xbmc/settings/Settings.cpp @@ -207,7 +207,7 @@ const std::string CSettings::SETTING_EPG_SELECTACTION = "epg.selectaction"; const std::string CSettings::SETTING_EPG_HIDENOINFOAVAILABLE = "epg.hidenoinfoavailable"; const std::string CSettings::SETTING_EPG_EPGUPDATE = "epg.epgupdate"; const std::string CSettings::SETTING_EPG_PREVENTUPDATESWHILEPLAYINGTV = "epg.preventupdateswhileplayingtv"; -const std::string CSettings::SETTING_EPG_IGNOREDBFORCLIENT = "epg.ignoredbforclient"; +const std::string CSettings::SETTING_EPG_STOREEPGINDATABASE = "epg.storeepgindatabase"; const std::string CSettings::SETTING_EPG_RESETEPG = "epg.resetepg"; const std::string CSettings::SETTING_PVRPLAYBACK_SWITCHTOFULLSCREEN = "pvrplayback.switchtofullscreen"; const std::string CSettings::SETTING_PVRPLAYBACK_SIGNALQUALITY = "pvrplayback.signalquality"; diff --git a/xbmc/settings/Settings.h b/xbmc/settings/Settings.h index d0743b32d3..7a49a1f2bc 100644 --- a/xbmc/settings/Settings.h +++ b/xbmc/settings/Settings.h @@ -170,7 +170,7 @@ public: static const std::string SETTING_EPG_HIDENOINFOAVAILABLE; static const std::string SETTING_EPG_EPGUPDATE; static const std::string SETTING_EPG_PREVENTUPDATESWHILEPLAYINGTV; - static const std::string SETTING_EPG_IGNOREDBFORCLIENT; + static const std::string SETTING_EPG_STOREEPGINDATABASE; static const std::string SETTING_EPG_RESETEPG; static const std::string SETTING_PVRPLAYBACK_SWITCHTOFULLSCREEN; static const std::string SETTING_PVRPLAYBACK_SIGNALQUALITY; diff --git a/xbmc/utils/ScraperUrl.cpp b/xbmc/utils/ScraperUrl.cpp index b4595faf92..baa22a68e9 100644 --- a/xbmc/utils/ScraperUrl.cpp +++ b/xbmc/utils/ScraperUrl.cpp @@ -374,7 +374,7 @@ void CScraperUrl::GetThumbURLs(std::vector<std::string> &thumbs, const std::stri { for (std::vector<SUrlEntry>::const_iterator iter = m_url.begin(); iter != m_url.end(); ++iter) { - if (iter->m_aspect == type || type.empty() || type == "thumb" || iter->m_aspect.empty()) + if (iter->m_aspect == type || type.empty() || iter->m_aspect.empty()) { if ((iter->m_type == CScraperUrl::URL_TYPE_GENERAL && season == -1) || (iter->m_type == CScraperUrl::URL_TYPE_SEASON && iter->m_season == season)) diff --git a/xbmc/video/VideoThumbLoader.cpp b/xbmc/video/VideoThumbLoader.cpp index 75946ca442..2e9c9a5936 100644 --- a/xbmc/video/VideoThumbLoader.cpp +++ b/xbmc/video/VideoThumbLoader.cpp @@ -108,7 +108,7 @@ bool CThumbExtractor::DoWork() CTextureDetails details; details.file = CTextureCache::GetCacheFile(m_target) + ".jpg"; result = CDVDFileInfo::ExtractThumb(m_item, details, m_fillStreamDetails ? &m_item.GetVideoInfoTag()->m_streamDetails : nullptr, m_pos); - if(result) + if (result) { CTextureCache::GetInstance().AddCachedTexture(m_target, details); m_item.SetProperty("HasAutoThumb", true); @@ -360,7 +360,15 @@ bool CVideoThumbLoader::LoadItemCached(CFileItem* pItem) !setting->FindIntInList(CSettings::VIDEOLIBRARY_THUMB_SHOW_UNWATCHED_EPISODE) ) { - pItem->SetArt("thumb", "OverlaySpoiler.png"); + // use fanart if available + if (pItem->HasArt("fanart")) + { + pItem->SetArt("thumb", pItem->GetArt("fanart")); + } + else + { + pItem->SetArt("thumb", "OverlaySpoiler.png"); + } } m_videoDatabase->Close(); diff --git a/xbmc/windowing/WinSystem.h b/xbmc/windowing/WinSystem.h index 6e84c49e5f..e8c13a159b 100644 --- a/xbmc/windowing/WinSystem.h +++ b/xbmc/windowing/WinSystem.h @@ -139,6 +139,17 @@ public: // Access render system interface CGraphicContext& GetGfxContext(); + /** + * Get OS specific hardware context + * + * \return OS specific context or nullptr if OS not have + * + * \note This function is currently only related to Windows with DirectX, + * all other OS where use GL returns nullptr. + * Returned Windows class pointer is ID3D11DeviceContext1. + */ + virtual void* GetHWContext() { return nullptr; } + protected: void UpdateDesktopResolution(RESOLUTION_INFO& newRes, const std::string &output, int width, int height, float refreshRate, uint32_t dwFlags); virtual std::unique_ptr<KODI::WINDOWING::IOSScreenSaver> GetOSScreenSaverImpl() { return nullptr; } diff --git a/xbmc/windowing/gbm/WinSystemGbm.cpp b/xbmc/windowing/gbm/WinSystemGbm.cpp index 4a3291a550..5cee802be2 100644 --- a/xbmc/windowing/gbm/WinSystemGbm.cpp +++ b/xbmc/windowing/gbm/WinSystemGbm.cpp @@ -207,11 +207,19 @@ void CWinSystemGbm::FlipPage(bool rendered, bool videoLayer) m_videoLayerBridge->Disable(); } - struct gbm_bo *bo = m_GBM->LockFrontBuffer(); + struct gbm_bo *bo = nullptr; + + if (rendered) + { + bo = m_GBM->LockFrontBuffer(); + } m_DRM->FlipPage(bo, rendered, videoLayer); - m_GBM->ReleaseBuffer(); + if (rendered) + { + m_GBM->ReleaseBuffer(); + } if (m_videoLayerBridge && !videoLayer) { diff --git a/xbmc/windowing/gbm/WinSystemGbmGLESContext.cpp b/xbmc/windowing/gbm/WinSystemGbmGLESContext.cpp index c24cb919ae..f763577117 100644 --- a/xbmc/windowing/gbm/WinSystemGbmGLESContext.cpp +++ b/xbmc/windowing/gbm/WinSystemGbmGLESContext.cpp @@ -14,6 +14,7 @@ #include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererGBM.h" #include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGLES.h" #include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h" +#include "cores/VideoPlayer/Process/gbm/ProcessInfoGBM.h" #include "cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.h" #include "cores/VideoPlayer/VideoRenderers/RenderFactory.h" @@ -65,6 +66,7 @@ bool CWinSystemGbmGLESContext::InitWindowSystem() CRendererDRMPRIMEGLES::Register(); CRendererDRMPRIME::Register(); CDVDVideoCodecDRMPRIME::Register(); + VIDEOPLAYER::CProcessInfoGBM::Register(); return true; } diff --git a/xbmc/windowing/win10/WinSystemWin10DX.h b/xbmc/windowing/win10/WinSystemWin10DX.h index 5edfd27bd8..61c747224c 100644 --- a/xbmc/windowing/win10/WinSystemWin10DX.h +++ b/xbmc/windowing/win10/WinSystemWin10DX.h @@ -25,6 +25,7 @@ public: void PresentRenderImpl(bool rendered) override; bool DPIChanged(WORD dpi, RECT windowRect) const override; bool DestroyRenderSystem() override; + void* GetHWContext() override { return m_deviceResources->GetD3DContext(); } void UninitHooks(); void InitHooks(IDXGIOutput* pOutput); diff --git a/xbmc/windowing/windows/WinSystemWin32DX.h b/xbmc/windowing/windows/WinSystemWin32DX.h index 3a613c2643..8a79c8be65 100644 --- a/xbmc/windowing/windows/WinSystemWin32DX.h +++ b/xbmc/windowing/windows/WinSystemWin32DX.h @@ -30,6 +30,7 @@ public: bool DPIChanged(WORD dpi, RECT windowRect) const override; void SetWindow(HWND hWnd) const; bool DestroyRenderSystem() override; + void* GetHWContext() override { return m_deviceResources->GetD3DContext(); } void UninitHooks(); void InitHooks(IDXGIOutput* pOutput); |