diff options
77 files changed, 843 insertions, 410 deletions
diff --git a/addons/skin.estuary/xml/Variables.xml b/addons/skin.estuary/xml/Variables.xml index afb70923e5..49382061a9 100644 --- a/addons/skin.estuary/xml/Variables.xml +++ b/addons/skin.estuary/xml/Variables.xml @@ -703,8 +703,10 @@ </variable> <variable name="VideoListThumbVar"> <value condition="!String.IsEmpty(Container(6).ListItem.Art(landscape))">$INFO[Container(6).ListItem.Art(landscape)]</value> + <value condition="!String.IsEmpty(Container(6).ListItem.Art(poster))">$INFO[Container(6).ListItem.Art(poster)]</value> <value condition="!String.IsEmpty(Container(6).ListItem.Art(thumb))">$INFO[Container(6).ListItem.Art(thumb)]</value> <value condition="!String.IsEmpty(Container(50).ListItem.Art(landscape))">$INFO[Container(50).ListItem.Art(landscape)]</value> + <value condition="!String.IsEmpty(Container(50).ListItem.Art(poster))">$INFO[Container(50).ListItem.Art(poster)]</value> <value condition="!String.IsEmpty(Container(50).ListItem.Art(thumb))">$INFO[Container(50).ListItem.Art(thumb)]</value> <value>$INFO[ListItem.Art(thumb)]</value> </variable> diff --git a/cmake/treedata/common/subdirs.txt b/cmake/treedata/common/subdirs.txt index 00bd9396e5..f4e46076fb 100644 --- a/cmake/treedata/common/subdirs.txt +++ b/cmake/treedata/common/subdirs.txt @@ -16,6 +16,7 @@ xbmc/dialogs dialogs xbmc/favourites favourites xbmc/guilib guilib xbmc/guilib/guiinfo guilib_guiinfo +xbmc/guilib/handlers guilib_announcement_handlers xbmc/guilib/listproviders guilib_listproviders xbmc/imagefiles imagefiles xbmc/messaging messaging diff --git a/tools/android/packaging/Makefile.in b/tools/android/packaging/Makefile.in index d0e9d3cc7a..d29675783f 100644 --- a/tools/android/packaging/Makefile.in +++ b/tools/android/packaging/Makefile.in @@ -57,7 +57,7 @@ python: | xbmc/assets cd xbmc/assets/python@PYTHON_VERSION@/lib/python@PYTHON_VERSION@/; rm -rf test config config-@PYTHON_VERSION@ lib-dynload ; find . -name "*.so" -exec rm -f {} \; res: - mkdir -p xbmc/res xbmc/res/values + mkdir -p xbmc/res xbmc/res/values xbmc/res/xml cp -rfp media/mipmap-* xbmc/res/ cp -fp $(CMAKE_SOURCE_DIR)/media/applaunch_screen.png xbmc/res/drawable/ cp -fp $(CMAKE_SOURCE_DIR)/media/applaunch_screen.png xbmc/res/drawable-xxxhdpi/ diff --git a/tools/android/packaging/xbmc/res/xml/searchable.xml b/tools/android/packaging/xbmc/res/xml/searchable.xml deleted file mode 100644 index af66a877fe..0000000000 --- a/tools/android/packaging/xbmc/res/xml/searchable.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<searchable xmlns:android="http://schemas.android.com/apk/res/android" - android:label="@string/app_name" - android:hint="@string/search_hint" - - android:searchSuggestAuthority="org.xbmc.kodi.media" - android:searchSuggestPath="suggestions" - - android:searchSuggestIntentAction="android.intent.action.GET_CONTENT" - android:searchSuggestIntentData="content://org.xbmc.kodi.media/intent" - - android:includeInGlobalSearch="true" - android:searchSettingsDescription="Media" - android:searchSuggestThreshold="3" - > -</searchable>
\ No newline at end of file diff --git a/tools/depends/native/expat/EXPAT-VERSION b/tools/depends/native/expat/EXPAT-VERSION index 3631ff3f5b..dd337f57dc 100644 --- a/tools/depends/native/expat/EXPAT-VERSION +++ b/tools/depends/native/expat/EXPAT-VERSION @@ -1,5 +1,5 @@ LIBNAME=expat -VERSION=2.6.2 +VERSION=2.6.3 SOURCE=$(LIBNAME)-$(VERSION) ARCHIVE=$(SOURCE).tar.xz -SHA512=47b60967d6346d330dded87ea1a2957aa7d34dd825043386a89aa131054714f618ede57bfe97cf6caa40582a4bc67e198d2a915e7d8dbe8ee4f581857c2e3c2e +SHA512=e02c4ad88f9d539258aa1c1db71ded7770a8f12c77b5535e5b34f040ae5b1361ef23132f16d96bdb7c096a83acd637a7c907916bdfcc6d5cfb9e35d04020ca0b diff --git a/tools/depends/native/openssl/OPENSSL-VERSION b/tools/depends/native/openssl/OPENSSL-VERSION index 424c6d4036..a7b9b2c717 100644 --- a/tools/depends/native/openssl/OPENSSL-VERSION +++ b/tools/depends/native/openssl/OPENSSL-VERSION @@ -1,4 +1,4 @@ LIBNAME=openssl -VERSION=3.0.13 +VERSION=3.0.15 ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz -SHA512=22f4096781f0b075f5bf81bd39a0f97e111760dfa73b6f858f6bb54968a7847944d74969ae10f9a51cc21a2f4af20d9a4c463649dc824f5e439e196d6764c4f9 +SHA512=acd80f2f7924d90c1416946a5c61eff461926ad60f4821bb6b08845ea18f8452fd5e88a2c2c5bd0d7590a792cb8341a3f3be042fd0a5b6c9c1b84a497c347bbf diff --git a/tools/depends/target/expat/EXPAT-VERSION b/tools/depends/target/expat/EXPAT-VERSION index 571fe9f0e6..ba82abe72f 100644 --- a/tools/depends/target/expat/EXPAT-VERSION +++ b/tools/depends/target/expat/EXPAT-VERSION @@ -1,6 +1,6 @@ LIBNAME=expat -VERSION=2.6.2 +VERSION=2.6.3 SOURCE=$(LIBNAME)-$(VERSION) ARCHIVE=$(SOURCE).tar.xz -SHA512=47b60967d6346d330dded87ea1a2957aa7d34dd825043386a89aa131054714f618ede57bfe97cf6caa40582a4bc67e198d2a915e7d8dbe8ee4f581857c2e3c2e +SHA512=e02c4ad88f9d539258aa1c1db71ded7770a8f12c77b5535e5b34f040ae5b1361ef23132f16d96bdb7c096a83acd637a7c907916bdfcc6d5cfb9e35d04020ca0b BYPRODUCT=libexpat.a diff --git a/tools/depends/target/openssl/OPENSSL-VERSION b/tools/depends/target/openssl/OPENSSL-VERSION index 424c6d4036..a7b9b2c717 100644 --- a/tools/depends/target/openssl/OPENSSL-VERSION +++ b/tools/depends/target/openssl/OPENSSL-VERSION @@ -1,4 +1,4 @@ LIBNAME=openssl -VERSION=3.0.13 +VERSION=3.0.15 ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz -SHA512=22f4096781f0b075f5bf81bd39a0f97e111760dfa73b6f858f6bb54968a7847944d74969ae10f9a51cc21a2f4af20d9a4c463649dc824f5e439e196d6764c4f9 +SHA512=acd80f2f7924d90c1416946a5c61eff461926ad60f4821bb6b08845ea18f8452fd5e88a2c2c5bd0d7590a792cb8341a3f3be042fd0a5b6c9c1b84a497c347bbf diff --git a/xbmc/ContextMenuItem.h b/xbmc/ContextMenuItem.h index aee6d82e71..6577de3224 100644 --- a/xbmc/ContextMenuItem.h +++ b/xbmc/ContextMenuItem.h @@ -8,6 +8,7 @@ #pragma once +#include <cstdint> #include <map> #include <memory> #include <string> diff --git a/xbmc/FileItem.cpp b/xbmc/FileItem.cpp index dcc28288e4..a3fa357232 100644 --- a/xbmc/FileItem.cpp +++ b/xbmc/FileItem.cpp @@ -2354,89 +2354,6 @@ const std::shared_ptr<PVR::CPVRChannel> CFileItem::GetPVRChannelInfoTag() const : std::shared_ptr<CPVRChannel>(); } -std::string CFileItem::FindTrailer() const -{ - std::string strFile2; - std::string strFile = m_strPath; - if (IsStack()) - { - std::string strPath; - URIUtils::GetParentPath(m_strPath,strPath); - CStackDirectory dir; - std::string strPath2; - strPath2 = dir.GetStackedTitlePath(strFile); - strFile = URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strPath2)); - CFileItem item(dir.GetFirstStackedFile(m_strPath),false); - std::string strTBNFile(URIUtils::ReplaceExtension(ART::GetTBNFile(item), "-trailer")); - strFile2 = URIUtils::AddFileToFolder(strPath,URIUtils::GetFileName(strTBNFile)); - } - if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile)) - { - std::string strPath = URIUtils::GetDirectory(strFile); - std::string strParent; - URIUtils::GetParentPath(strPath,strParent); - strFile = URIUtils::AddFileToFolder(strParent,URIUtils::GetFileName(m_strPath)); - } - - // no local trailer available for these - if (NETWORK::IsInternetStream(*this) || URIUtils::IsUPnP(strFile) || - URIUtils::IsBluray(strFile) || IsLiveTV() || IsPlugin() || IsDVD()) - return ""; - - std::string strDir = URIUtils::GetDirectory(strFile); - CFileItemList items; - CDirectory::GetDirectory(strDir, items, CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(), DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO | DIR_FLAG_NO_FILE_DIRS); - URIUtils::RemoveExtension(strFile); - strFile += "-trailer"; - std::string strFile3 = URIUtils::AddFileToFolder(strDir, "movie-trailer"); - - // Precompile our REs - VECCREGEXP matchRegExps; - CRegExp tmpRegExp(true, CRegExp::autoUtf8); - const std::vector<std::string>& strMatchRegExps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_trailerMatchRegExps; - - std::vector<std::string>::const_iterator strRegExp = strMatchRegExps.begin(); - while (strRegExp != strMatchRegExps.end()) - { - if (tmpRegExp.RegComp(*strRegExp)) - { - matchRegExps.push_back(tmpRegExp); - } - ++strRegExp; - } - - std::string strTrailer; - for (int i = 0; i < items.Size(); i++) - { - std::string strCandidate = items[i]->m_strPath; - URIUtils::RemoveExtension(strCandidate); - if (StringUtils::EqualsNoCase(strCandidate, strFile) || - StringUtils::EqualsNoCase(strCandidate, strFile2) || - StringUtils::EqualsNoCase(strCandidate, strFile3)) - { - strTrailer = items[i]->m_strPath; - break; - } - else - { - VECCREGEXP::iterator expr = matchRegExps.begin(); - - while (expr != matchRegExps.end()) - { - if (expr->RegFind(strCandidate) != -1) - { - strTrailer = items[i]->m_strPath; - i = items.Size(); - break; - } - ++expr; - } - } - } - - return strTrailer; -} - VideoDbContentType CFileItem::GetVideoContentType() const { VideoDbContentType type = VideoDbContentType::MOVIES; diff --git a/xbmc/FileItem.h b/xbmc/FileItem.h index 635c964d8d..ffdac95d49 100644 --- a/xbmc/FileItem.h +++ b/xbmc/FileItem.h @@ -448,9 +448,6 @@ public: */ std::string GetLocalMetadataPath() const; - // finds a matching local trailer file - std::string FindTrailer() const; - bool LoadMusicTag(); bool LoadGameTag(); diff --git a/xbmc/addons/AddonManager.h b/xbmc/addons/AddonManager.h index fd7ee70f66..0679ffd81f 100644 --- a/xbmc/addons/AddonManager.h +++ b/xbmc/addons/AddonManager.h @@ -11,6 +11,7 @@ #include "threads/CriticalSection.h" #include "utils/EventStream.h" +#include <cstdint> #include <map> #include <memory> #include <mutex> diff --git a/xbmc/addons/IAddon.h b/xbmc/addons/IAddon.h index 0bc383055f..d242f60f0e 100644 --- a/xbmc/addons/IAddon.h +++ b/xbmc/addons/IAddon.h @@ -8,6 +8,7 @@ #pragma once +#include <cstdint> #include <map> #include <memory> #include <string> diff --git a/xbmc/addons/interfaces/Filesystem.cpp b/xbmc/addons/interfaces/Filesystem.cpp index 9f22b67ecc..eb35cb7b54 100644 --- a/xbmc/addons/interfaces/Filesystem.cpp +++ b/xbmc/addons/interfaces/Filesystem.cpp @@ -151,8 +151,11 @@ unsigned int Interface_Filesystem::TranslateFileReadBitsToKodi(unsigned int addo kodiFlags |= READ_AUDIO_VIDEO; if (addonFlags & ADDON_READ_AFTER_WRITE) kodiFlags |= READ_AFTER_WRITE; - if (addonFlags & READ_REOPEN) + if (addonFlags & ADDON_READ_REOPEN) kodiFlags |= READ_REOPEN; + //! @todo Add ADDON_READ_NO_BUFFER to filesystem.h in the binary addon devkit + if (addonFlags & READ_NO_BUFFER) + kodiFlags |= READ_NO_BUFFER; return kodiFlags; } diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp index 5877e9f319..02357c123d 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp @@ -254,8 +254,7 @@ unsigned int CAESinkWASAPI::AddPackets(uint8_t **data, unsigned int frames, unsi return 0; HRESULT hr; - BYTE *buf; - DWORD flags = 0; + BYTE* buf; #ifndef _DEBUG LARGE_INTEGER timerStart; @@ -293,9 +292,8 @@ unsigned int CAESinkWASAPI::AddPackets(uint8_t **data, unsigned int frames, unsi return INT_MAX; } - memset(buf, 0, NumFramesRequested * m_format.m_frameSize); //fill buffer with silence - - hr = m_pRenderClient->ReleaseBuffer(NumFramesRequested, flags); //pass back to audio driver + hr = m_pRenderClient->ReleaseBuffer(NumFramesRequested, + AUDCLNT_BUFFERFLAGS_SILENT); //pass back to audio driver if (FAILED(hr)) { #ifdef _DEBUG @@ -359,7 +357,7 @@ unsigned int CAESinkWASAPI::AddPackets(uint8_t **data, unsigned int frames, unsi NumFramesRequested * m_format.m_frameSize); m_bufferPtr = 0; - hr = m_pRenderClient->ReleaseBuffer(NumFramesRequested, flags); //pass back to audio driver + hr = m_pRenderClient->ReleaseBuffer(NumFramesRequested, 0); //pass back to audio driver if (FAILED(hr)) { #ifdef _DEBUG @@ -396,7 +394,10 @@ void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo deviceInfo.m_channels.Reset(); deviceInfo.m_dataFormats.clear(); deviceInfo.m_sampleRates.clear(); + deviceInfo.m_streamTypes.clear(); deviceChannels.Reset(); + add192 = false; + add48 = false; for (unsigned int c = 0; c < WASAPI_SPEAKER_COUNT; c++) { @@ -592,8 +593,23 @@ void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo wfxex.dwChannelMask = KSAUDIO_SPEAKER_STEREO; wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - wfxex.Format.wBitsPerSample = 16; - wfxex.Samples.wValidBitsPerSample = 16; + + // 16 bits is most widely supported and likely to have the widest range of sample rates + if (deviceInfo.m_dataFormats.empty() || + std::find(deviceInfo.m_dataFormats.cbegin(), deviceInfo.m_dataFormats.cend(), + AE_FMT_S16NE) != deviceInfo.m_dataFormats.cend()) + { + wfxex.Format.wBitsPerSample = 16; + wfxex.Samples.wValidBitsPerSample = 16; + } + else + { + const AEDataFormat fmt = deviceInfo.m_dataFormats.front(); + wfxex.Format.wBitsPerSample = CAEUtil::DataFormatToBits(fmt); + wfxex.Samples.wValidBitsPerSample = + (fmt == AE_FMT_S24NE4MSB ? 24 : wfxex.Format.wBitsPerSample); + } + wfxex.Format.nChannels = 2; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; @@ -745,17 +761,17 @@ bool CAESinkWASAPI::InitializeExclusive(AEAudioFormat &format) else if (format.m_dataFormat == AE_FMT_RAW) //No sense in trying other formats for passthrough. return false; - CLog::Log(LOGWARNING, - "AESinkWASAPI: IsFormatSupported failed ({}) - trying to find a compatible format", - WASAPIErrToStr(hr)); + CLog::LogF(LOGWARNING, + "format {} not supported by the device - trying to find a compatible format", + CAEUtil::DataFormatToStr(format.m_dataFormat)); requestedChannels = wfxex.Format.nChannels; desired_map = CAESinkFactoryWin::SpeakerMaskFromAEChannels(format.m_channelLayout); /* The requested format is not supported by the device. Find something that works */ - CLog::Log(LOGWARNING, - "AESinkWASAPI: Input channels are [{}] - Trying to find a matching output layout", - std::string(format.m_channelLayout)); + CLog::LogF(LOGWARNING, "Input channels are [{}] - Trying to find a matching output layout", + std::string(format.m_channelLayout)); + for (int layout = -1; layout <= (int)ARRAYSIZE(layoutsList); layout++) { // if requested layout is not supported, try standard layouts which contain @@ -886,6 +902,20 @@ initialize: format.m_sampleRate = wfxex.Format.nSamplesPerSec; //PCM: Sample rate. RAW: Link speed format.m_frameSize = (wfxex.Format.wBitsPerSample >> 3) * wfxex.Format.nChannels; + ComPtr<IAudioClient2> audioClient2; + if (SUCCEEDED(m_pAudioClient.As(&audioClient2))) + { + AudioClientProperties props = {}; + props.cbSize = sizeof(props); + // ForegroundOnlyMedia/BackgroundCapableMedia replaced in Windows 10 by Movie/Media + props.eCategory = CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10) + ? AudioCategory_Media + : AudioCategory_ForegroundOnlyMedia; + + if (FAILED(hr = audioClient2->SetClientProperties(&props))) + CLog::LogF(LOGERROR, "unable to set audio category, {}", WASAPIErrToStr(hr)); + } + REFERENCE_TIME audioSinkBufferDurationMsec, hnsLatency; audioSinkBufferDurationMsec = (REFERENCE_TIME)500000; diff --git a/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.h b/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.h index 8d153ab5c2..132b71218c 100644 --- a/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.h +++ b/xbmc/cores/RetroPlayer/streams/RetroPlayerVideo.h @@ -11,6 +11,8 @@ #include "IRetroPlayerStream.h" #include "cores/RetroPlayer/RetroPlayerTypes.h" +#include <cstdint> + extern "C" { #include <libavutil/pixfmt.h> diff --git a/xbmc/cores/VideoPlayer/Buffers/VideoBufferDRMPRIME.h b/xbmc/cores/VideoPlayer/Buffers/VideoBufferDRMPRIME.h index b83ee8ca68..dca6e82177 100644 --- a/xbmc/cores/VideoPlayer/Buffers/VideoBufferDRMPRIME.h +++ b/xbmc/cores/VideoPlayer/Buffers/VideoBufferDRMPRIME.h @@ -54,6 +54,8 @@ public: virtual const VideoPicture& GetPicture() const { return m_picture; } virtual uint32_t GetWidth() const { return GetPicture().iWidth; } virtual uint32_t GetHeight() const { return GetPicture().iHeight; } + virtual uint32_t GetXOffset() const { return GetPicture().m_xOffset; } + virtual uint32_t GetYOffset() const { return GetPicture().m_yOffset; } virtual AVDRMFrameDescriptor* GetDescriptor() const = 0; virtual bool IsValid() const { return true; } diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.cpp index 495d6a25f5..a5468d12a0 100644 --- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.cpp +++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.cpp @@ -58,6 +58,8 @@ void VideoPicture::Reset() iWidth = 0; iHeight = 0; + m_xOffset = 0; + m_yOffset = 0; iDisplayWidth = 0; iDisplayHeight = 0; } diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h index ca83b1a04f..f3373612e8 100644 --- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h +++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h @@ -75,6 +75,8 @@ public: unsigned int iWidth; unsigned int iHeight; + unsigned int m_xOffset{0}; + unsigned int m_yOffset{0}; unsigned int iDisplayWidth; //< width of the picture without black bars unsigned int iDisplayHeight; //< height of the picture without black bars diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp index eb2943bb8c..0d407043dd 100644 --- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp +++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecDRMPRIME.cpp @@ -505,6 +505,8 @@ void CDVDVideoCodecDRMPRIME::SetPictureParams(VideoPicture* pVideoPicture) { pVideoPicture->iWidth = m_pFrame->width; pVideoPicture->iHeight = m_pFrame->height; + pVideoPicture->m_xOffset = m_pFrame->crop_left; + pVideoPicture->m_yOffset = m_pFrame->crop_top; double aspect_ratio = 0; AVRational pixel_aspect = m_pFrame->sample_aspect_ratio; diff --git a/xbmc/cores/VideoPlayer/VideoPlayer.cpp b/xbmc/cores/VideoPlayer/VideoPlayer.cpp index 87a9f7240e..5a20ca0fff 100644 --- a/xbmc/cores/VideoPlayer/VideoPlayer.cpp +++ b/xbmc/cores/VideoPlayer/VideoPlayer.cpp @@ -792,7 +792,8 @@ bool CVideoPlayer::OpenInputStream() // find any available external subtitles std::vector<std::string> filenames; - if (!URIUtils::IsUPnP(m_item.GetPath())) + if (!URIUtils::IsUPnP(m_item.GetPath()) && + !m_item.GetProperty("no-ext-subs-scan").asBoolean(false)) CUtil::ScanForExternalSubtitles(m_item.GetDynPath(), filenames); // load any subtitles from file item diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIME.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIME.cpp index 66df0e49c7..2ef7f4d521 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIME.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererDRMPRIME.cpp @@ -77,7 +77,7 @@ CBaseRenderer* CRendererDRMPRIME::Create(CVideoBuffer* buffer) if (!plane) return nullptr; - if (!plane->SupportsFormatAndModifier(format, modifier)) + if (!drm->FindVideoPlane(format, modifier)) return nullptr; return new CRendererDRMPRIME(); diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.cpp index 34d1ab6235..33db29b140 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/VideoLayerBridgeDRMPRIME.cpp @@ -118,9 +118,10 @@ bool CVideoLayerBridgeDRMPRIME::Map(CVideoBufferDRMPRIME* buffer) flags = DRM_MODE_FB_MODIFIERS; // add the video frame FB - ret = drmModeAddFB2WithModifiers(m_DRM->GetFileDescriptor(), buffer->GetWidth(), - buffer->GetHeight(), layer->format, handles, pitches, offsets, - modifier, &buffer->m_fb_id, flags); + ret = drmModeAddFB2WithModifiers(m_DRM->GetFileDescriptor(), + buffer->GetWidth() + buffer->GetXOffset(), + buffer->GetHeight() + buffer->GetYOffset(), layer->format, + handles, pitches, offsets, modifier, &buffer->m_fb_id, flags); if (ret < 0) { CLog::Log(LOGERROR, "CVideoLayerBridgeDRMPRIME::{} - failed to add fb {}, ret = {}", @@ -188,8 +189,8 @@ void CVideoLayerBridgeDRMPRIME::SetVideoPlane(CVideoBufferDRMPRIME* buffer, cons auto plane = m_DRM->GetVideoPlane(); m_DRM->AddProperty(plane, "FB_ID", buffer->m_fb_id); m_DRM->AddProperty(plane, "CRTC_ID", m_DRM->GetCrtc()->GetCrtcId()); - m_DRM->AddProperty(plane, "SRC_X", 0); - m_DRM->AddProperty(plane, "SRC_Y", 0); + m_DRM->AddProperty(plane, "SRC_X", buffer->GetXOffset() << 16); + m_DRM->AddProperty(plane, "SRC_Y", buffer->GetYOffset() << 16); m_DRM->AddProperty(plane, "SRC_W", buffer->GetWidth() << 16); m_DRM->AddProperty(plane, "SRC_H", buffer->GetHeight() << 16); m_DRM->AddProperty(plane, "CRTC_X", static_cast<int32_t>(destRect.x1) & ~1); diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/ConversionMatrix.h b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/ConversionMatrix.h index b2c66a7e73..4f43fc0475 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/ConversionMatrix.h +++ b/xbmc/cores/VideoPlayer/VideoRenderers/VideoShaders/ConversionMatrix.h @@ -10,6 +10,7 @@ #include <array> #include <cmath> +#include <cstdint> #include <memory> extern "C" { diff --git a/xbmc/dialogs/GUIDialogMediaSource.cpp b/xbmc/dialogs/GUIDialogMediaSource.cpp index 0afbc2d060..1109157c63 100644 --- a/xbmc/dialogs/GUIDialogMediaSource.cpp +++ b/xbmc/dialogs/GUIDialogMediaSource.cpp @@ -36,7 +36,7 @@ #if defined(TARGET_ANDROID) #include "utils/FileUtils.h" -#include "platform/android/activity/XBMCApp.h" +#include "platform/android/storage/AndroidStorageProvider.h" #endif #ifdef TARGET_WINDOWS_STORE @@ -249,7 +249,8 @@ void CGUIDialogMediaSource::OnPathBrowse(int item) #if defined(TARGET_ANDROID) // add the default android music directory std::string path; - if (CXBMCApp::GetExternalStorage(path, "music") && !path.empty() && CDirectory::Exists(path)) + if (CAndroidStorageProvider::GetExternalStorage(path, "music") && !path.empty() && + CDirectory::Exists(path)) { share1.strPath = path; share1.strName = g_localizeStrings.Get(20240); @@ -303,7 +304,8 @@ void CGUIDialogMediaSource::OnPathBrowse(int item) #if defined(TARGET_ANDROID) // add the default android video directory std::string path; - if (CXBMCApp::GetExternalStorage(path, "videos") && !path.empty() && CFileUtils::Exists(path)) + if (CAndroidStorageProvider::GetExternalStorage(path, "videos") && !path.empty() && + CFileUtils::Exists(path)) { share1.strPath = path; share1.strName = g_localizeStrings.Get(20241); @@ -349,7 +351,8 @@ void CGUIDialogMediaSource::OnPathBrowse(int item) #if defined(TARGET_ANDROID) // add the default android music directory std::string path; - if (CXBMCApp::GetExternalStorage(path, "pictures") && !path.empty() && CFileUtils::Exists(path)) + if (CAndroidStorageProvider::GetExternalStorage(path, "pictures") && !path.empty() && + CFileUtils::Exists(path)) { share1.strPath = path; share1.strName = g_localizeStrings.Get(20242); @@ -358,7 +361,8 @@ void CGUIDialogMediaSource::OnPathBrowse(int item) } path.clear(); - if (CXBMCApp::GetExternalStorage(path, "photos") && !path.empty() && CFileUtils::Exists(path)) + if (CAndroidStorageProvider::GetExternalStorage(path, "photos") && !path.empty() && + CFileUtils::Exists(path)) { share1.strPath = path; share1.strName = g_localizeStrings.Get(20243); diff --git a/xbmc/filesystem/DirectoryFactory.cpp b/xbmc/filesystem/DirectoryFactory.cpp index 465816c4b9..e9c76cba97 100644 --- a/xbmc/filesystem/DirectoryFactory.cpp +++ b/xbmc/filesystem/DirectoryFactory.cpp @@ -97,7 +97,14 @@ using namespace XFILE; */ IDirectory* CDirectoryFactory::Create(const CFileItem& item) { - return Create(CURL{item.GetDynPath()}); + CURL curl{item.GetDynPath()}; + + // Store the mimetype, allowing the PlayListFactory to set it on the created FileItem + const std::string& mimeType = item.GetMimeType(); + if (!mimeType.empty()) + curl.SetOption("mimetype", mimeType); + + return Create(curl); } /*! diff --git a/xbmc/filesystem/PlaylistFileDirectory.cpp b/xbmc/filesystem/PlaylistFileDirectory.cpp index d95989067f..18e5d7affa 100644 --- a/xbmc/filesystem/PlaylistFileDirectory.cpp +++ b/xbmc/filesystem/PlaylistFileDirectory.cpp @@ -25,12 +25,11 @@ namespace XFILE bool CPlaylistFileDirectory::GetDirectory(const CURL& url, CFileItemList& items) { - const std::string pathToUrl = url.Get(); - std::unique_ptr<PLAYLIST::CPlayList> pPlayList(PLAYLIST::CPlayListFactory::Create(pathToUrl)); + std::unique_ptr<PLAYLIST::CPlayList> pPlayList(PLAYLIST::CPlayListFactory::Create(url)); if (nullptr != pPlayList) { // load it - if (!pPlayList->Load(pathToUrl)) + if (!pPlayList->Load(url.Get())) return false; //hmmm unable to load playlist? PLAYLIST::CPlayList playlist = *pPlayList; @@ -47,12 +46,11 @@ namespace XFILE bool CPlaylistFileDirectory::ContainsFiles(const CURL& url) { - const std::string pathToUrl = url.Get(); - std::unique_ptr<PLAYLIST::CPlayList> pPlayList(PLAYLIST::CPlayListFactory::Create(pathToUrl)); + std::unique_ptr<PLAYLIST::CPlayList> pPlayList(PLAYLIST::CPlayListFactory::Create(url)); if (nullptr != pPlayList) { // load it - if (!pPlayList->Load(pathToUrl)) + if (!pPlayList->Load(url.Get())) return false; //hmmm unable to load playlist? return (pPlayList->size() > 1); diff --git a/xbmc/guilib/FFmpegImage.h b/xbmc/guilib/FFmpegImage.h index 0f7cee380c..8d34def513 100644 --- a/xbmc/guilib/FFmpegImage.h +++ b/xbmc/guilib/FFmpegImage.h @@ -9,6 +9,8 @@ #pragma once #include "iimage.h" + +#include <cstdint> #include <memory> extern "C" diff --git a/xbmc/guilib/GUIComponent.cpp b/xbmc/guilib/GUIComponent.cpp index 5c8b414fd8..449a85d073 100644 --- a/xbmc/guilib/GUIComponent.cpp +++ b/xbmc/guilib/GUIComponent.cpp @@ -18,6 +18,7 @@ #include "TextureManager.h" #include "URL.h" #include "dialogs/GUIDialogYesNo.h" +#include "handlers/GUIAnnouncementHandlerContainer.h" #include <memory> @@ -28,7 +29,8 @@ CGUIComponent::CGUIComponent() m_stereoscopicsManager(std::make_unique<CStereoscopicsManager>()), m_guiInfoManager(std::make_unique<CGUIInfoManager>()), m_guiColorManager(std::make_unique<CGUIColorManager>()), - m_guiAudioManager(std::make_unique<CGUIAudioManager>()) + m_guiAudioManager(std::make_unique<CGUIAudioManager>()), + m_announcementHandlerContainer(std::make_unique<CGUIAnnouncementHandlerContainer>()) { } diff --git a/xbmc/guilib/GUIComponent.h b/xbmc/guilib/GUIComponent.h index b7eb75c58a..2e3e7aadff 100644 --- a/xbmc/guilib/GUIComponent.h +++ b/xbmc/guilib/GUIComponent.h @@ -18,6 +18,7 @@ class CStereoscopicsManager; class CGUIInfoManager; class CGUIColorManager; class CGUIAudioManager; +class CGUIAnnouncementHandlerContainer; class CGUIComponent { @@ -47,4 +48,5 @@ protected: std::unique_ptr<CGUIInfoManager> m_guiInfoManager; std::unique_ptr<CGUIColorManager> m_guiColorManager; std::unique_ptr<CGUIAudioManager> m_guiAudioManager; + std::unique_ptr<CGUIAnnouncementHandlerContainer> m_announcementHandlerContainer; }; diff --git a/xbmc/guilib/handlers/CMakeLists.txt b/xbmc/guilib/handlers/CMakeLists.txt new file mode 100644 index 0000000000..fc052e35df --- /dev/null +++ b/xbmc/guilib/handlers/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SOURCES GUIAnnouncementHandlerContainer.cpp + sources/GUISourcesAnnouncementHandler.cpp) + +set(HEADERS GUIAnnouncementHandlerContainer.h + sources/GUISourcesAnnouncementHandler.h) + +core_add_library(guilib_announcement_handlers) diff --git a/xbmc/guilib/handlers/GUIAnnouncementHandlerContainer.cpp b/xbmc/guilib/handlers/GUIAnnouncementHandlerContainer.cpp new file mode 100644 index 0000000000..f7a5fbae4a --- /dev/null +++ b/xbmc/guilib/handlers/GUIAnnouncementHandlerContainer.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2024 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 "GUIAnnouncementHandlerContainer.h" + +#include "sources/GUISourcesAnnouncementHandler.h" + +CGUIAnnouncementHandlerContainer::CGUIAnnouncementHandlerContainer() +{ + m_announcementHandlers.emplace_back(std::make_unique<CGUISourcesAnnouncementHandler>()); +} diff --git a/xbmc/guilib/handlers/GUIAnnouncementHandlerContainer.h b/xbmc/guilib/handlers/GUIAnnouncementHandlerContainer.h new file mode 100644 index 0000000000..40122c5dbb --- /dev/null +++ b/xbmc/guilib/handlers/GUIAnnouncementHandlerContainer.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 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 "interfaces/IAnnouncer.h" + +#include <memory> +#include <vector> + +/*! +\brief This class is a container of announcement handlers per application component. It allows the GUI Layer +to execute GUI Actions upon receiving announcements from other components effectively decoupling GUI +from other components. +*/ +class CGUIAnnouncementHandlerContainer final +{ +public: + CGUIAnnouncementHandlerContainer(); + ~CGUIAnnouncementHandlerContainer() = default; + +private: + std::vector<std::unique_ptr<ANNOUNCEMENT::IAnnouncer>> m_announcementHandlers; +}; diff --git a/xbmc/guilib/handlers/sources/GUISourcesAnnouncementHandler.cpp b/xbmc/guilib/handlers/sources/GUISourcesAnnouncementHandler.cpp new file mode 100644 index 0000000000..251f0e14ec --- /dev/null +++ b/xbmc/guilib/handlers/sources/GUISourcesAnnouncementHandler.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 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 "GUISourcesAnnouncementHandler.h" + +#include "GUIUserMessages.h" +#include "ServiceBroker.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "interfaces/AnnouncementManager.h" + +CGUISourcesAnnouncementHandler::CGUISourcesAnnouncementHandler() +{ + CServiceBroker::GetAnnouncementManager()->AddAnnouncer(this); +} + +CGUISourcesAnnouncementHandler::~CGUISourcesAnnouncementHandler() +{ + CServiceBroker::GetAnnouncementManager()->RemoveAnnouncer(this); +} + +void CGUISourcesAnnouncementHandler::Announce(ANNOUNCEMENT::AnnouncementFlag flag, + const std::string& sender, + const std::string& message, + const CVariant& data) +{ + // We are only interested in sources changes + if ((flag & ANNOUNCEMENT::Sources) == 0) + return; + + if (message == "OnAdded" || message == "OnRemoved" || message == "OnUpdated") + { + CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); + message.SetStringParam(data.asString()); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); + } +} diff --git a/xbmc/guilib/handlers/sources/GUISourcesAnnouncementHandler.h b/xbmc/guilib/handlers/sources/GUISourcesAnnouncementHandler.h new file mode 100644 index 0000000000..0ec166fdf1 --- /dev/null +++ b/xbmc/guilib/handlers/sources/GUISourcesAnnouncementHandler.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 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 "interfaces/IAnnouncer.h" + +/*! +\brief Handler for announcements of type sources +*/ +class CGUISourcesAnnouncementHandler : public ANNOUNCEMENT::IAnnouncer +{ +public: + CGUISourcesAnnouncementHandler(); + ~CGUISourcesAnnouncementHandler(); + + void Announce(ANNOUNCEMENT::AnnouncementFlag flag, + const std::string& sender, + const std::string& message, + const CVariant& data) override; +}; diff --git a/xbmc/input/keymaps/remote/IRTranslator.h b/xbmc/input/keymaps/remote/IRTranslator.h index 96eddcf153..862fb7599c 100644 --- a/xbmc/input/keymaps/remote/IRTranslator.h +++ b/xbmc/input/keymaps/remote/IRTranslator.h @@ -8,6 +8,7 @@ #pragma once +#include <cstdint> #include <map> #include <memory> #include <string> diff --git a/xbmc/interfaces/IAnnouncer.h b/xbmc/interfaces/IAnnouncer.h index 7c20203573..513fb25937 100644 --- a/xbmc/interfaces/IAnnouncer.h +++ b/xbmc/interfaces/IAnnouncer.h @@ -13,33 +13,35 @@ class CVariant; namespace ANNOUNCEMENT { - enum AnnouncementFlag - { - Player = 0x001, - Playlist = 0x002, - GUI = 0x004, - System = 0x008, - VideoLibrary = 0x010, - AudioLibrary = 0x020, - Application = 0x040, - Input = 0x080, - PVR = 0x100, - Other = 0x200, - Info = 0x400 - }; +enum AnnouncementFlag +{ + Player = 0x001, + Playlist = 0x002, + GUI = 0x004, + System = 0x008, + VideoLibrary = 0x010, + AudioLibrary = 0x020, + Application = 0x040, + Input = 0x080, + PVR = 0x100, + Other = 0x200, + Info = 0x400, + Sources = 0x800 +}; - const auto ANNOUNCE_ALL = (Player | Playlist | GUI | System | VideoLibrary | AudioLibrary | Application | Input | ANNOUNCEMENT::PVR | Other); +const auto ANNOUNCE_ALL = (Player | Playlist | GUI | System | VideoLibrary | AudioLibrary | + Application | Input | ANNOUNCEMENT::PVR | Other); - /*! +/*! \brief Returns a string representation for the given AnnouncementFlag \param notification Specific AnnouncementFlag \return String representation of the given AnnouncementFlag */ - inline const char *AnnouncementFlagToString(const AnnouncementFlag ¬ification) +inline const char* AnnouncementFlagToString(const AnnouncementFlag& notification) +{ + switch (notification) { - switch (notification) - { case Player: return "Player"; case Playlist: @@ -62,10 +64,12 @@ namespace ANNOUNCEMENT return "Other"; case Info: return "Info"; + case Sources: + return "Sources"; default: return "Unknown"; - } } +} class IAnnouncer { diff --git a/xbmc/messaging/ThreadMessage.h b/xbmc/messaging/ThreadMessage.h index 1a277ffcd4..8360d30cc1 100644 --- a/xbmc/messaging/ThreadMessage.h +++ b/xbmc/messaging/ThreadMessage.h @@ -8,6 +8,7 @@ #pragma once +#include <cstdint> #include <memory> #include <string> #include <utility> diff --git a/xbmc/network/ZeroconfBrowser.h b/xbmc/network/ZeroconfBrowser.h index 76a4439a11..7f104eabc5 100644 --- a/xbmc/network/ZeroconfBrowser.h +++ b/xbmc/network/ZeroconfBrowser.h @@ -89,8 +89,7 @@ public: void Stop(); ///returns the list of found services - /// if this is updated, the following message with "zeroconf://" as path is sent: - /// CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); + /// if this is updated, a source update announcement with "zeroconf://" as path is sent: std::vector<ZeroconfService> GetFoundServices(); ///@} diff --git a/xbmc/network/mdns/ZeroconfBrowserMDNS.cpp b/xbmc/network/mdns/ZeroconfBrowserMDNS.cpp index c4a1c1ecab..9546dcc6b5 100644 --- a/xbmc/network/mdns/ZeroconfBrowserMDNS.cpp +++ b/xbmc/network/mdns/ZeroconfBrowserMDNS.cpp @@ -8,11 +8,8 @@ #include "ZeroconfBrowserMDNS.h" -#include "GUIUserMessages.h" #include "ServiceBroker.h" -#include "guilib/GUIComponent.h" -#include "guilib/GUIMessage.h" -#include "guilib/GUIWindowManager.h" +#include "interfaces/AnnouncementManager.h" #include "network/DNSNameCache.h" #include "utils/log.h" @@ -88,10 +85,11 @@ void DNSSD_API CZeroconfBrowserMDNS::BrowserCallback(DNSServiceRef browser, } if(! (flags & kDNSServiceFlagsMoreComing) ) { - CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); - message.SetStringParam("zeroconf://"); - CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); - CLog::Log(LOGDEBUG, "ZeroconfBrowserMDNS::BrowserCallback sent gui update for path zeroconf://"); + CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Sources, "OnUpdated", + CVariant{"zeroconf://"}); + CLog::Log( + LOGDEBUG, + "ZeroconfBrowserMDNS::BrowserCallback sent source update announce for path zeroconf://"); } } else diff --git a/xbmc/network/upnp/UPnP.cpp b/xbmc/network/upnp/UPnP.cpp index 2acfc55069..371efbace2 100644 --- a/xbmc/network/upnp/UPnP.cpp +++ b/xbmc/network/upnp/UPnP.cpp @@ -13,7 +13,6 @@ #include "UPnP.h" #include "FileItem.h" -#include "GUIUserMessages.h" #include "ServiceBroker.h" #include "UPnPInternal.h" #include "UPnPRenderer.h" @@ -21,8 +20,7 @@ #include "UPnPSettings.h" #include "URL.h" #include "cores/playercorefactory/PlayerCoreFactory.h" -#include "guilib/GUIComponent.h" -#include "guilib/GUIWindowManager.h" +#include "interfaces/AnnouncementManager.h" #include "messaging/ApplicationMessenger.h" #include "network/Network.h" #include "profiles/ProfileManager.h" @@ -171,19 +169,15 @@ public: // PLT_MediaBrowser methods bool OnMSAdded(PLT_DeviceDataReference& device) override { - CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); - message.SetStringParam("upnp://"); - CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); + CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Sources, "OnAdded", + CVariant{"upnp://"}); return PLT_SyncMediaBrowser::OnMSAdded(device); } void OnMSRemoved(PLT_DeviceDataReference& device) override { - PLT_SyncMediaBrowser::OnMSRemoved(device); - - CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); - message.SetStringParam("upnp://"); - CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); + CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Sources, "OnRemoved", + CVariant{"upnp://"}); PLT_SyncMediaBrowser::OnMSRemoved(device); } @@ -202,9 +196,8 @@ public: } m_logger->debug("notified container update {}", (const char*)path); - CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); - message.SetStringParam(path.GetChars()); - CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); + CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Sources, "OnUpdated", + CVariant{path.GetChars()}); } bool MarkWatched(const CFileItem& item, const bool watched) diff --git a/xbmc/network/upnp/UPnPRenderer.cpp b/xbmc/network/upnp/UPnPRenderer.cpp index acf6296d3b..196824fbee 100644 --- a/xbmc/network/upnp/UPnPRenderer.cpp +++ b/xbmc/network/upnp/UPnPRenderer.cpp @@ -686,6 +686,7 @@ NPT_Result CUPnPRenderer::PlayMedia(const NPT_String& uri, } else { + item->SetProperty("no-ext-subs-scan", true); CFileItemList* l = new CFileItemList; //don't delete, l->Add(std::make_shared<CFileItem>(*item)); CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MEDIA_PLAY, -1, -1, static_cast<void*>(l)); diff --git a/xbmc/platform/android/PlatformAndroid.cpp b/xbmc/platform/android/PlatformAndroid.cpp index ba9208f6f5..d35896cd14 100644 --- a/xbmc/platform/android/PlatformAndroid.cpp +++ b/xbmc/platform/android/PlatformAndroid.cpp @@ -17,6 +17,7 @@ #include "platform/android/activity/XBMCApp.h" #include "platform/android/powermanagement/AndroidPowerSyscall.h" +#include "platform/android/storage/AndroidStorageProvider.h" #include <stdlib.h> @@ -66,7 +67,7 @@ void CPlatformAndroid::PlatformSyslog() CJNIBuild::BRAND, CJNIBuild::MODEL, CJNIBuild::HARDWARE); std::string extstorage; - bool extready = CXBMCApp::GetExternalStorage(extstorage); + const bool extready = CAndroidStorageProvider::GetExternalStorage(extstorage); CLog::Log( LOGINFO, "External storage path = {}; status = {}; Permissions = {}{}", extstorage, extready ? "ok" : "nok", diff --git a/xbmc/platform/android/activity/XBMCApp.cpp b/xbmc/platform/android/activity/XBMCApp.cpp index b4cdde669c..10e00d0146 100644 --- a/xbmc/platform/android/activity/XBMCApp.cpp +++ b/xbmc/platform/android/activity/XBMCApp.cpp @@ -81,7 +81,6 @@ #include <androidjni/Cursor.h> #include <androidjni/Display.h> #include <androidjni/DisplayManager.h> -#include <androidjni/Environment.h> #include <androidjni/File.h> #include <androidjni/Intent.h> #include <androidjni/IntentFilter.h> @@ -1091,42 +1090,6 @@ int CXBMCApp::GetBatteryLevel() const return m_batteryLevel; } -bool CXBMCApp::GetExternalStorage(std::string &path, const std::string &type /* = "" */) -{ - std::string sType; - std::string mountedState; - bool mounted = false; - - if(type == "files" || type.empty()) - { - CJNIFile external = CJNIEnvironment::getExternalStorageDirectory(); - if (external) - path = external.getAbsolutePath(); - } - else - { - if (type == "music") - sType = "Music"; // Environment.DIRECTORY_MUSIC - else if (type == "videos") - sType = "Movies"; // Environment.DIRECTORY_MOVIES - else if (type == "pictures") - sType = "Pictures"; // Environment.DIRECTORY_PICTURES - else if (type == "photos") - sType = "DCIM"; // Environment.DIRECTORY_DCIM - else if (type == "downloads") - sType = "Download"; // Environment.DIRECTORY_DOWNLOADS - if (!sType.empty()) - { - CJNIFile external = CJNIEnvironment::getExternalStoragePublicDirectory(sType); - if (external) - path = external.getAbsolutePath(); - } - } - mountedState = CJNIEnvironment::getExternalStorageState(); - mounted = (mountedState == "mounted" || mountedState == "mounted_ro"); - return mounted && !path.empty(); -} - // Used in Application.cpp to figure out volume steps int CXBMCApp::GetMaxSystemVolume() { diff --git a/xbmc/platform/android/activity/XBMCApp.h b/xbmc/platform/android/activity/XBMCApp.h index 6fa89cbce3..73f5dcdf09 100644 --- a/xbmc/platform/android/activity/XBMCApp.h +++ b/xbmc/platform/android/activity/XBMCApp.h @@ -166,13 +166,6 @@ public: const std::string& className = std::string()); std::vector<androidPackage> GetApplications() const; - /*! - * \brief If external storage is available, it returns the path for the external storage (for the specified type) - * \param path will contain the path of the external storage (for the specified type) - * \param type optional type. Possible values are "", "files", "music", "videos", "pictures", "photos, "downloads" - * \return true if external storage is available and a valid path has been stored in the path parameter - */ - static bool GetExternalStorage(std::string& path, const std::string& type = ""); static int GetMaxSystemVolume(); static float GetSystemVolume(); static void SetSystemVolume(float percent); diff --git a/xbmc/platform/android/network/ZeroconfBrowserAndroid.cpp b/xbmc/platform/android/network/ZeroconfBrowserAndroid.cpp index dd6db2576b..d31564f5ea 100644 --- a/xbmc/platform/android/network/ZeroconfBrowserAndroid.cpp +++ b/xbmc/platform/android/network/ZeroconfBrowserAndroid.cpp @@ -8,11 +8,8 @@ #include "ZeroconfBrowserAndroid.h" -#include "GUIUserMessages.h" #include "ServiceBroker.h" -#include "guilib/GUIComponent.h" -#include "guilib/GUIMessage.h" -#include "guilib/GUIWindowManager.h" +#include "interfaces/AnnouncementManager.h" #include "network/DNSNameCache.h" #include "utils/log.h" @@ -241,10 +238,10 @@ void CZeroconfBrowserAndroidDiscover::onServiceFound(const jni::CJNINsdServiceIn s.GetName(), s.GetType(), s.GetDomain()); m_browser->addDiscoveredService(this, s); - CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); - message.SetStringParam("zeroconf://"); - CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); - CLog::Log(LOGDEBUG, "CZeroconfBrowserAndroidDiscover::onServiceFound sent gui update for path zeroconf://"); + CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Sources, "OnUpdated", + CVariant{"zeroconf://"}); + CLog::Log(LOGDEBUG, "CZeroconfBrowserAndroidDiscover::onServiceFound sent source update announce " + "for path zeroconf://"); } void CZeroconfBrowserAndroidDiscover::onServiceLost(const jni::CJNINsdServiceInfo& serviceInfo) diff --git a/xbmc/platform/android/storage/AndroidStorageProvider.cpp b/xbmc/platform/android/storage/AndroidStorageProvider.cpp index 33f7ab8a23..59fe3d6ae2 100644 --- a/xbmc/platform/android/storage/AndroidStorageProvider.cpp +++ b/xbmc/platform/android/storage/AndroidStorageProvider.cpp @@ -17,8 +17,6 @@ #include "utils/URIUtils.h" #include "utils/log.h" -#include "platform/android/activity/XBMCApp.h" - #include <array> #include <cstdio> #include <cstdlib> @@ -131,7 +129,7 @@ void CAndroidStorageProvider::GetLocalDrives(VECSOURCES &localDrives) // external directory std::string path; - if (CXBMCApp::GetExternalStorage(path) && !path.empty() && XFILE::CDirectory::Exists(path)) + if (GetExternalStorage(path) && !path.empty() && XFILE::CDirectory::Exists(path)) { share.strPath = path; share.strName = g_localizeStrings.Get(21456); @@ -391,8 +389,7 @@ std::vector<std::string> CAndroidStorageProvider::GetDiskUsage() usage.clear(); // add external storage if available std::string path; - if (CXBMCApp::GetExternalStorage(path) && !path.empty() && GetStorageUsage(path, usage) && - !usage.empty()) + if (GetExternalStorage(path) && !path.empty() && GetStorageUsage(path, usage) && !usage.empty()) result.push_back(usage); // add removable storage @@ -451,3 +448,41 @@ bool CAndroidStorageProvider::GetStorageUsage(const std::string& path, std::stri PATH_MAXLEN, totalSize, "G", usedSize, "G", freeSize, "G", usedPercentage, "%"); return true; } + +bool CAndroidStorageProvider::GetExternalStorage(std::string& path, + const std::string& type /* = "" */) +{ + std::string sType; + std::string mountedState; + bool mounted = false; + + if (type == "files" || type.empty()) + { + CJNIFile external = CJNIEnvironment::getExternalStorageDirectory(); + if (external) + path = external.getAbsolutePath(); + } + else + { + if (type == "music") + sType = "Music"; // Environment.DIRECTORY_MUSIC + else if (type == "videos") + sType = "Movies"; // Environment.DIRECTORY_MOVIES + else if (type == "pictures") + sType = "Pictures"; // Environment.DIRECTORY_PICTURES + else if (type == "photos") + sType = "DCIM"; // Environment.DIRECTORY_DCIM + else if (type == "downloads") + sType = "Download"; // Environment.DIRECTORY_DOWNLOADS + if (!sType.empty()) + { + CJNIFile external = CJNIEnvironment::getExternalStoragePublicDirectory(sType); + if (external) + path = external.getAbsolutePath(); + } + } + + mountedState = CJNIEnvironment::getExternalStorageState(); + mounted = (mountedState == "mounted" || mountedState == "mounted_ro"); + return mounted && !path.empty(); +} diff --git a/xbmc/platform/android/storage/AndroidStorageProvider.h b/xbmc/platform/android/storage/AndroidStorageProvider.h index e39ff60dea..abd7b8e3b2 100644 --- a/xbmc/platform/android/storage/AndroidStorageProvider.h +++ b/xbmc/platform/android/storage/AndroidStorageProvider.h @@ -30,6 +30,14 @@ public: bool PumpDriveChangeEvents(IStorageEventsCallback* callback) override; + /*! + * \brief If external storage is available, it returns the path for the external storage (for the specified type) + * \param path will contain the path of the external storage (for the specified type) + * \param type optional type. Possible values are "", "files", "music", "videos", "pictures", "photos, "downloads" + * \return true if external storage is available and a valid path has been stored in the path parameter + */ + static bool GetExternalStorage(std::string& path, const std::string& type = ""); + private: std::string unescape(const std::string& str); VECSOURCES m_removableDrives; diff --git a/xbmc/platform/darwin/network/ZeroconfBrowserDarwin.cpp b/xbmc/platform/darwin/network/ZeroconfBrowserDarwin.cpp index 50515d0879..e2078a3520 100644 --- a/xbmc/platform/darwin/network/ZeroconfBrowserDarwin.cpp +++ b/xbmc/platform/darwin/network/ZeroconfBrowserDarwin.cpp @@ -8,11 +8,8 @@ #include "ZeroconfBrowserDarwin.h" -#include "GUIUserMessages.h" #include "ServiceBroker.h" -#include "guilib/GUIComponent.h" -#include "guilib/GUIMessage.h" -#include "guilib/GUIWindowManager.h" +#include "interfaces/AnnouncementManager.h" #include "utils/log.h" #include "platform/darwin/DarwinUtils.h" @@ -168,10 +165,10 @@ void CZeroconfBrowserDarwin::BrowserCallback(CFNetServiceBrowserRef browser, CFO } if (! (flags & kCFNetServiceFlagMoreComing) ) { - CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH); - message.SetStringParam("zeroconf://"); - CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message); - CLog::Log(LOGDEBUG, "CZeroconfBrowserDarwin::BrowserCallback sent gui update for path zeroconf://"); + CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Sources, "OnUpdated", + CVariant{"zeroconf://"}); + CLog::Log(LOGDEBUG, "CZeroconfBrowserDarwin::BrowserCallback sent sources update " + "announcement for path zeroconf://"); } } else { diff --git a/xbmc/platform/linux/storage/UDevProvider.cpp b/xbmc/platform/linux/storage/UDevProvider.cpp index 7123e87a77..df5b9844c5 100644 --- a/xbmc/platform/linux/storage/UDevProvider.cpp +++ b/xbmc/platform/linux/storage/UDevProvider.cpp @@ -17,7 +17,9 @@ extern "C" { #include <poll.h> } -static const char *get_mountpoint(const char *devnode) +namespace +{ +const char* get_mountpoint(const char* devnode) { static char buf[4096]; const char *delim = " "; @@ -59,6 +61,7 @@ static const char *get_mountpoint(const char *devnode) fclose(fp); return mountpoint; } +} // namespace CUDevProvider::CUDevProvider() { diff --git a/xbmc/playlists/PlayListFactory.cpp b/xbmc/playlists/PlayListFactory.cpp index 7fe5ff6fcb..63b9bd401d 100644 --- a/xbmc/playlists/PlayListFactory.cpp +++ b/xbmc/playlists/PlayListFactory.cpp @@ -9,6 +9,7 @@ #include "PlayListFactory.h" #include "FileItem.h" +#include "URL.h" #include "network/NetworkFileItemClassify.h" #include "playlists/PlayListASX.h" #include "playlists/PlayListB4S.h" @@ -25,6 +26,19 @@ namespace KODI::PLAYLIST { +CPlayList* CPlayListFactory::Create(const CURL& url) +{ + CFileItem item{url.Get(), false}; + + if (url.HasOption("mimetype")) + { + item.SetContentLookup(false); + item.SetMimeType(url.GetOption("mimetype")); + } + + return Create(item); +} + CPlayList* CPlayListFactory::Create(const std::string& filename) { CFileItem item(filename,false); diff --git a/xbmc/playlists/PlayListFactory.h b/xbmc/playlists/PlayListFactory.h index 5161150ea5..410c1e392d 100644 --- a/xbmc/playlists/PlayListFactory.h +++ b/xbmc/playlists/PlayListFactory.h @@ -20,6 +20,7 @@ namespace KODI::PLAYLIST class CPlayListFactory { public: + static CPlayList* Create(const CURL& url); static CPlayList* Create(const std::string& filename); static CPlayList* Create(const CFileItem& item); static bool IsPlaylist(const CURL& url); diff --git a/xbmc/pvr/PVRContextMenus.cpp b/xbmc/pvr/PVRContextMenus.cpp index f882c9e624..9faa27d1e8 100644 --- a/xbmc/pvr/PVRContextMenus.cpp +++ b/xbmc/pvr/PVRContextMenus.cpp @@ -268,53 +268,37 @@ bool FindSimilar::Execute(const CFileItemPtr& item) const bool StartRecording::IsVisible(const CFileItem& item) const { - const std::shared_ptr<const CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(item); - - std::shared_ptr<CPVRChannel> channel = item.GetPVRChannelInfoTag(); + const std::shared_ptr<CPVRChannel> channel{item.GetPVRChannelInfoTag()}; if (channel) + { + const std::shared_ptr<const CPVRClient> client{CServiceBroker::GetPVRManager().GetClient(item)}; return client && client->GetClientCapabilities().SupportsTimers() && !CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel); + } - const std::shared_ptr<const CPVREpgInfoTag> epg = item.GetEPGInfoTag(); - if (epg && epg->IsRecordable()) + const std::shared_ptr<const CPVREpgInfoTag> epgTag{item.GetEPGInfoTag()}; + if (epgTag && epgTag->IsRecordable()) { - if (epg->IsGapTag()) - { - channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(epg); - if (channel) - { - return client && client->GetClientCapabilities().SupportsTimers() && - !CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel); - } - } - else - { - return client && client->GetClientCapabilities().SupportsTimers() && - !CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epg); - } + const std::shared_ptr<const CPVRClient> client{CServiceBroker::GetPVRManager().GetClient(item)}; + return client && client->GetClientCapabilities().SupportsTimers() && + !CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epgTag); } + return false; } bool StartRecording::Execute(const CFileItemPtr& item) const { - const std::shared_ptr<const CPVREpgInfoTag> epgTag = item->GetEPGInfoTag(); - if (!epgTag || epgTag->IsActive()) - { - // instant recording - std::shared_ptr<CPVRChannel> channel; - if (epgTag) - channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(epgTag); + const std::shared_ptr<CPVRChannel> channel{item->GetPVRChannelInfoTag()}; + if (channel) + return CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().SetRecordingOnChannel(channel, + true); - if (!channel) - channel = item->GetPVRChannelInfoTag(); + const std::shared_ptr<const CPVREpgInfoTag> epgTag{item->GetEPGInfoTag()}; + if (epgTag) + return CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().AddTimer(*item, false); - if (channel) - return CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().SetRecordingOnChannel(channel, - true); - } - - return CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().AddTimer(*item, false); + return false; } /////////////////////////////////////////////////////////////////////////////// @@ -337,9 +321,13 @@ bool StopRecording::IsVisible(const CFileItem& item) const const std::shared_ptr<const CPVREpgInfoTag> epg = item.GetEPGInfoTag(); if (epg && epg->IsGapTag()) { - channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(epg); - if (channel) - return CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel); + const CDateTime now{CDateTime::GetUTCDateTime()}; + if (epg->StartAsUTC() <= now && epg->EndAsUTC() >= now) + { + channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(epg); + if (channel) + return CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel); + } } return false; @@ -350,12 +338,16 @@ bool StopRecording::Execute(const CFileItemPtr& item) const const std::shared_ptr<const CPVREpgInfoTag> epgTag = item->GetEPGInfoTag(); if (epgTag && epgTag->IsGapTag()) { - // instance recording - const std::shared_ptr<CPVRChannel> channel = - CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(epgTag); - if (channel) - return CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().SetRecordingOnChannel(channel, - false); + const CDateTime now{CDateTime::GetUTCDateTime()}; + if (epgTag->StartAsUTC() <= now && epgTag->EndAsUTC() >= now) + { + const std::shared_ptr<CPVRChannel> channel{ + CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(epgTag)}; + if (channel) + return CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().SetRecordingOnChannel( + channel, false); + } + return false; } return CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().StopRecording(*item); diff --git a/xbmc/pvr/epg/Epg.cpp b/xbmc/pvr/epg/Epg.cpp index d1c39edcd3..8bc6045d20 100644 --- a/xbmc/pvr/epg/Epg.cpp +++ b/xbmc/pvr/epg/Epg.cpp @@ -223,7 +223,8 @@ bool CPVREpg::UpdateEntry(const std::shared_ptr<CPVREpgInfoTag>& tag, EPG_EVENT_ } else { - if (IsTagExpired(existingTag)) + // Delete future events and past events if they are older than epg linger time setting + if ((existingTag->StartAsUTC() > CDateTime::GetUTCDateTime()) || IsTagExpired(existingTag)) { m_tags.DeleteEntry(existingTag); } diff --git a/xbmc/pvr/filesystem/PVRGUIDirectory.cpp b/xbmc/pvr/filesystem/PVRGUIDirectory.cpp index d53e8279a9..1fadc5d6d2 100644 --- a/xbmc/pvr/filesystem/PVRGUIDirectory.cpp +++ b/xbmc/pvr/filesystem/PVRGUIDirectory.cpp @@ -429,6 +429,54 @@ void GetGetRecordingsSubDirectories(const CPVRRecordingsPath& recParentPath, } // unnamed namespace +bool CPVRGUIDirectory::GetRecordingsDirectoryInfo(CFileItem& item) +{ + CFileItemList results; + const CPVRGUIDirectory dir{item.GetPath()}; + if (dir.GetRecordingsDirectory(results)) + { + item.SetLabelPreformatted(true); + item.SetProperty("totalepisodes", 0); + item.SetProperty("watchedepisodes", 0); + item.SetProperty("unwatchedepisodes", 0); + item.SetProperty("inprogressepisodes", 0); + + int64_t sizeInBytes{0}; + + for (const auto& result : results.GetList()) + { + const auto recording{result->GetPVRRecordingInfoTag()}; + if (!recording) + continue; + + if (item.m_dateTime.IsValid() || (item.m_dateTime < recording->RecordingTimeAsLocalTime())) + item.m_dateTime = recording->RecordingTimeAsLocalTime(); + + item.IncrementProperty("totalepisodes", 1); + + if (recording->GetPlayCount() == 0) + item.IncrementProperty("unwatchedepisodes", 1); + else + item.IncrementProperty("watchedepisodes", 1); + + if (recording->GetResumePoint().IsPartWay()) + item.IncrementProperty("inprogressepisodes", 1); + + sizeInBytes += recording->GetSizeInBytes(); + } + + item.SetProperty("recordingsize", StringUtils::SizeToString(sizeInBytes)); + + if (item.GetProperty("unwatchedepisodes").asInteger() > 0) + item.SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED); + else + item.SetOverlayImage(CGUIListItem::ICON_OVERLAY_WATCHED); + + return true; + } + return false; +} + bool CPVRGUIDirectory::GetRecordingsDirectory(CFileItemList& results) const { results.SetContent("recordings"); diff --git a/xbmc/pvr/filesystem/PVRGUIDirectory.h b/xbmc/pvr/filesystem/PVRGUIDirectory.h index 132b0a227d..925d97d0b3 100644 --- a/xbmc/pvr/filesystem/PVRGUIDirectory.h +++ b/xbmc/pvr/filesystem/PVRGUIDirectory.h @@ -12,6 +12,7 @@ #include <string> +class CFileItem; class CFileItemList; namespace PVR @@ -103,6 +104,13 @@ public: */ bool GetProvidersDirectory(CFileItemList& results) const; + /*! + * @brief Get info for a recording folder. + * @param item The folder. + * @return True on success, false otherwise.. + */ + static bool GetRecordingsDirectoryInfo(CFileItem& item); + private: bool GetTimersDirectory(CFileItemList& results) const; bool GetRecordingsDirectory(CFileItemList& results) const; diff --git a/xbmc/pvr/guilib/PVRGUIActionsTimers.cpp b/xbmc/pvr/guilib/PVRGUIActionsTimers.cpp index 570249b12a..a247d46ec5 100644 --- a/xbmc/pvr/guilib/PVRGUIActionsTimers.cpp +++ b/xbmc/pvr/guilib/PVRGUIActionsTimers.cpp @@ -207,11 +207,17 @@ bool CPVRGUIActionsTimers::AddTimer(const CFileItem& item, ParentalCheckResult::SUCCESS) return false; + CDateTime gapStart; + int gapDuration{CPVRTimerInfoTag::DEFAULT_PVRRECORD_INSTANTRECORDTIME}; std::shared_ptr<CPVREpgInfoTag> epgTag = CPVRItem(item).GetEpgInfoTag(); if (epgTag) { if (epgTag->IsGapTag()) - epgTag.reset(); // for gap tags, we can only create instant timers + { + gapStart = epgTag->StartAsUTC(); + gapDuration = (epgTag->EndAsUTC() - gapStart).GetSecondsTotal(); + epgTag.reset(); // for gap tags, we can only create instant or time-based timers + } } else if (bCreateRule) { @@ -232,9 +238,28 @@ bool CPVRGUIActionsTimers::AddTimer(const CFileItem& item, return false; } - std::shared_ptr<CPVRTimerInfoTag> newTimer( - epgTag ? CPVRTimerInfoTag::CreateFromEpg(epgTag, bCreateRule) - : CPVRTimerInfoTag::CreateInstantTimerTag(channel)); + std::shared_ptr<CPVRTimerInfoTag> newTimer; + if (epgTag) + { + newTimer = CPVRTimerInfoTag::CreateFromEpg(epgTag, bCreateRule); + } + else if (gapStart.IsValid() && + gapDuration != CPVRTimerInfoTag::DEFAULT_PVRRECORD_INSTANTRECORDTIME) + { + if (gapStart <= CDateTime::GetUTCDateTime()) + gapStart = CDateTime{time_t{0}}; // special PVR addon API value for an instant recording + + // prevent super long recordings for channels without any epg data + gapDuration = std::min( + m_settings.GetIntValue(CSettings::SETTING_PVRRECORD_INSTANTRECORDTIME) * 60, gapDuration); + + newTimer = CPVRTimerInfoTag::CreateTimerTag(channel, gapStart, gapDuration); + } + else + { + newTimer = CPVRTimerInfoTag::CreateInstantTimerTag(channel); + } + if (!newTimer) { if (bCreateRule && bFallbackToOneShotTimer) @@ -562,7 +587,7 @@ bool CPVRGUIActionsTimers::SetRecordingOnChannel(const std::shared_ptr<CPVRChann const std::shared_ptr<CPVRTimerInfoTag> newTimer( epgTag ? CPVRTimerInfoTag::CreateFromEpg(epgTag, false) - : CPVRTimerInfoTag::CreateInstantTimerTag(channel, iDuration)); + : CPVRTimerInfoTag::CreateInstantTimerTag(channel, iDuration * 60)); if (newTimer) bReturn = CServiceBroker::GetPVRManager().Timers()->AddTimer(newTimer); @@ -984,7 +1009,7 @@ void CPVRGUIActionsTimers::AnnounceReminder(const std::shared_ptr<CPVRTimerInfoT } else { - int iDuration = (timer->EndAsUTC() - timer->StartAsUTC()).GetSecondsTotal() / 60; + const int iDuration{(timer->EndAsUTC() - timer->StartAsUTC()).GetSecondsTotal()}; newTimer = CPVRTimerInfoTag::CreateTimerTag(timer->Channel(), timer->StartAsUTC(), iDuration); } diff --git a/xbmc/pvr/timers/PVRTimerInfoTag.cpp b/xbmc/pvr/timers/PVRTimerInfoTag.cpp index 6f5ea746ea..9c49d9d850 100644 --- a/xbmc/pvr/timers/PVRTimerInfoTag.cpp +++ b/xbmc/pvr/timers/PVRTimerInfoTag.cpp @@ -791,7 +791,7 @@ std::shared_ptr<CPVRTimerInfoTag> CPVRTimerInfoTag::CreateFromDate( if (bInstantStart) epgTag = channel->GetEPGNow(); else if (channel->GetEPG()) - epgTag = channel->GetEPG()->GetTagBetween(start, start + CDateTimeSpan(0, 0, iDuration, 0)); + epgTag = channel->GetEPG()->GetTagBetween(start, start + CDateTimeSpan(0, 0, 0, iDuration)); } std::shared_ptr<CPVRTimerInfoTag> newTimer; @@ -853,16 +853,17 @@ std::shared_ptr<CPVRTimerInfoTag> CPVRTimerInfoTag::CreateFromDate( if (iDuration == DEFAULT_PVRRECORD_INSTANTRECORDTIME) iDuration = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt( - CSettings::SETTING_PVRRECORD_INSTANTRECORDTIME); + CSettings::SETTING_PVRRECORD_INSTANTRECORDTIME) * + 60; if (bInstantStart) { - CDateTime endTime = now + CDateTimeSpan(0, 0, iDuration ? iDuration : 120, 0); + const CDateTime endTime{now + CDateTimeSpan(0, 0, 0, iDuration ? iDuration : 2 * 60 * 60)}; newTimer->SetEndFromUTC(endTime); } else { - CDateTime endTime = start + CDateTimeSpan(0, 0, iDuration ? iDuration : 120, 0); + const CDateTime endTime{start + CDateTimeSpan(0, 0, 0, iDuration ? iDuration : 2 * 60 * 60)}; newTimer->SetEndFromUTC(endTime); } diff --git a/xbmc/pvr/timers/PVRTimers.cpp b/xbmc/pvr/timers/PVRTimers.cpp index ccf719091a..a6d0ae395d 100644 --- a/xbmc/pvr/timers/PVRTimers.cpp +++ b/xbmc/pvr/timers/PVRTimers.cpp @@ -635,8 +635,8 @@ bool CPVRTimers::UpdateEntries(int iMaxNotificationDelay) { const CDateTimeSpan duration = timer->EndAsUTC() - timer->StartAsUTC(); const std::shared_ptr<CPVRTimerInfoTag> childTimer = - CPVRTimerInfoTag::CreateReminderFromDate( - nextStart, duration.GetSecondsTotal() / 60, timer); + CPVRTimerInfoTag::CreateReminderFromDate(nextStart, duration.GetSecondsTotal(), + timer); if (childTimer) { bChanged = true; @@ -1062,7 +1062,7 @@ bool CPVRTimers::AddLocalTimer(const std::shared_ptr<CPVRTimerInfoTag>& tag, boo { const CDateTimeSpan duration = persistedTimer->EndAsUTC() - persistedTimer->StartAsUTC(); const std::shared_ptr<CPVRTimerInfoTag> childTimer = - CPVRTimerInfoTag::CreateReminderFromDate(nextStart, duration.GetSecondsTotal() / 60, + CPVRTimerInfoTag::CreateReminderFromDate(nextStart, duration.GetSecondsTotal(), persistedTimer); if (childTimer) { diff --git a/xbmc/settings/AdvancedSettings.h b/xbmc/settings/AdvancedSettings.h index 0f4c5a7871..051636cb05 100644 --- a/xbmc/settings/AdvancedSettings.h +++ b/xbmc/settings/AdvancedSettings.h @@ -13,6 +13,7 @@ #include "settings/lib/ISettingsHandler.h" #include "utils/SortUtils.h" +#include <cstdint> #include <set> #include <string> #include <utility> diff --git a/xbmc/storage/DetectDVDType.cpp b/xbmc/storage/DetectDVDType.cpp index 3a3a3a08a8..91fd7deecb 100644 --- a/xbmc/storage/DetectDVDType.cpp +++ b/xbmc/storage/DetectDVDType.cpp @@ -389,13 +389,6 @@ void CDetectDVDMedia::WaitMediaReady() std::unique_lock<CCriticalSection> waitLock(m_muReadingMedia); } -// Static function -// Returns status of the DVD Drive -bool CDetectDVDMedia::DriveReady() -{ - return m_DriveState == DriveState::READY; -} - DriveState CDetectDVDMedia::GetDriveState() { return m_DriveState; diff --git a/xbmc/storage/DetectDVDType.h b/xbmc/storage/DetectDVDType.h index 500a10f574..a97944509f 100644 --- a/xbmc/storage/DetectDVDType.h +++ b/xbmc/storage/DetectDVDType.h @@ -42,7 +42,6 @@ public: static void WaitMediaReady(); static bool IsDiscInDrive(); - static bool DriveReady(); static DriveState GetDriveState(); static CCdInfo* GetCdInfo(); static CEvent m_evAutorun; diff --git a/xbmc/utils/Archive.h b/xbmc/utils/Archive.h index a1af0c3cf8..341c07d779 100644 --- a/xbmc/utils/Archive.h +++ b/xbmc/utils/Archive.h @@ -8,6 +8,7 @@ #pragma once +#include <cstdint> #include <cstring> #include <memory> #include <string> diff --git a/xbmc/utils/CharArrayParser.cpp b/xbmc/utils/CharArrayParser.cpp index 5aeec2040b..ec5af70a9c 100644 --- a/xbmc/utils/CharArrayParser.cpp +++ b/xbmc/utils/CharArrayParser.cpp @@ -156,11 +156,19 @@ bool CCharArrayParser::ReadNextLine(std::string& line) line.assign(m_data + m_position, lineLimit - m_position); m_position = lineLimit; + // Skip EOL chars if (m_data[m_position] == '\r') { m_position++; + + if (m_data[m_position] == '\n') + m_position++; + // Malformed EOL as \r\r\n + else if (m_position + 1 <= m_limit && m_data[m_position] == '\r' && + m_data[m_position + 1] == '\n') + m_position += 2; } - if (m_data[m_position] == '\n') + else if (m_data[m_position] == '\n') { m_position++; } diff --git a/xbmc/utils/EGLFence.cpp b/xbmc/utils/EGLFence.cpp index 9d0065bdaf..58a9ec1683 100644 --- a/xbmc/utils/EGLFence.cpp +++ b/xbmc/utils/EGLFence.cpp @@ -133,11 +133,7 @@ void CEGLFence::WaitSyncCPU() if (!m_kmsFence) return; - EGLint status{EGL_FALSE}; - - while (status != EGL_CONDITION_SATISFIED_KHR) - status = m_eglClientWaitSyncKHR(m_display, m_kmsFence, 0, EGL_FOREVER_KHR); - - m_eglDestroySyncKHR(m_display, m_kmsFence); + if (m_eglClientWaitSyncKHR(m_display, m_kmsFence, 0, EGL_FOREVER_KHR) != EGL_FALSE) + m_eglDestroySyncKHR(m_display, m_kmsFence); } #endif diff --git a/xbmc/video/VideoDatabase.cpp b/xbmc/video/VideoDatabase.cpp index f333bb334b..20b8724977 100644 --- a/xbmc/video/VideoDatabase.cpp +++ b/xbmc/video/VideoDatabase.cpp @@ -4994,6 +4994,8 @@ void CVideoDatabase::SetArtForItem(int mediaId, const MediaType &mediaType, cons sql = PrepareSQL("INSERT INTO art(media_id, media_type, type, url) VALUES (%d, '%s', '%s', '%s')", mediaId, mediaType.c_str(), artType.c_str(), url.c_str()); m_pDS->exec(sql); } + + AnnounceUpdate(mediaType, mediaId); } catch (...) { diff --git a/xbmc/video/VideoInfoScanner.cpp b/xbmc/video/VideoInfoScanner.cpp index c02ee60282..10cc30a6c9 100644 --- a/xbmc/video/VideoInfoScanner.cpp +++ b/xbmc/video/VideoInfoScanner.cpp @@ -49,6 +49,7 @@ #include "video/VideoFileItemClassify.h" #include "video/VideoManagerTypes.h" #include "video/VideoThumbLoader.h" +#include "video/VideoUtils.h" #include "video/dialogs/GUIDialogVideoManagerExtras.h" #include "video/dialogs/GUIDialogVideoManagerVersions.h" @@ -59,7 +60,7 @@ using namespace XFILE; using namespace ADDON; using namespace KODI::MESSAGING; -using namespace KODI::VIDEO; +using namespace KODI; using KODI::MESSAGING::HELPERS::DialogResponse; using KODI::UTILITY::CDigest; @@ -1527,7 +1528,7 @@ namespace KODI::VIDEO if (content == CONTENT_MOVIES) { // find local trailer first - std::string strTrailer = pItem->FindTrailer(); + std::string strTrailer = UTILS::FindTrailer(*pItem); if (!strTrailer.empty()) movieDetails.m_strTrailer = strTrailer; diff --git a/xbmc/video/VideoUtils.cpp b/xbmc/video/VideoUtils.cpp index eb6ab9674d..73f38e4ead 100644 --- a/xbmc/video/VideoUtils.cpp +++ b/xbmc/video/VideoUtils.cpp @@ -13,12 +13,18 @@ #include "ServiceBroker.h" #include "Util.h" #include "filesystem/Directory.h" +#include "filesystem/StackDirectory.h" #include "filesystem/VideoDatabaseDirectory/QueryParams.h" +#include "network/NetworkFileItemClassify.h" #include "playlists/PlayListFileItemClassify.h" +#include "pvr/filesystem/PVRGUIDirectory.h" +#include "settings/AdvancedSettings.h" #include "settings/SettingUtils.h" #include "settings/Settings.h" #include "settings/SettingsComponent.h" #include "settings/lib/Setting.h" +#include "utils/ArtUtils.h" +#include "utils/FileExtensionProvider.h" #include "utils/FileUtils.h" #include "utils/URIUtils.h" #include "utils/log.h" @@ -41,15 +47,17 @@ KODI::VIDEO::UTILS::ResumeInformation GetFolderItemResumeInformation(const CFile return {}; CFileItem folderItem(item); - if ((!folderItem.HasProperty("inprogressepisodes") || // season/show - (folderItem.GetProperty("inprogressepisodes").asInteger() == 0)) && - (!folderItem.HasProperty("inprogress") || // movie set - (folderItem.GetProperty("inprogress").asInteger() == 0))) + if (!folderItem.HasProperty("inprogressepisodes") && // season/show/recordings + !folderItem.HasProperty("inprogress")) // movie set { - CVideoDatabase db; - if (db.Open()) + if (URIUtils::IsPVRRecordingFileOrFolder(folderItem.GetPath())) + { + PVR::CPVRGUIDirectory::GetRecordingsDirectoryInfo(folderItem); + } + else { - if (!folderItem.HasProperty("inprogressepisodes") && !folderItem.HasProperty("inprogress")) + CVideoDatabase db; + if (db.Open()) { XFILE::VIDEODATABASEDIRECTORY::CQueryParams params; XFILE::VIDEODATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(item.GetPath(), params); @@ -79,7 +87,6 @@ KODI::VIDEO::UTILS::ResumeInformation GetFolderItemResumeInformation(const CFile db.GetSetInfo(static_cast<int>(params.GetSetId()), details, &folderItem); } } - db.Close(); } } @@ -185,6 +192,86 @@ KODI::VIDEO::UTILS::ResumeInformation GetNonFolderItemResumeInformation(const CF namespace KODI::VIDEO::UTILS { + +std::string FindTrailer(const CFileItem& item) +{ + std::string strFile2; + std::string strFile = item.GetPath(); + if (item.IsStack()) + { + std::string strPath; + URIUtils::GetParentPath(item.GetPath(), strPath); + XFILE::CStackDirectory dir; + std::string strPath2; + strPath2 = dir.GetStackedTitlePath(strFile); + strFile = URIUtils::AddFileToFolder(strPath, URIUtils::GetFileName(strPath2)); + CFileItem sitem(dir.GetFirstStackedFile(item.GetPath()), false); + std::string strTBNFile(URIUtils::ReplaceExtension(ART::GetTBNFile(sitem), "-trailer")); + strFile2 = URIUtils::AddFileToFolder(strPath, URIUtils::GetFileName(strTBNFile)); + } + if (URIUtils::IsInRAR(strFile) || URIUtils::IsInZIP(strFile)) + { + std::string strPath = URIUtils::GetDirectory(strFile); + std::string strParent; + URIUtils::GetParentPath(strPath, strParent); + strFile = URIUtils::AddFileToFolder(strParent, URIUtils::GetFileName(item.GetPath())); + } + + // no local trailer available for these + if (NETWORK::IsInternetStream(item) || URIUtils::IsUPnP(strFile) || URIUtils::IsBluray(strFile) || + item.IsLiveTV() || item.IsPlugin() || item.IsDVD()) + return ""; + + std::string strDir = URIUtils::GetDirectory(strFile); + CFileItemList items; + XFILE::CDirectory::GetDirectory( + strDir, items, CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(), + XFILE::DIR_FLAG_READ_CACHE | XFILE::DIR_FLAG_NO_FILE_INFO | XFILE::DIR_FLAG_NO_FILE_DIRS); + URIUtils::RemoveExtension(strFile); + strFile += "-trailer"; + std::string strFile3 = URIUtils::AddFileToFolder(strDir, "movie-trailer"); + + // Precompile our REs + VECCREGEXP matchRegExps; + CRegExp tmpRegExp(true, CRegExp::autoUtf8); + const std::vector<std::string>& strMatchRegExps = + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_trailerMatchRegExps; + + for (const auto& strRegExp : strMatchRegExps) + { + if (tmpRegExp.RegComp(strRegExp)) + matchRegExps.push_back(tmpRegExp); + } + + std::string strTrailer; + for (int i = 0; i < items.Size(); i++) + { + std::string strCandidate = items[i]->GetPath(); + URIUtils::RemoveExtension(strCandidate); + if (StringUtils::EqualsNoCase(strCandidate, strFile) || + StringUtils::EqualsNoCase(strCandidate, strFile2) || + StringUtils::EqualsNoCase(strCandidate, strFile3)) + { + strTrailer = items[i]->GetPath(); + break; + } + else + { + for (auto& expr : matchRegExps) + { + if (expr.RegFind(strCandidate) != -1) + { + strTrailer = items[i]->GetPath(); + i = items.Size(); + break; + } + } + } + } + + return strTrailer; +} + std::string GetOpticalMediaPath(const CFileItem& item) { auto exists = [&item](const std::string& file) diff --git a/xbmc/video/VideoUtils.h b/xbmc/video/VideoUtils.h index c344072ffe..2ee611c4e8 100644 --- a/xbmc/video/VideoUtils.h +++ b/xbmc/video/VideoUtils.h @@ -14,6 +14,13 @@ class CFileItem; namespace KODI::VIDEO::UTILS { + +/*! \brief + * Find a local trailer file for a given file item + * \return non-empty string with path of trailer if found + */ +std::string FindTrailer(const CFileItem& item); + /*! \brief Check whether an item is an optical media folder or its parent. This will return the non-empty path to the playable entry point of the media diff --git a/xbmc/video/dialogs/GUIDialogVideoInfo.cpp b/xbmc/video/dialogs/GUIDialogVideoInfo.cpp index 7d93996ba5..d297a15634 100644 --- a/xbmc/video/dialogs/GUIDialogVideoInfo.cpp +++ b/xbmc/video/dialogs/GUIDialogVideoInfo.cpp @@ -59,6 +59,7 @@ #include "video/VideoItemArtworkHandler.h" #include "video/VideoLibraryQueue.h" #include "video/VideoThumbLoader.h" +#include "video/VideoUtils.h" #include "video/dialogs/GUIDialogVideoManagerExtras.h" #include "video/dialogs/GUIDialogVideoManagerVersions.h" #include "video/guilib/VideoGUIUtils.h" @@ -453,7 +454,7 @@ void CGUIDialogVideoInfo::SetMovie(const CFileItem *item) if (m_movieItem->GetVideoInfoTag()->m_strTrailer.empty() || URIUtils::IsInternetStream(m_movieItem->GetVideoInfoTag()->m_strTrailer)) { - std::string localTrailer = m_movieItem->FindTrailer(); + std::string localTrailer = VIDEO::UTILS::FindTrailer(*m_movieItem); if (!localTrailer.empty()) { m_movieItem->GetVideoInfoTag()->m_strTrailer = localTrailer; @@ -863,8 +864,9 @@ void AddHardCodedAndExtendedArtTypes(std::vector<std::string>& artTypes, const C } // Add art types currently assigned to the media item -void AddCurrentArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag, - CVideoDatabase& db) +void AddCurrentArtTypes(std::vector<std::string>& artTypes, + const CVideoInfoTag& tag, + CVideoDatabase& db) { std::map<std::string, std::string> currentArt; @@ -882,8 +884,9 @@ void AddCurrentArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& } // Add art types that exist for other media items of the same type -void AddMediaTypeArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag, - CVideoDatabase& db) +void AddMediaTypeArtTypes(std::vector<std::string>& artTypes, + const CVideoInfoTag& tag, + CVideoDatabase& db) { std::vector<std::string> dbArtTypes; db.GetArtTypes(tag.m_type, dbArtTypes); @@ -895,8 +898,9 @@ void AddMediaTypeArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTa } // Add art types from available but unassigned artwork for this media item -void AddAvailableArtTypes(std::vector<std::string>& artTypes, const CVideoInfoTag& tag, - CVideoDatabase& db) +void AddAvailableArtTypes(std::vector<std::string>& artTypes, + const CVideoInfoTag& tag, + CVideoDatabase& db) { for (const auto& artType : db.GetAvailableArtTypesForItem(tag.m_iDbId, tag.m_type)) { @@ -929,6 +933,7 @@ public: bool ChooseArtType(); const std::string& GetArtType() const { return m_artType; } + void UpdateArtType(const std::string& type, const std::string& art) const; private: std::shared_ptr<CFileItem> m_item; @@ -937,6 +942,15 @@ private: std::string m_artType; }; +void CArtTypeChooser::UpdateArtType(const std::string& type, const std::string& art) const +{ + m_item->SetArt(type, art); + if (!m_items.IsEmpty()) + for (auto& item : m_items) + if (item->GetProperty("type") == type) + item->SetArt("thumb", art); +} + bool CArtTypeChooser::ChooseArtType() { CGUIDialogSelect* dialog = @@ -1831,7 +1845,10 @@ bool CGUIDialogVideoInfo::ChooseAndManageVideoItemArtwork(const std::shared_ptr< if (!chooser.ChooseArtType()) break; - result = ManageVideoItemArtwork(item, item->GetVideoInfoTag()->m_type, chooser.GetArtType()); + const std::string chosenArtType{chooser.GetArtType()}; + result = ManageVideoItemArtwork(item, item->GetVideoInfoTag()->m_type, chosenArtType); + if (result) + chooser.UpdateArtType(chosenArtType, item->GetArt(chosenArtType)); } while (true); diff --git a/xbmc/video/test/TestVideoUtils.cpp b/xbmc/video/test/TestVideoUtils.cpp index 579fff0180..9feb832e5b 100644 --- a/xbmc/video/test/TestVideoUtils.cpp +++ b/xbmc/video/test/TestVideoUtils.cpp @@ -7,6 +7,7 @@ */ #include "FileItem.h" +#include "URL.h" #include "Util.h" #include "filesystem/Directory.h" #include "platform/Filesystem.h" @@ -22,12 +23,23 @@ using namespace KODI; namespace fs = KODI::PLATFORM::FILESYSTEM; +namespace +{ + using OptDef = std::pair<std::string, bool>; class OpticalMediaPathTest : public testing::WithParamInterface<OptDef>, public testing::Test { }; +using TrailerDef = std::pair<std::string, std::string>; + +class TrailerTest : public testing::WithParamInterface<TrailerDef>, public testing::Test +{ +}; + +} // namespace + TEST_P(OpticalMediaPathTest, GetOpticalMediaPath) { std::error_code ec; @@ -57,3 +69,53 @@ const auto mediapath_tests = std::array{ }; INSTANTIATE_TEST_SUITE_P(TestVideoUtils, OpticalMediaPathTest, testing::ValuesIn(mediapath_tests)); + +TEST_P(TrailerTest, FindTrailer) +{ + std::string temp_path; + if (!GetParam().second.empty()) + { + std::error_code ec; + temp_path = fs::create_temp_directory(ec); + EXPECT_FALSE(ec); + XFILE::CDirectory::Create(temp_path); + const std::string file_path = URIUtils::AddFileToFolder(temp_path, GetParam().second); + { + std::ofstream of(file_path); + } + URIUtils::AddSlashAtEnd(temp_path); + } + + std::string input_path = GetParam().first; + if (!temp_path.empty()) + { + StringUtils::Replace(input_path, "#DIRECTORY#", temp_path); + StringUtils::Replace(input_path, "#URLENCODED_DIRECTORY#", CURL::Encode(temp_path)); + } + + CFileItem item(input_path, false); + EXPECT_EQ(VIDEO::UTILS::FindTrailer(item), + GetParam().second.empty() ? "" + : URIUtils::AddFileToFolder(temp_path, GetParam().second)); + + if (!temp_path.empty()) + XFILE::CDirectory::RemoveRecursive(temp_path); +} + +const auto trailer_tests = std::array{ + TrailerDef{"https://some.where/foo", ""}, + TrailerDef{"upnp://1/2/3", ""}, + TrailerDef{"bluray://1", ""}, + TrailerDef{"pvr://foobar.pvr", ""}, + TrailerDef{"plugin://plugin.video.foo/foo?param=1", ""}, + TrailerDef{"dvd://1", ""}, + TrailerDef{"stack://#DIRECTORY#foo-cd1.avi , #DIRECTORY#foo-cd2.avi", "foo-trailer.mkv"}, + TrailerDef{"stack://#DIRECTORY#foo-cd1.avi , #DIRECTORY#foo-cd2.avi", "foo-cd1-trailer.avi"}, + TrailerDef{"stack://#DIRECTORY#foo-cd1.avi , #DIRECTORY#foo-cd2.avi", "movie-trailer.mp4"}, + TrailerDef{"zip://#URLENCODED_DIRECTORY#bar.zip/bar.avi", "bar-trailer.mov"}, + TrailerDef{"zip://#URLENCODED_DIRECTORY#bar.zip/bar.mkv", "movie-trailer.ogm"}, + TrailerDef{"#DIRECTORY#bar.mkv", "bar-trailer.mkv"}, + TrailerDef{"#DIRECTORY#bar.mkv", "movie-trailer.avi"}, +}; + +INSTANTIATE_TEST_SUITE_P(TestVideoUtils, TrailerTest, testing::ValuesIn(trailer_tests)); diff --git a/xbmc/video/windows/GUIWindowVideoBase.cpp b/xbmc/video/windows/GUIWindowVideoBase.cpp index a875ae4bfa..d231a809f7 100644 --- a/xbmc/video/windows/GUIWindowVideoBase.cpp +++ b/xbmc/video/windows/GUIWindowVideoBase.cpp @@ -65,7 +65,6 @@ #include "video/guilib/VideoGUIUtils.h" #include "video/guilib/VideoPlayActionProcessor.h" #include "video/guilib/VideoSelectActionProcessor.h" -#include "video/guilib/VideoVersionHelper.h" #include "view/GUIViewState.h" #include <memory> diff --git a/xbmc/windowing/gbm/drm/DRMAtomic.cpp b/xbmc/windowing/gbm/drm/DRMAtomic.cpp index ff7f137d60..70ae92e948 100644 --- a/xbmc/windowing/gbm/drm/DRMAtomic.cpp +++ b/xbmc/windowing/gbm/drm/DRMAtomic.cpp @@ -149,6 +149,11 @@ void CDRMAtomic::DrmAtomicCommit(int fb_id, int flags, bool rendered, bool video { CLog::Log(LOGERROR, "CDRMAtomic::{} - atomic commit failed: {}", __FUNCTION__, strerror(errno)); + m_atomicRequestQueue.pop_back(); + } + else if (m_atomicRequestQueue.size() > 1) + { + m_atomicRequestQueue.pop_front(); } if (m_inFenceFd != -1) @@ -164,9 +169,6 @@ void CDRMAtomic::DrmAtomicCommit(int fb_id, int flags, bool rendered, bool video strerror(errno)); } - if (m_atomicRequestQueue.size() > 1) - m_atomicRequestQueue.pop_back(); - m_atomicRequestQueue.emplace_back(std::make_unique<CDRMAtomicRequest>()); m_req = m_atomicRequestQueue.back().get(); } diff --git a/xbmc/windowing/gbm/drm/DRMObject.cpp b/xbmc/windowing/gbm/drm/DRMObject.cpp index 5ffce40fa3..99dda24490 100644 --- a/xbmc/windowing/gbm/drm/DRMObject.cpp +++ b/xbmc/windowing/gbm/drm/DRMObject.cpp @@ -105,6 +105,25 @@ std::optional<uint64_t> CDRMObject::GetPropertyValue(std::string_view name, return {}; } +std::optional<std::span<uint64_t, 2>> CDRMObject::GetRangePropertyLimits(std::string_view name) +{ + auto property = std::find_if(m_propsInfo.begin(), m_propsInfo.end(), + [&name](const auto& prop) { return prop->name == name; }); + + if (property == m_propsInfo.end()) + return {}; + + auto prop = property->get(); + + if (!static_cast<bool>(drm_property_type_is(prop, DRM_MODE_PROP_RANGE))) + return {}; + + if (prop->count_values != 2) + return {}; + + return std::make_optional<std::span<uint64_t, 2>>(prop->values, 2); +} + bool CDRMObject::SetProperty(const std::string& name, uint64_t value) { auto property = std::find_if(m_propsInfo.begin(), m_propsInfo.end(), @@ -130,3 +149,14 @@ bool CDRMObject::SupportsProperty(const std::string& name) return false; } + +std::optional<bool> CDRMObject::IsPropertyImmutable(std::string_view name) +{ + auto property = std::find_if(m_propsInfo.begin(), m_propsInfo.end(), + [&name](const auto& prop) { return prop->name == name; }); + + if (property == m_propsInfo.end()) + return {}; + + return static_cast<bool>(drm_property_type_is(property->get(), DRM_MODE_PROP_IMMUTABLE)); +} diff --git a/xbmc/windowing/gbm/drm/DRMObject.h b/xbmc/windowing/gbm/drm/DRMObject.h index c4200b1a86..39ba28a004 100644 --- a/xbmc/windowing/gbm/drm/DRMObject.h +++ b/xbmc/windowing/gbm/drm/DRMObject.h @@ -12,6 +12,7 @@ #include <cstdint> #include <memory> #include <optional> +#include <span> #include <string_view> #include <vector> @@ -40,6 +41,8 @@ public: bool SetProperty(const std::string& name, uint64_t value); bool SupportsProperty(const std::string& name); + std::optional<bool> IsPropertyImmutable(std::string_view name); + std::optional<std::span<uint64_t, 2>> GetRangePropertyLimits(std::string_view name); protected: explicit CDRMObject(int fd); diff --git a/xbmc/windowing/gbm/drm/DRMUtils.cpp b/xbmc/windowing/gbm/drm/DRMUtils.cpp index 3dd4ee9783..22db758ab6 100644 --- a/xbmc/windowing/gbm/drm/DRMUtils.cpp +++ b/xbmc/windowing/gbm/drm/DRMUtils.cpp @@ -181,77 +181,130 @@ bool CDRMUtils::FindPreferredMode() return true; } -bool CDRMUtils::FindPlanes() +bool CDRMUtils::FindGuiPlane() { - for (size_t i = 0; i < m_crtcs.size(); i++) - { - if (!(m_encoder->GetPossibleCrtcs() & (1 << i))) + /* find the gui plane which support ARGB and 8bit or 10 bit XRGB + * prefer the one which does not support NV12, because it can be re-used in future for video + * prefer the highest id number because they are listed on top where zpos is not available + * and use the gui plane crtc as the crtc + * */ + CDRMPlane* gui_plane_nv12{nullptr}; + CDRMPlane* gui_plane{nullptr}; + CDRMCrtc* gui_crtc_nv12{nullptr}; + CDRMCrtc* gui_crtc{nullptr}; + + for (size_t crtc_offset = 0; crtc_offset < m_crtcs.size(); crtc_offset++) + { + if (!(m_encoder->GetPossibleCrtcs() & (1 << crtc_offset))) continue; - auto videoPlane = std::find_if(m_planes.begin(), m_planes.end(), [&i](auto& plane) { - if (plane->GetPossibleCrtcs() & (1 << i)) - { - return plane->SupportsFormat(DRM_FORMAT_NV12); - } - return false; - }); - - uint32_t videoPlaneId{0}; - - if (videoPlane != m_planes.end()) - videoPlaneId = videoPlane->get()->GetPlaneId(); - - auto guiPlane = - std::find_if(m_planes.begin(), m_planes.end(), [&i, &videoPlaneId](auto& plane) { - if (plane->GetPossibleCrtcs() & (1 << i)) - { - return (plane->GetPlaneId() != videoPlaneId && - (videoPlaneId == 0 || plane->SupportsFormat(DRM_FORMAT_ARGB8888)) && - (plane->SupportsFormat(DRM_FORMAT_XRGB2101010) || - plane->SupportsFormat(DRM_FORMAT_XRGB8888))); - } - return false; - }); - - if (videoPlane != m_planes.end() && guiPlane != m_planes.end()) + for (auto& plane : m_planes) { - m_crtc = m_crtcs[i].get(); - m_video_plane = videoPlane->get(); - m_gui_plane = guiPlane->get(); - break; - } + if (!(plane.get()->GetPossibleCrtcs() & (1 << crtc_offset))) + continue; - if (guiPlane != m_planes.end()) - { - if (!m_crtc && m_encoder->GetCrtcId() == m_crtcs[i]->GetCrtcId()) + if (plane.get()->SupportsFormat(DRM_FORMAT_ARGB8888) && + (plane.get()->SupportsFormat(DRM_FORMAT_XRGB2101010) || + plane.get()->SupportsFormat(DRM_FORMAT_XRGB8888))) { - m_crtc = m_crtcs[i].get(); - m_gui_plane = guiPlane->get(); - m_video_plane = nullptr; + if (plane.get()->SupportsFormat(DRM_FORMAT_NV12) && + (gui_plane_nv12 == nullptr || gui_plane_nv12->GetId() < plane.get()->GetId())) + { + gui_plane_nv12 = plane.get(); + gui_crtc_nv12 = m_crtcs[crtc_offset].get(); + } + else if (!plane.get()->SupportsFormat(DRM_FORMAT_NV12) && + (gui_plane == nullptr || gui_plane->GetId() < plane.get()->GetId())) + { + gui_plane = plane.get(); + gui_crtc = m_crtcs[crtc_offset].get(); + } } } } - CLog::Log(LOGINFO, "CDRMUtils::{} - using crtc: {}", __FUNCTION__, m_crtc->GetCrtcId()); - - // video plane may not be available - if (m_video_plane) - CLog::Log(LOGDEBUG, "CDRMUtils::{} - using video plane {}", __FUNCTION__, - m_video_plane->GetPlaneId()); - - if (m_gui_plane->SupportsFormat(DRM_FORMAT_XRGB2101010)) + // fallback to NV12 supporting plane + if (gui_plane == nullptr) { - m_gui_plane->SetFormat(DRM_FORMAT_XRGB2101010); - CLog::Log(LOGDEBUG, "CDRMUtils::{} - using 10bit gui plane {}", __FUNCTION__, - m_gui_plane->GetPlaneId()); + gui_crtc = gui_crtc_nv12; + gui_plane = gui_plane_nv12; } - else + + if (gui_plane != nullptr) { - m_gui_plane->SetFormat(DRM_FORMAT_XRGB8888); - CLog::Log(LOGDEBUG, "CDRMUtils::{} - using gui plane {}", __FUNCTION__, - m_gui_plane->GetPlaneId()); + m_crtc = gui_crtc; + m_gui_plane = gui_plane; + + CLog::Log(LOGINFO, "CDRMUtils::{} - using crtc: {}", __FUNCTION__, m_crtc->GetCrtcId()); + if (m_gui_plane->SupportsFormat(DRM_FORMAT_XRGB2101010)) + { + m_gui_plane->SetFormat(DRM_FORMAT_XRGB2101010); + CLog::Log(LOGDEBUG, "CDRMUtils::{} - using 10bit gui plane {}", __FUNCTION__, + m_gui_plane->GetPlaneId()); + } + else + { + m_gui_plane->SetFormat(DRM_FORMAT_XRGB8888); + CLog::Log(LOGDEBUG, "CDRMUtils::{} - using gui plane {}", __FUNCTION__, + m_gui_plane->GetPlaneId()); + } + return true; } + CLog::Log(LOGERROR, "CDRMUtils::{} - Can not find a GUI plane", __FUNCTION__); + return false; +} + +bool CDRMUtils::FindVideoPlane(uint32_t format, uint64_t modifier) +{ + bool supports_zpos = m_gui_plane->SupportsProperty("zpos"); + bool zpos_immutable = supports_zpos && m_gui_plane->IsPropertyImmutable("zpos").value(); + + auto crtc_offset = std::distance( + m_crtcs.begin(), + std::find_if(m_crtcs.begin(), m_crtcs.end(), + [this](auto& crtc) { return crtc->GetCrtcId() == m_crtc->GetCrtcId(); })); + + auto guiplane_id = m_gui_plane->GetId(); + auto videoPlane = std::find_if(m_planes.begin(), m_planes.end(), + [&crtc_offset, &format, &modifier, &guiplane_id](auto& plane) + { + if (plane->GetPossibleCrtcs() & (1 << crtc_offset)) + { + return (guiplane_id != plane->GetPlaneId() && + plane->SupportsFormatAndModifier(format, modifier)); + } + return false; + }); + + if (videoPlane == m_planes.end()) + { + CLog::Log(LOGERROR, + "CDRMUtils::{} - Can not find a Video Plane plane for format {}, modifier {}", + __FUNCTION__, format, modifier); + return false; + } + + m_video_plane = videoPlane->get(); + CLog::Log(LOGDEBUG, "CDRMUtils::{} - using video plane {}", __FUNCTION__, + m_video_plane->GetPlaneId()); + + if (!supports_zpos || zpos_immutable) + return true; + + // re-sort the video and gui planes + auto limits = m_gui_plane->GetRangePropertyLimits("zpos"); + + if (!limits) + return true; + + m_gui_plane->SetProperty("zpos", limits.value()[1]); + m_video_plane->SetProperty("zpos", limits.value()[0]); + CLog::Log(LOGDEBUG, "CDRMUtils::{} - gui plane id,zpos: {}, {}", __FUNCTION__, + m_gui_plane->GetId(), limits.value()[1]); + CLog::Log(LOGDEBUG, "CDRMUtils::{} - video plane id,zpos: {}, {}", __FUNCTION__, + m_video_plane->GetId(), limits.value()[0]); + return true; } @@ -467,9 +520,11 @@ bool CDRMUtils::InitDrm() if (!FindCrtc()) return false; - if (!FindPlanes()) + if (!FindGuiPlane()) return false; + FindVideoPlane(DRM_FORMAT_NV12, DRM_FORMAT_MOD_LINEAR); + if (!FindPreferredMode()) return false; diff --git a/xbmc/windowing/gbm/drm/DRMUtils.h b/xbmc/windowing/gbm/drm/DRMUtils.h index f92f716fc4..b99a6dc4fe 100644 --- a/xbmc/windowing/gbm/drm/DRMUtils.h +++ b/xbmc/windowing/gbm/drm/DRMUtils.h @@ -64,6 +64,8 @@ public: static uint32_t FourCCWithoutAlpha(uint32_t fourcc); void SetInFenceFd(int fd) { m_inFenceFd = fd; } + bool FindVideoPlane(uint32_t format, uint64_t modifier); + bool FindGuiPlane(); int TakeOutFenceFd() { int fd{-1}; @@ -89,13 +91,13 @@ protected: int m_inFenceFd{-1}; int m_outFenceFd{-1}; + std::vector<std::unique_ptr<CDRMCrtc>> m_crtcs; std::vector<std::unique_ptr<CDRMPlane>> m_planes; private: bool FindConnector(); bool FindEncoder(); bool FindCrtc(); - bool FindPlanes(); bool FindPreferredMode(); bool RestoreOriginalMode(); RESOLUTION_INFO GetResolutionInfo(drmModeModeInfoPtr mode); @@ -106,7 +108,6 @@ private: std::vector<std::unique_ptr<CDRMConnector>> m_connectors; std::vector<std::unique_ptr<CDRMEncoder>> m_encoders; - std::vector<std::unique_ptr<CDRMCrtc>> m_crtcs; }; } |