diff options
51 files changed, 435 insertions, 294 deletions
diff --git a/addons/skin.estuary/xml/Custom_1109_TopBarOverlay.xml b/addons/skin.estuary/xml/Custom_1109_TopBarOverlay.xml index 3f046338ee..9a5aed2d46 100644 --- a/addons/skin.estuary/xml/Custom_1109_TopBarOverlay.xml +++ b/addons/skin.estuary/xml/Custom_1109_TopBarOverlay.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <window type="dialog" id="1109"> - <onload>Skin.TimerStart(1110_topbaroverlay)</onload> + <onload>Skin.TimerStart(1109_topbaroverlay)</onload> <visible>Window.IsActive(fullscreenvideo) | Window.IsActive(visualisation)</visible> <visible>Window.IsActive(seekbar) | Window.IsActive(pvrosdchannels) | Window.IsActive(pvrchannelguide)</visible> <depth>DepthOSD</depth> @@ -10,7 +10,7 @@ <control type="group"> <visible>![Player.ShowInfo | Window.IsActive(fullscreeninfo) | Player.ShowTime | Window.IsActive(videoosd) | Window.IsActive(musicosd) | Window.IsActive(playerprocessinfo) | Window.IsActive(pvrosdchannels) | Window.IsActive(pvrchannelguide)] + [!String.IsEmpty(Player.SeekNumeric) | Player.Seeking | Player.HasPerformedSeek(3) | Player.Forwarding | Player.Rewinding | Player.Paused] | !String.IsEmpty(PVR.ChannelNumberInput)</visible> <animation effect="fade" start="0" end="100" time="300">VisibleChange</animation> - <animation effect="slide" start="0,0" end="0,-80" time="300" condition="Player.Paused + Integer.IsGreaterOrEqual(Skin.TimerElapsedSecs(1110_topbaroverlay),5)">Conditional</animation> + <animation effect="slide" start="0,0" end="0,-80" time="300" condition="Player.Paused + Integer.IsGreaterOrEqual(Skin.TimerElapsedSecs(1109_topbaroverlay),5)">Conditional</animation> <control type="image"> <left>0</left> <top>0</top> diff --git a/addons/skin.estuary/xml/Timers.xml b/addons/skin.estuary/xml/Timers.xml index e9549dc9db..27b9e16e58 100644 --- a/addons/skin.estuary/xml/Timers.xml +++ b/addons/skin.estuary/xml/Timers.xml @@ -9,8 +9,10 @@ <onstop>Dialog.Close(videoosd)</onstop> </timer> <timer> - <name>1110_topbaroverlay</name> + <name>1109_topbaroverlay</name> <description>A timer that is activated when the topbaroverlay is loaded and stops automatically after 5 seconds (or playback is resumed)</description> - <stop>!Player.Paused | Integer.IsGreaterOrEqual(Skin.TimerElapsedSecs(1110_topbaroverlay),5)</stop> + <start reset="true">Window.IsActive(1109) + [Player.Seeking | Player.Forwarding | Player.Rewinding | Player.HasPerformedSeek(1)]</start> + <reset>Player.Seeking | Player.Forwarding | Player.Rewinding | Player.HasPerformedSeek(1)</reset> + <stop>Integer.IsGreaterOrEqual(Skin.TimerElapsedSecs(1109_topbaroverlay),5) | Player.Playing</stop> </timer> </timers> diff --git a/system/shaders/output_d3d.fx b/system/shaders/output_d3d.fx index eebc200a19..fd5fff1d53 100644 --- a/system/shaders/output_d3d.fx +++ b/system/shaders/output_d3d.fx @@ -96,19 +96,25 @@ float3 inversePQ(float3 x) } #endif #if defined(KODI_HLG_TO_PQ) + +// HLG inverse OETF - BT.2100 +// input: non-linear signal [0,1] range +// output: linear [0,1] range float3 inverseHLG(float3 x) { - const float B67_a = 0.17883277f; - const float B67_b = 0.28466892f; - const float B67_c = 0.55991073f; - const float B67_inv_r2 = 4.0f; - x = (x <= 0.5f) ? x * x * B67_inv_r2 : exp((x - B67_c) / B67_a) + B67_b; + static const float B67_a = 0.17883277f; + static const float B67_b = 0.28466892f; // b = 1 - 4*a + static const float B67_c = 0.55991073f; // c = 0.5 - a*log(4*a) + x = (x <= 0.5f) ? x * x / 3.0f : (exp((x - B67_c) / B67_a) + B67_b) / 12.0f; return x; } +// PQ inverse EOTF, BT.2100 +// input: linear cd/m2 [0,10000] range +// output: non-linear [0,1] range float3 tranferPQ(float3 x) { - x = pow(x / 1000.0f, ST2084_m1); + x = pow(x / 10000.0f, ST2084_m1); x = (ST2084_c1 + ST2084_c2 * x) / (1.0f + ST2084_c3 * x); x = pow(x, ST2084_m2); return x; @@ -136,10 +142,21 @@ float4 output4(float4 color, float2 uv) color.rgb = pow(color.rgb, 1.0f / 2.2f); #endif #if defined(KODI_HLG_TO_PQ) + + // Reference: BT.2100, Table 5, HLG Reference EOTF + + // Display peak luminance in cd/m2 + static const float HLG_Lw = 1000.0f; + static const float HLG_gamma = 1.2f + 0.42f * log10(HLG_Lw / 1000.0f); + + // color.rgb: E', range [0,1] color.rgb = inverseHLG(color.rgb); - float3 ootf_2020 = float3(0.2627f, 0.6780f, 0.0593f); - float ootf_ys = 2000.0f * dot(ootf_2020, color.rgb); - color.rgb *= pow(ootf_ys, 0.2f); + // color.rgb: E, range [0,1] + static const float3 bt2020_lum_rgbweights = float3(0.2627f, 0.6780f, 0.0593f); + float HLG_Ys = dot(bt2020_lum_rgbweights, color.rgb); + color.rgb *= HLG_Lw * pow(HLG_Ys, HLG_gamma - 1.0f); + + // color.rgb: FD, in cd/m2 color.rgb = tranferPQ(color.rgb); #endif #if defined(KODI_3DLUT) diff --git a/tools/depends/native/TexturePacker/src/TexturePacker.cpp b/tools/depends/native/TexturePacker/src/TexturePacker.cpp index 99ceeaa96b..38c31f500c 100644 --- a/tools/depends/native/TexturePacker/src/TexturePacker.cpp +++ b/tools/depends/native/TexturePacker/src/TexturePacker.cpp @@ -197,7 +197,7 @@ CXBTFFrame TexturePacker::CreateXBTFFrame(DecodedFrame& decodedFrame, CXBTFWrite const unsigned int width = decodedFrame.rgbaImage.width; const unsigned int height = decodedFrame.rgbaImage.height; const unsigned int size = width * height * 4; - const unsigned int format = XB_FMT_A8R8G8B8; + const XB_FMT format = XB_FMT_A8R8G8B8; unsigned char* data = (unsigned char*)decodedFrame.rgbaImage.pixels.data(); const bool hasAlpha = HasAlpha(data, width, height); @@ -246,7 +246,7 @@ CXBTFFrame TexturePacker::CreateXBTFFrame(DecodedFrame& decodedFrame, CXBTFWrite frame.SetUnpackedSize(size); frame.SetWidth(width); frame.SetHeight(height); - frame.SetFormat(hasAlpha ? format : format | XB_FMT_OPAQUE); + frame.SetFormat(hasAlpha ? format : static_cast<XB_FMT>(format | XB_FMT_OPAQUE)); frame.SetDuration(delay); return frame; } diff --git a/tools/depends/target/hwdata/Makefile b/tools/depends/target/hwdata/Makefile index 8e1155e59d..e1d23755fb 100644 --- a/tools/depends/target/hwdata/Makefile +++ b/tools/depends/target/hwdata/Makefile @@ -9,8 +9,6 @@ include ../../download-files.include all: .installed-$(PLATFORM) -download: $(TARBALLS_LOCATION)/$(ARCHIVE) - $(PLATFORM): $(DEPS) | $(TARBALLS_LOCATION)/$(ARCHIVE).$(HASH_TYPE) rm -rf $(PLATFORM)/*; mkdir -p $(PLATFORM) cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) diff --git a/tools/depends/target/libdisplay-info/Makefile b/tools/depends/target/libdisplay-info/Makefile index 561a5aeb49..ee900ce0d6 100644 --- a/tools/depends/target/libdisplay-info/Makefile +++ b/tools/depends/target/libdisplay-info/Makefile @@ -33,8 +33,6 @@ export PKG_CONFIG_LIBDIR=$(PREFIX)/share/pkgconfig all: .installed-$(PLATFORM) -download: $(TARBALLS_LOCATION)/$(ARCHIVE) - $(PLATFORM): $(DEPS) | $(TARBALLS_LOCATION)/$(ARCHIVE).$(HASH_TYPE) rm -rf $(PLATFORM)/*; mkdir -p $(PLATFORM) cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE) diff --git a/xbmc/addons/gui/skin/SkinTimerManager.cpp b/xbmc/addons/gui/skin/SkinTimerManager.cpp index b29b05bec5..663f5aa7a3 100644 --- a/xbmc/addons/gui/skin/SkinTimerManager.cpp +++ b/xbmc/addons/gui/skin/SkinTimerManager.cpp @@ -73,7 +73,8 @@ void CSkinTimerManager::LoadTimerInternal(const TiXmlElement* node) startInfo = CServiceBroker::GetGUI()->GetInfoManager().Register( node->FirstChild("start")->FirstChild()->ValueStr()); // check if timer needs to be reset after start - if (node->Attribute("reset") && StringUtils::EqualsNoCase(node->Attribute("reset"), "true")) + if (node->FirstChildElement("start")->Attribute("reset") && + StringUtils::EqualsNoCase(node->FirstChildElement("start")->Attribute("reset"), "true")) { resetOnStart = true; } diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp index bc96e2149f..7be6528684 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp @@ -247,9 +247,12 @@ CAESinkAUDIOTRACK::CAESinkAUDIOTRACK() m_sink_frameSize = 0; m_encoding = CJNIAudioFormat::ENCODING_PCM_16BIT; m_audiotrackbuffer_sec = 0.0; + m_audiotrackbuffer_sec_orig = 0.0; m_at_jni = NULL; m_duration_written = 0; m_headPos = 0; + m_stuckCounter = 0; + m_headPosOld = 0; m_timestampPos = 0; m_sink_sampleRate = 0; m_passthrough = false; @@ -317,6 +320,8 @@ bool CAESinkAUDIOTRACK::Initialize(AEAudioFormat &format, std::string &device) m_format = format; m_headPos = 0; + m_stuckCounter = 0; + m_headPosOld = 0; m_timestampPos = 0; m_linearmovingaverage.clear(); m_pause_ms = 0.0; @@ -572,6 +577,8 @@ bool CAESinkAUDIOTRACK::Initialize(AEAudioFormat &format, std::string &device) "Created Audiotrackbuffer with playing time of {:f} ms min buffer size: {} bytes", m_audiotrackbuffer_sec * 1000, m_min_buffer_size); + m_audiotrackbuffer_sec_orig = m_audiotrackbuffer_sec; + m_jniAudioFormat = m_encoding; m_at_jni = CreateAudioTrack(stream, m_sink_sampleRate, atChannelMask, m_encoding, m_min_buffer_size); @@ -640,6 +647,7 @@ void CAESinkAUDIOTRACK::Deinitialize() m_duration_written = 0; m_headPos = 0; + m_headPosOld = 0; m_timestampPos = 0; m_stampTimer.SetExpired(); @@ -677,6 +685,14 @@ void CAESinkAUDIOTRACK::GetDelay(AEDelayStatus& status) // clear lower 32 bit values, e.g. 0x0001 FFFF FFFF -> 0x0001 0000 0000 // and add head_pos which wrapped around, e.g. 0x0001 0000 0000 -> 0x0001 0000 0004 m_headPos = (m_headPos & UINT64_UPPER_BYTES) | (uint64_t)head_pos; + // check if sink is stuck + if (m_headPos == m_headPosOld) + m_stuckCounter++; + else + { + m_stuckCounter = 0; + m_headPosOld = m_headPos; + } double gone = static_cast<double>(m_headPos) / m_sink_sampleRate; @@ -703,6 +719,8 @@ void CAESinkAUDIOTRACK::GetDelay(AEDelayStatus& status) m_stampTimer.Set(100ms); } } + if (usesAdvancedLogging) + CLog::Log(LOGINFO, "RAW Head-Position {}", m_headPos); // check if last value was received less than 2 seconds ago if (m_timestamp.get_framePosition() > 0 && (CurrentHostCounter() - m_timestamp.get_nanoTime()) < 2 * 1000 * 1000 * 1000) @@ -820,13 +838,24 @@ unsigned int CAESinkAUDIOTRACK::AddPackets(uint8_t **data, unsigned int frames, if (!IsInitialized()) return INT_MAX; - const bool isRawPt = m_passthrough && !m_info.m_wantsIECPassthrough; - const double max_delay = isRawPt ? 3.0 : 1.0; + // If the sink did not move twice the buffer size in time it was opened + // take action. Some sinks open with e.g. 128 ms nicely but under the + // hood need a bit more samples to start moving on sink start. + // Simple equation: N x stime packages in ms > 2 configured audiotrack_buffer in ms + // will result in the error condition triggering. - if (m_delay > max_delay) + const bool isRawPt = m_passthrough && !m_info.m_wantsIECPassthrough; + if (!isRawPt) { - CLog::Log(LOGERROR, "Sink got stuck with large buffer {:f} - reopening", m_delay); - return INT_MAX; + const double max_stuck_delay_ms = m_audiotrackbuffer_sec_orig * 2000.0; + const double stime_ms = 1000.0 * frames / m_format.m_sampleRate; + + if (m_stuckCounter * stime_ms > max_stuck_delay_ms) + { + CLog::Log(LOGERROR, "Sink got stuck with {:f} ms - ask AE for reopening", max_stuck_delay_ms); + usleep(max_stuck_delay_ms * 1000); + return INT_MAX; + } } // for debugging only - can be removed if everything is really stable @@ -992,6 +1021,7 @@ void CAESinkAUDIOTRACK::Drain() } m_duration_written = 0; m_headPos = 0; + m_stuckCounter = 0; m_timestampPos = 0; m_linearmovingaverage.clear(); m_stampTimer.SetExpired(); diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h index 27976b7f7f..e53e56518a 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h @@ -62,6 +62,8 @@ private: double m_duration_written; unsigned int m_min_buffer_size; uint64_t m_headPos; + uint64_t m_headPosOld; + uint32_t m_stuckCounter; uint64_t m_timestampPos = 0; // Moving Average computes the weighted average delay over // a fixed size of delay values - current size: 20 values @@ -87,6 +89,7 @@ private: unsigned int m_sink_sampleRate; bool m_passthrough; double m_audiotrackbuffer_sec; + double m_audiotrackbuffer_sec_orig; int m_encoding; double m_pause_ms = 0.0; double m_delay = 0.0; diff --git a/xbmc/cores/AudioEngine/Sinks/pipewire/Pipewire.cpp b/xbmc/cores/AudioEngine/Sinks/pipewire/Pipewire.cpp index dafa9099ee..25b5518d32 100644 --- a/xbmc/cores/AudioEngine/Sinks/pipewire/Pipewire.cpp +++ b/xbmc/cores/AudioEngine/Sinks/pipewire/Pipewire.cpp @@ -12,6 +12,7 @@ #include "PipewireCore.h" #include "PipewireRegistry.h" #include "PipewireThreadLoop.h" +#include "commons/ilog.h" #include "utils/log.h" #include <pipewire/pipewire.h> @@ -89,10 +90,18 @@ std::unique_ptr<CPipewire> CPipewire::Create() using CPipewire::CPipewire; }; - auto pipewire = std::make_unique<PipewireMaker>(); + try + { + auto pipewire = std::make_unique<PipewireMaker>(); - if (!pipewire->Start()) - return {}; + if (!pipewire->Start()) + return {}; - return pipewire; + return pipewire; + } + catch (const std::exception& e) + { + CLog::Log(LOGWARNING, "Pipewire: Exception in 'Create': {}", e.what()); + return {}; + } } diff --git a/xbmc/cores/RetroPlayer/buffers/video/RenderBufferGuiTexture.cpp b/xbmc/cores/RetroPlayer/buffers/video/RenderBufferGuiTexture.cpp index 9f8e05b641..cdd9dd6a52 100644 --- a/xbmc/cores/RetroPlayer/buffers/video/RenderBufferGuiTexture.cpp +++ b/xbmc/cores/RetroPlayer/buffers/video/RenderBufferGuiTexture.cpp @@ -75,7 +75,7 @@ void CRenderBufferGuiTexture::BindToUnit(unsigned int unit) m_texture->BindToUnit(unit); } -AVPixelFormat CRenderBufferGuiTexture::TranslateFormat(unsigned int textureFormat) +AVPixelFormat CRenderBufferGuiTexture::TranslateFormat(XB_FMT textureFormat) { switch (textureFormat) { diff --git a/xbmc/cores/RetroPlayer/buffers/video/RenderBufferGuiTexture.h b/xbmc/cores/RetroPlayer/buffers/video/RenderBufferGuiTexture.h index b78d15744a..80d7ed0a7a 100644 --- a/xbmc/cores/RetroPlayer/buffers/video/RenderBufferGuiTexture.h +++ b/xbmc/cores/RetroPlayer/buffers/video/RenderBufferGuiTexture.h @@ -37,12 +37,12 @@ public: CTexture* GetTexture() { return m_texture.get(); } protected: - AVPixelFormat TranslateFormat(unsigned int textureFormat); + AVPixelFormat TranslateFormat(XB_FMT textureFormat); TEXTURE_SCALING TranslateScalingMethod(SCALINGMETHOD scalingMethod); // Texture parameters SCALINGMETHOD m_scalingMethod; - unsigned int m_textureFormat = XB_FMT_UNKNOWN; + XB_FMT m_textureFormat = XB_FMT_UNKNOWN; std::unique_ptr<CTexture> m_texture; }; diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.cpp index 6ef8caf816..a88902bc3c 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.cpp @@ -215,8 +215,12 @@ bool CProcessorHD::InitProcessor() CLog::LogF(LOGDEBUG, "video processor has {:#x} stereo caps.", m_vcaps.StereoCaps); CLog::LogF(LOGDEBUG, "video processor has {} max input streams.", m_vcaps.MaxInputStreams); CLog::LogF(LOGDEBUG, "video processor has {} max stream states.", m_vcaps.MaxStreamStates); + if (m_vcaps.FeatureCaps & D3D11_VIDEO_PROCESSOR_FEATURE_CAPS_METADATA_HDR10) - CLog::LogF(LOGDEBUG, "video processor supports HDR10."); + { + CLog::LogF(LOGDEBUG, "video processor supports HDR10 metadata."); + m_hasMetadataHDR10Support = true; + } if (0 != (m_vcaps.FeatureCaps & D3D11_VIDEO_PROCESSOR_FEATURE_CAPS_LEGACY)) CLog::LogF(LOGWARNING, "the video driver does not support full video processing capabilities."); @@ -416,7 +420,7 @@ bool CProcessorHD::IsFormatConversionSupported(DXGI_FORMAT inputFormat, return supported == TRUE; } -bool CProcessorHD::Open(UINT width, UINT height) +bool CProcessorHD::Open(UINT width, UINT height, const VideoPicture& picture) { Close(); @@ -424,6 +428,8 @@ bool CProcessorHD::Open(UINT width, UINT height) m_width = width; m_height = height; + m_color_primaries = static_cast<AVColorPrimaries>(picture.color_primaries); + m_color_transfer = static_cast<AVColorTransferCharacteristic>(picture.color_transfer); if (!InitProcessor()) return false; @@ -473,6 +479,37 @@ bool CProcessorHD::OpenProcessor() color.YCbCr = { 0.0625f, 0.5f, 0.5f, 1.0f }; // black color m_pVideoContext->VideoProcessorSetOutputBackgroundColor(m_pVideoProcessor.Get(), TRUE, &color); + // AMD/HDR (as of 2023-06-16): processor tone maps by default and modifies high code values + // We want "passthrough" of the signal and to do our own tone mapping when needed. + // Disable the functionality by pretending that the display supports all PQ levels (0-10000) + DXGI_ADAPTER_DESC ad{}; + DX::DeviceResources::Get()->GetAdapterDesc(&ad); + bool streamIsHDR = + (m_color_primaries == AVCOL_PRI_BT2020) && + (m_color_transfer == AVCOL_TRC_SMPTE2084 || m_color_transfer == AVCOL_TRC_ARIB_STD_B67); + + if (m_hasMetadataHDR10Support && ad.VendorId == PCIV_AMD && streamIsHDR) + { + ComPtr<ID3D11VideoContext2> videoCtx2; + if (SUCCEEDED(m_pVideoContext.As(&videoCtx2))) + { + DXGI_HDR_METADATA_HDR10 hdr10{}; + hdr10.MaxMasteringLuminance = 10000; + hdr10.MinMasteringLuminance = 0; + + videoCtx2->VideoProcessorSetOutputHDRMetaData(m_pVideoProcessor.Get(), + DXGI_HDR_METADATA_TYPE_HDR10, + sizeof(DXGI_HDR_METADATA_HDR10), &hdr10); + + CLog::LogF(LOGDEBUG, "video processor tone mapping disabled."); + } + else + { + CLog::LogF(LOGDEBUG, + "unable to retrieve ID3D11VideoContext2 to disable video processor tone mapping."); + } + } + return true; } diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.h b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.h index cc280550df..6fb4e6a226 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.h +++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.h @@ -85,7 +85,7 @@ public: bool PreInit() const; void UnInit(); - bool Open(UINT width, UINT height); + bool Open(UINT width, UINT height, const VideoPicture& picture); void Close(); bool Render(CRect src, CRect dst, ID3D11Resource* target, CRenderBuffer **views, DWORD flags, UINT frameIdx, UINT rotation, float contrast, float brightness); uint8_t PastRefs() const { return m_max_back_refs; } @@ -173,6 +173,7 @@ protected: bool m_bSupportHLG = false; bool m_HDR10Left{false}; bool m_BT2020Left{false}; + bool m_hasMetadataHDR10Support{false}; struct ProcAmpInfo { @@ -186,6 +187,9 @@ protected: Microsoft::WRL::ComPtr<ID3D11VideoProcessorEnumerator1> m_pEnumerator1; Microsoft::WRL::ComPtr<ID3D11VideoProcessor> m_pVideoProcessor; + AVColorPrimaries m_color_primaries{AVCOL_PRI_UNSPECIFIED}; + AVColorTransferCharacteristic m_color_transfer{AVCOL_TRC_UNSPECIFIED}; + bool m_forced8bit{false}; bool m_superResolutionEnabled{false}; }; diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp index 09f721d8a3..bc5d84d47e 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp @@ -271,7 +271,8 @@ void CRendererBase::Render(CD3DTexture& target, const CRect& sourceRect, const C void CRendererBase::FinalOutput(CD3DTexture& source, CD3DTexture& target, const CRect& src, const CPoint(&destPoints)[4]) { - m_outputShader->Render(source, src, destPoints, target); + if (m_outputShader) + m_outputShader->Render(source, src, destPoints, target); } void CRendererBase::ManageTextures() diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.cpp index 6bbd1b13ba..e1031987dd 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.cpp @@ -136,7 +136,7 @@ bool CRendererDXVA::Configure(const VideoPicture& picture, float fps, unsigned o // create processor m_processor = std::make_unique<DXVA::CProcessorHD>(); - if (m_processor->PreInit() && m_processor->Open(m_sourceWidth, m_sourceHeight) && + if (m_processor->PreInit() && m_processor->Open(m_sourceWidth, m_sourceHeight, picture) && m_processor->IsFormatSupported(dxgi_format, support_type)) { if (CServiceBroker::GetLogging().IsLogLevelLogged(LOGDEBUG)) @@ -144,18 +144,19 @@ bool CRendererDXVA::Configure(const VideoPicture& picture, float fps, unsigned o if (m_processor->IsFormatConversionSupported(dxgi_format, dest_format, picture)) { - const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + if (DX::Windowing()->SupportsVideoSuperResolution()) + { + const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings(); - if (!settings) - return true; + if (!settings) + return true; - if (settings->GetBool(CSettings::SETTING_VIDEOPLAYER_USESUPERRESOLUTION) && - DXVA::CProcessorHD::IsSuperResolutionSuitable(picture) && - DX::Windowing()->SupportsVideoSuperResolution()) - { - m_processor->TryEnableVideoSuperResolution(); + if (settings->GetBool(CSettings::SETTING_VIDEOPLAYER_USESUPERRESOLUTION) && + CProcessorHD::IsSuperResolutionSuitable(picture)) + { + m_processor->TryEnableVideoSuperResolution(); + } } - return true; } } diff --git a/xbmc/dialogs/GUIDialogFileBrowser.cpp b/xbmc/dialogs/GUIDialogFileBrowser.cpp index a9f81cbb34..4c6122cc11 100644 --- a/xbmc/dialogs/GUIDialogFileBrowser.cpp +++ b/xbmc/dialogs/GUIDialogFileBrowser.cpp @@ -345,7 +345,9 @@ void CGUIDialogFileBrowser::Update(const std::string &strDirectory) { strSelectedItem = pItem->GetPath(); URIUtils::RemoveSlashAtEnd(strSelectedItem); - m_history.SetSelectedItem(strSelectedItem, m_Directory->GetPath().empty()?"empty":m_Directory->GetPath()); + m_history.SetSelectedItem(strSelectedItem, + m_Directory->GetPath().empty() ? "empty" : m_Directory->GetPath(), + iItem); } } diff --git a/xbmc/filesystem/DirectoryHistory.cpp b/xbmc/filesystem/DirectoryHistory.cpp index aecf285504..a5acadb523 100644 --- a/xbmc/filesystem/DirectoryHistory.cpp +++ b/xbmc/filesystem/DirectoryHistory.cpp @@ -35,7 +35,9 @@ void CDirectoryHistory::RemoveSelectedItem(const std::string& strDirectory) m_vecHistory.erase(iter); } -void CDirectoryHistory::SetSelectedItem(const std::string& strSelectedItem, const std::string& strDirectory) +void CDirectoryHistory::SetSelectedItem(const std::string& strSelectedItem, + const std::string& strDirectory, + const int indexItem) { if (strSelectedItem.empty()) return; @@ -47,12 +49,13 @@ void CDirectoryHistory::SetSelectedItem(const std::string& strSelectedItem, cons if (iter != m_vecHistory.end()) { iter->second.m_strItem = strItem; + iter->second.m_indexItem = indexItem; return; } CHistoryItem item; item.m_strItem = strItem; - item.m_strDirectory = strDir; + item.m_indexItem = indexItem; m_vecHistory[strDir] = item; } @@ -65,6 +68,15 @@ const std::string& CDirectoryHistory::GetSelectedItem(const std::string& strDire return StringUtils::Empty; } +int CDirectoryHistory::GetSelectedItemIndex(const std::string& strDirectory) const +{ + HistoryMap::const_iterator iter = m_vecHistory.find(preparePath(strDirectory)); + if (iter != m_vecHistory.end()) + return iter->second.m_indexItem; + + return -1; +} + void CDirectoryHistory::AddPath(const std::string& strPath, const std::string &strFilterPath /* = "" */) { if (!m_vecPathHistory.empty() && m_vecPathHistory.back().m_strPath == strPath) diff --git a/xbmc/filesystem/DirectoryHistory.h b/xbmc/filesystem/DirectoryHistory.h index 49f1873847..bbec135802 100644 --- a/xbmc/filesystem/DirectoryHistory.h +++ b/xbmc/filesystem/DirectoryHistory.h @@ -21,7 +21,7 @@ public: CHistoryItem() = default; virtual ~CHistoryItem() = default; std::string m_strItem; - std::string m_strDirectory; + int m_indexItem{-1}; }; class CPathHistoryItem @@ -39,8 +39,18 @@ public: CDirectoryHistory() = default; virtual ~CDirectoryHistory(); - void SetSelectedItem(const std::string& strSelectedItem, const std::string& strDirectory); + /*! + * \brief Store the currently selected item for the navigation path + * \param strSelectedItem Selected item + * \param strDirectory Path + * \param indexItem Index of the selected item (in list, after filtering/sorting). + * -1 when the index information is not available. + */ + void SetSelectedItem(const std::string& strSelectedItem, + const std::string& strDirectory, + const int indexItem = -1); const std::string& GetSelectedItem(const std::string& strDirectory) const; + int GetSelectedItemIndex(const std::string& strDirectory) const; void RemoveSelectedItem(const std::string& strDirectory); void AddPath(const std::string& strPath, const std::string &m_strFilterPath = ""); diff --git a/xbmc/filesystem/XbtFile.cpp b/xbmc/filesystem/XbtFile.cpp index 92586b7f82..3f4061ea04 100644 --- a/xbmc/filesystem/XbtFile.cpp +++ b/xbmc/filesystem/XbtFile.cpp @@ -305,11 +305,11 @@ uint32_t CXbtFile::GetImageHeight() const return frame.GetHeight(); } -uint32_t CXbtFile::GetImageFormat() const +XB_FMT CXbtFile::GetImageFormat() const { CXBTFFrame frame; if (!GetFirstFrame(frame)) - return false; + return XB_FMT_UNKNOWN; return frame.GetFormat(); } diff --git a/xbmc/filesystem/XbtFile.h b/xbmc/filesystem/XbtFile.h index fad28a508a..6da937c907 100644 --- a/xbmc/filesystem/XbtFile.h +++ b/xbmc/filesystem/XbtFile.h @@ -42,7 +42,7 @@ public: uint32_t GetImageWidth() const; uint32_t GetImageHeight() const; - uint32_t GetImageFormat() const; + XB_FMT GetImageFormat() const; bool HasImageAlpha() const; private: diff --git a/xbmc/guilib/DDSImage.cpp b/xbmc/guilib/DDSImage.cpp index 124352d74f..76b61e5f08 100644 --- a/xbmc/guilib/DDSImage.cpp +++ b/xbmc/guilib/DDSImage.cpp @@ -22,7 +22,7 @@ CDDSImage::CDDSImage() memset(&m_desc, 0, sizeof(m_desc)); } -CDDSImage::CDDSImage(unsigned int width, unsigned int height, unsigned int format) +CDDSImage::CDDSImage(unsigned int width, unsigned int height, XB_FMT format) { m_data = NULL; Allocate(width, height, format); @@ -43,10 +43,10 @@ unsigned int CDDSImage::GetHeight() const return m_desc.height; } -unsigned int CDDSImage::GetFormat() const +XB_FMT CDDSImage::GetFormat() const { if (m_desc.pixelFormat.flags & DDPF_RGB) - return 0; // Not supported + return XB_FMT_UNKNOWN; // Not supported if (m_desc.pixelFormat.flags & DDPF_FOURCC) { if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "DXT1", 4) == 0) @@ -58,7 +58,7 @@ unsigned int CDDSImage::GetFormat() const if (strncmp((const char *)&m_desc.pixelFormat.fourcc, "ARGB", 4) == 0) return XB_FMT_A8R8G8B8; } - return 0; + return XB_FMT_UNKNOWN; } unsigned int CDDSImage::GetSize() const @@ -100,7 +100,9 @@ bool CDDSImage::ReadFile(const std::string &inputFile) return true; } -unsigned int CDDSImage::GetStorageRequirements(unsigned int width, unsigned int height, unsigned int format) +unsigned int CDDSImage::GetStorageRequirements(unsigned int width, + unsigned int height, + XB_FMT format) { switch (format) { @@ -115,7 +117,7 @@ unsigned int CDDSImage::GetStorageRequirements(unsigned int width, unsigned int } } -void CDDSImage::Allocate(unsigned int width, unsigned int height, unsigned int format) +void CDDSImage::Allocate(unsigned int width, unsigned int height, XB_FMT format) { memset(&m_desc, 0, sizeof(m_desc)); m_desc.size = sizeof(m_desc); @@ -131,7 +133,7 @@ void CDDSImage::Allocate(unsigned int width, unsigned int height, unsigned int f m_data = new unsigned char[m_desc.linearSize]; } -const char *CDDSImage::GetFourCC(unsigned int format) +const char* CDDSImage::GetFourCC(XB_FMT format) { switch (format) { diff --git a/xbmc/guilib/DDSImage.h b/xbmc/guilib/DDSImage.h index 8c8bca21d3..2053990cec 100644 --- a/xbmc/guilib/DDSImage.h +++ b/xbmc/guilib/DDSImage.h @@ -8,6 +8,8 @@ #pragma once +#include "TextureFormats.h" + #include <stdint.h> #include <string> @@ -15,22 +17,24 @@ class CDDSImage { public: CDDSImage(); - CDDSImage(unsigned int width, unsigned int height, unsigned int format); + CDDSImage(unsigned int width, unsigned int height, XB_FMT format); ~CDDSImage(); unsigned int GetWidth() const; unsigned int GetHeight() const; - unsigned int GetFormat() const; + XB_FMT GetFormat() const; unsigned int GetSize() const; unsigned char *GetData() const; bool ReadFile(const std::string &file); private: - void Allocate(unsigned int width, unsigned int height, unsigned int format); - static const char *GetFourCC(unsigned int format); + void Allocate(unsigned int width, unsigned int height, XB_FMT format); + static const char* GetFourCC(XB_FMT format); - static unsigned int GetStorageRequirements(unsigned int width, unsigned int height, unsigned int format); + static unsigned int GetStorageRequirements(unsigned int width, + unsigned int height, + XB_FMT format); enum { ddsd_caps = 0x00000001, ddsd_height = 0x00000002, diff --git a/xbmc/guilib/Texture.cpp b/xbmc/guilib/Texture.cpp index 4a2f80e362..cfc506d48c 100644 --- a/xbmc/guilib/Texture.cpp +++ b/xbmc/guilib/Texture.cpp @@ -37,7 +37,7 @@ /************************************************************************/ /* */ /************************************************************************/ -CTexture::CTexture(unsigned int width, unsigned int height, unsigned int format) +CTexture::CTexture(unsigned int width, unsigned int height, XB_FMT format) { m_pixels = NULL; m_loadedToGPU = false; @@ -50,7 +50,7 @@ CTexture::~CTexture() m_pixels = NULL; } -void CTexture::Allocate(unsigned int width, unsigned int height, unsigned int format) +void CTexture::Allocate(unsigned int width, unsigned int height, XB_FMT format) { m_imageWidth = m_originalWidth = width; m_imageHeight = m_originalHeight = height; @@ -117,7 +117,7 @@ void CTexture::Allocate(unsigned int width, unsigned int height, unsigned int fo void CTexture::Update(unsigned int width, unsigned int height, unsigned int pitch, - unsigned int format, + XB_FMT format, const unsigned char* pixels, bool loadToGPU) { @@ -357,7 +357,7 @@ bool CTexture::LoadIImage(IImage* pImage, bool CTexture::LoadFromMemory(unsigned int width, unsigned int height, unsigned int pitch, - unsigned int format, + XB_FMT format, bool hasAlpha, const unsigned char* pixels) { @@ -372,7 +372,7 @@ bool CTexture::LoadFromMemory(unsigned int width, bool CTexture::LoadPaletted(unsigned int width, unsigned int height, unsigned int pitch, - unsigned int format, + XB_FMT format, const unsigned char* pixels, const COLOR* palette) { diff --git a/xbmc/guilib/Texture.h b/xbmc/guilib/Texture.h index 343e9968bf..8cb1e4b9f7 100644 --- a/xbmc/guilib/Texture.h +++ b/xbmc/guilib/Texture.h @@ -35,12 +35,12 @@ class CTexture { public: - CTexture(unsigned int width = 0, unsigned int height = 0, unsigned int format = XB_FMT_A8R8G8B8); + CTexture(unsigned int width = 0, unsigned int height = 0, XB_FMT format = XB_FMT_A8R8G8B8); virtual ~CTexture(); static std::unique_ptr<CTexture> CreateTexture(unsigned int width = 0, unsigned int height = 0, - unsigned int format = XB_FMT_A8R8G8B8); + XB_FMT format = XB_FMT_A8R8G8B8); /*! \brief Load a texture from a file Loads a texture from a file, restricting in size if needed based on maxHeight and maxWidth. @@ -73,8 +73,18 @@ public: unsigned int idealWidth = 0, unsigned int idealHeight = 0); - bool LoadFromMemory(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, bool hasAlpha, const unsigned char* pixels); - bool LoadPaletted(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, const unsigned char *pixels, const COLOR *palette); + bool LoadFromMemory(unsigned int width, + unsigned int height, + unsigned int pitch, + XB_FMT format, + bool hasAlpha, + const unsigned char* pixels); + bool LoadPaletted(unsigned int width, + unsigned int height, + unsigned int pitch, + XB_FMT format, + const unsigned char* pixels, + const COLOR* palette); bool HasAlpha() const; @@ -105,8 +115,13 @@ public: int GetOrientation() const { return m_orientation; } void SetOrientation(int orientation) { m_orientation = orientation; } - void Update(unsigned int width, unsigned int height, unsigned int pitch, unsigned int format, const unsigned char *pixels, bool loadToGPU); - void Allocate(unsigned int width, unsigned int height, unsigned int format); + void Update(unsigned int width, + unsigned int height, + unsigned int pitch, + XB_FMT format, + const unsigned char* pixels, + bool loadToGPU); + void Allocate(unsigned int width, unsigned int height, XB_FMT format); void ClampToEdge(); static unsigned int PadPow2(unsigned int x); @@ -135,7 +150,7 @@ protected: unsigned char* m_pixels; bool m_loadedToGPU; - unsigned int m_format; + XB_FMT m_format; int m_orientation; bool m_hasAlpha = true ; bool m_mipmapping = false ; diff --git a/xbmc/guilib/TextureBundle.cpp b/xbmc/guilib/TextureBundle.cpp index 491ca692ae..7e7417973a 100644 --- a/xbmc/guilib/TextureBundle.cpp +++ b/xbmc/guilib/TextureBundle.cpp @@ -44,27 +44,20 @@ std::vector<std::string> CTextureBundle::GetTexturesFromPath(const std::string& return {}; } -bool CTextureBundle::LoadTexture(const std::string& filename, - std::unique_ptr<CTexture>& texture, - int& width, - int& height) +std::optional<CTextureBundleXBT::Texture> CTextureBundle::LoadTexture(const std::string& filename) { if (m_useXBT) - return m_tbXBT.LoadTexture(filename, texture, width, height); + return m_tbXBT.LoadTexture(filename); else - return false; + return {}; } -bool CTextureBundle::LoadAnim(const std::string& filename, - std::vector<std::pair<std::unique_ptr<CTexture>, int>>& textures, - int& width, - int& height, - int& nLoops) +std::optional<CTextureBundleXBT::Animation> CTextureBundle::LoadAnim(const std::string& filename) { if (m_useXBT) - return m_tbXBT.LoadAnim(filename, textures, width, height, nLoops); + return m_tbXBT.LoadAnim(filename); else - return false; + return {}; } void CTextureBundle::Close() diff --git a/xbmc/guilib/TextureBundle.h b/xbmc/guilib/TextureBundle.h index a736192f00..8227949f9f 100644 --- a/xbmc/guilib/TextureBundle.h +++ b/xbmc/guilib/TextureBundle.h @@ -33,39 +33,18 @@ public: * \brief Load texture from bundle * * \param[in] filename name of the texture to load - * \param[out] texture holds the pointer to the texture after successful loading - * \param[out] width width of the loaded texture - * \param[out] height height of the loaded texture - * \return true if texture was loaded - * - * \todo With c++17 this should be changed to return a std::optional that's - * wrapping a struct containing the output values. Same for - * CTextureBundleXBT::LoadTexture. + * \return std::optional<CTextureBundleXBT::Texture> if texture was loaded */ - bool LoadTexture(const std::string& filename, - std::unique_ptr<CTexture>& texture, - int& width, - int& height); + std::optional<CTextureBundleXBT::Texture> LoadTexture(const std::string& filename); /*! * \brief Load animation from bundle * * \param[in] filename name of the animation to load - * \param[out] texture vector of frames. Each frame is pair of a texture and - * the duration the frame - * \param[out] width width of the loaded textures - * \param[out] height height of the loaded textures - * \return true if animation was loaded - * - * \todo With c++17 this should be changed to return a std::optional that's - * wrapping a struct containing the output values. Same for - * CTextureBundleXBT::LoadAnim. + * \return std::optional<CTextureBundleXBT::Animation> if animation was loaded */ - bool LoadAnim(const std::string& filename, - std::vector<std::pair<std::unique_ptr<CTexture>, int>>& textures, - int& width, - int& height, - int& nLoops); + std::optional<CTextureBundleXBT::Animation> LoadAnim(const std::string& filename); + void Close(); private: CTextureBundleXBT m_tbXBT; diff --git a/xbmc/guilib/TextureBundleXBT.cpp b/xbmc/guilib/TextureBundleXBT.cpp index 981ddad825..f557d28a26 100644 --- a/xbmc/guilib/TextureBundleXBT.cpp +++ b/xbmc/guilib/TextureBundleXBT.cpp @@ -148,71 +148,67 @@ std::vector<std::string> CTextureBundleXBT::GetTexturesFromPath(const std::strin return textures; } -bool CTextureBundleXBT::LoadTexture(const std::string& filename, - std::unique_ptr<CTexture>& texture, - int& width, - int& height) +std::optional<CTextureBundleXBT::Texture> CTextureBundleXBT::LoadTexture( + const std::string& filename) { std::string name = Normalize(filename); CXBTFFile file; if (!m_XBTFReader->Get(name, file)) - return false; + return {}; if (file.GetFrames().empty()) - return false; + return {}; const CXBTFFrame& frame = file.GetFrames().at(0); - if (!ConvertFrameToTexture(filename, frame, texture)) - { - return false; - } - width = frame.GetWidth(); - height = frame.GetHeight(); + Texture texture; + texture.width = frame.GetWidth(); + texture.height = frame.GetHeight(); - return true; + texture.texture = ConvertFrameToTexture(filename, frame); + if (!texture.texture) + return {}; + + return std::make_optional<Texture>(std::move(texture)); } -bool CTextureBundleXBT::LoadAnim(const std::string& filename, - std::vector<std::pair<std::unique_ptr<CTexture>, int>>& textures, - int& width, - int& height, - int& nLoops) +std::optional<CTextureBundleXBT::Animation> CTextureBundleXBT::LoadAnim(const std::string& filename) { std::string name = Normalize(filename); CXBTFFile file; if (!m_XBTFReader->Get(name, file)) - return false; + return {}; if (file.GetFrames().empty()) - return false; + return {}; size_t nTextures = file.GetFrames().size(); - textures.reserve(nTextures); + + Animation animation; + animation.textures.reserve(nTextures); for (size_t i = 0; i < nTextures; i++) { CXBTFFrame& frame = file.GetFrames().at(i); - std::unique_ptr<CTexture> texture; - if (!ConvertFrameToTexture(filename, frame, texture)) - return false; + std::unique_ptr<CTexture> texture = ConvertFrameToTexture(filename, frame); + if (!texture) + return {}; - textures.emplace_back(std::move(texture), frame.GetDuration()); + animation.textures.emplace_back(std::move(texture), frame.GetDuration()); } - width = file.GetFrames().at(0).GetWidth(); - height = file.GetFrames().at(0).GetHeight(); - nLoops = file.GetLoop(); + animation.width = file.GetFrames().at(0).GetWidth(); + animation.height = file.GetFrames().at(0).GetHeight(); + animation.loops = file.GetLoop(); - return true; + return std::make_optional<Animation>(std::move(animation)); } -bool CTextureBundleXBT::ConvertFrameToTexture(const std::string& name, - const CXBTFFrame& frame, - std::unique_ptr<CTexture>& texture) +std::unique_ptr<CTexture> CTextureBundleXBT::ConvertFrameToTexture(const std::string& name, + const CXBTFFrame& frame) { // found texture - allocate the necessary buffers std::vector<unsigned char> buffer(static_cast<size_t>(frame.GetPackedSize())); @@ -221,7 +217,7 @@ bool CTextureBundleXBT::ConvertFrameToTexture(const std::string& name, if (!m_XBTFReader->Load(frame, buffer.data())) { CLog::Log(LOGERROR, "Error loading texture: {}", name); - return false; + return {}; } // check if it's packed with lzo @@ -234,17 +230,17 @@ bool CTextureBundleXBT::ConvertFrameToTexture(const std::string& name, s != frame.GetUnpackedSize()) { CLog::Log(LOGERROR, "Error loading texture: {}: Decompression error", name); - return false; + return {}; } buffer = std::move(unpacked); } // create an xbmc texture - texture = CTexture::CreateTexture(); + std::unique_ptr<CTexture> texture = CTexture::CreateTexture(); texture->LoadFromMemory(frame.GetWidth(), frame.GetHeight(), 0, frame.GetFormat(), frame.HasAlpha(), buffer.data()); - return true; + return texture; } void CTextureBundleXBT::SetThemeBundle(bool themeBundle) diff --git a/xbmc/guilib/TextureBundleXBT.h b/xbmc/guilib/TextureBundleXBT.h index 915b52bd60..304605607f 100644 --- a/xbmc/guilib/TextureBundleXBT.h +++ b/xbmc/guilib/TextureBundleXBT.h @@ -8,14 +8,16 @@ #pragma once +#include "Texture.h" + #include <cstdint> #include <ctime> #include <memory> +#include <optional> #include <string> #include <utility> #include <vector> -class CTexture; class CXBTFReader; class CXBTFFrame; @@ -31,22 +33,30 @@ public: std::vector<std::string> GetTexturesFromPath(const std::string& path); static std::string Normalize(std::string name); + struct Texture + { + std::unique_ptr<CTexture> texture; + int width; + int height; + }; + /*! * \brief See CTextureBundle::LoadTexture */ - bool LoadTexture(const std::string& filename, - std::unique_ptr<CTexture>& texture, - int& width, - int& height); + std::optional<Texture> LoadTexture(const std::string& filename); + + struct Animation + { + std::vector<std::pair<std::unique_ptr<CTexture>, int>> textures; + int width; + int height; + int loops; + }; /*! * \brief See CTextureBundle::LoadAnim */ - bool LoadAnim(const std::string& filename, - std::vector<std::pair<std::unique_ptr<CTexture>, int>>& textures, - int& width, - int& height, - int& nLoops); + std::optional<Animation> LoadAnim(const std::string& filename); //! @todo Change return to std::optional<std::vector<uint8_t>>> when c++17 is allowed static std::vector<uint8_t> UnpackFrame(const CXBTFReader& reader, const CXBTFFrame& frame); @@ -55,9 +65,7 @@ public: private: bool OpenBundle(); - bool ConvertFrameToTexture(const std::string& name, - const CXBTFFrame& frame, - std::unique_ptr<CTexture>& texture); + std::unique_ptr<CTexture> ConvertFrameToTexture(const std::string& name, const CXBTFFrame& frame); time_t m_TimeStamp; diff --git a/xbmc/guilib/TextureDX.cpp b/xbmc/guilib/TextureDX.cpp index 1690adde38..10ffcc65fc 100644 --- a/xbmc/guilib/TextureDX.cpp +++ b/xbmc/guilib/TextureDX.cpp @@ -15,12 +15,12 @@ std::unique_ptr<CTexture> CTexture::CreateTexture(unsigned int width, unsigned int height, - unsigned int format) + XB_FMT format) { return std::make_unique<CDXTexture>(width, height, format); } -CDXTexture::CDXTexture(unsigned int width, unsigned int height, unsigned int format) +CDXTexture::CDXTexture(unsigned int width, unsigned int height, XB_FMT format) : CTexture(width, height, format) { } diff --git a/xbmc/guilib/TextureDX.h b/xbmc/guilib/TextureDX.h index 0858780df2..6d0c99abe9 100644 --- a/xbmc/guilib/TextureDX.h +++ b/xbmc/guilib/TextureDX.h @@ -17,7 +17,7 @@ class CDXTexture : public CTexture { public: - CDXTexture(unsigned int width = 0, unsigned int height = 0, unsigned int format = XB_FMT_UNKNOWN); + CDXTexture(unsigned int width = 0, unsigned int height = 0, XB_FMT format = XB_FMT_UNKNOWN); virtual ~CDXTexture(); void CreateTextureObject(); diff --git a/xbmc/guilib/TextureFormats.h b/xbmc/guilib/TextureFormats.h index 95fff02092..2aab71a947 100644 --- a/xbmc/guilib/TextureFormats.h +++ b/xbmc/guilib/TextureFormats.h @@ -8,15 +8,21 @@ #pragma once -#define XB_FMT_MASK 0xffff ///< mask for format info - other flags are outside this -#define XB_FMT_DXT_MASK 15 -#define XB_FMT_UNKNOWN 0 -#define XB_FMT_DXT1 1 -#define XB_FMT_DXT3 2 -#define XB_FMT_DXT5 4 -#define XB_FMT_DXT5_YCoCg 8 -#define XB_FMT_A8R8G8B8 16 // texture.xbt byte order (matches BGRA8) -#define XB_FMT_A8 32 -#define XB_FMT_RGBA8 64 -#define XB_FMT_RGB8 128 -#define XB_FMT_OPAQUE 65536 +// clang-format off +enum XB_FMT +{ + XB_FMT_UNKNOWN = 0x0, + XB_FMT_DXT1 = 0x1, + XB_FMT_DXT3 = 0x2, + XB_FMT_DXT5 = 0x4, + XB_FMT_DXT5_YCoCg = 0x8, + XB_FMT_DXT_MASK = 0xF, + + XB_FMT_A8R8G8B8 = 0x10, // texture.xbt byte order (matches BGRA8) + XB_FMT_A8 = 0x20, + XB_FMT_RGBA8 = 0x40, + XB_FMT_RGB8 = 0x80, + XB_FMT_MASK = 0xFFFF, + XB_FMT_OPAQUE = 0x10000, +}; +// clang-format on diff --git a/xbmc/guilib/TextureGL.cpp b/xbmc/guilib/TextureGL.cpp index ef01bdf540..a2cf428b23 100644 --- a/xbmc/guilib/TextureGL.cpp +++ b/xbmc/guilib/TextureGL.cpp @@ -20,12 +20,12 @@ std::unique_ptr<CTexture> CTexture::CreateTexture(unsigned int width, unsigned int height, - unsigned int format) + XB_FMT format) { return std::make_unique<CGLTexture>(width, height, format); } -CGLTexture::CGLTexture(unsigned int width, unsigned int height, unsigned int format) +CGLTexture::CGLTexture(unsigned int width, unsigned int height, XB_FMT format) : CTexture(width, height, format) { unsigned int major, minor; diff --git a/xbmc/guilib/TextureGL.h b/xbmc/guilib/TextureGL.h index 51c7035de6..58d219b622 100644 --- a/xbmc/guilib/TextureGL.h +++ b/xbmc/guilib/TextureGL.h @@ -18,7 +18,7 @@ class CGLTexture : public CTexture { public: - CGLTexture(unsigned int width = 0, unsigned int height = 0, unsigned int format = XB_FMT_A8R8G8B8); + CGLTexture(unsigned int width = 0, unsigned int height = 0, XB_FMT format = XB_FMT_A8R8G8B8); ~CGLTexture() override; void CreateTextureObject() override; diff --git a/xbmc/guilib/TextureManager.cpp b/xbmc/guilib/TextureManager.cpp index ddf2e77e7f..ae002d0f35 100644 --- a/xbmc/guilib/TextureManager.cpp +++ b/xbmc/guilib/TextureManager.cpp @@ -340,19 +340,22 @@ const CTextureArray& CGUITextureManager::Load(const std::string& strTextureName, if (bundle >= 0 && StringUtils::EndsWithNoCase(strPath, ".gif")) { CTextureMap* pMap = nullptr; - std::vector<std::pair<std::unique_ptr<CTexture>, int>> textures; - int nLoops = 0, width = 0, height = 0; - bool success = m_TexBundle[bundle].LoadAnim(strTextureName, textures, width, height, nLoops); - if (!success) + std::optional<CTextureBundleXBT::Animation> animation = + m_TexBundle[bundle].LoadAnim(strTextureName); + if (!animation) { CLog::Log(LOGERROR, "Texture manager unable to load bundled file: {}", strTextureName); return emptyTexture; } + int nLoops = animation.value().loops; + int width = animation.value().width; + int height = animation.value().height; + unsigned int maxWidth = 0; unsigned int maxHeight = 0; pMap = new CTextureMap(strTextureName, width, height, nLoops); - for (auto& texture : textures) + for (auto& texture : animation.value().textures) { maxWidth = std::max(maxWidth, texture.first->GetWidth()); maxHeight = std::max(maxHeight, texture.first->GetHeight()); @@ -428,11 +431,17 @@ const CTextureArray& CGUITextureManager::Load(const std::string& strTextureName, int width = 0, height = 0; if (bundle >= 0) { - if (!m_TexBundle[bundle].LoadTexture(strTextureName, pTexture, width, height)) + std::optional<CTextureBundleXBT::Texture> texture = + m_TexBundle[bundle].LoadTexture(strTextureName); + if (!texture) { CLog::Log(LOGERROR, "Texture manager unable to load bundled file: {}", strTextureName); return emptyTexture; } + + pTexture = std::move(texture.value().texture); + width = texture.value().width; + height = texture.value().height; } else { diff --git a/xbmc/guilib/XBTF.cpp b/xbmc/guilib/XBTF.cpp index aa095ccb5f..fb89c97889 100644 --- a/xbmc/guilib/XBTF.cpp +++ b/xbmc/guilib/XBTF.cpp @@ -72,14 +72,14 @@ void CXBTFFrame::SetUnpackedSize(uint64_t size) m_unpackedSize = size; } -void CXBTFFrame::SetFormat(uint32_t format) +void CXBTFFrame::SetFormat(XB_FMT format) { m_format = format; } -uint32_t CXBTFFrame::GetFormat(bool raw) const +XB_FMT CXBTFFrame::GetFormat(bool raw) const { - return raw ? m_format : (m_format & XB_FMT_MASK); + return raw ? m_format : static_cast<XB_FMT>(m_format & XB_FMT_MASK); } uint64_t CXBTFFrame::GetOffset() const diff --git a/xbmc/guilib/XBTF.h b/xbmc/guilib/XBTF.h index 382943902b..588ba6234d 100644 --- a/xbmc/guilib/XBTF.h +++ b/xbmc/guilib/XBTF.h @@ -28,8 +28,8 @@ public: uint32_t GetWidth() const; void SetWidth(uint32_t width); - uint32_t GetFormat(bool raw = false) const; - void SetFormat(uint32_t format); + XB_FMT GetFormat(bool raw = false) const; + void SetFormat(XB_FMT format); uint32_t GetHeight() const; void SetHeight(uint32_t height); @@ -54,7 +54,7 @@ public: private: uint32_t m_width; uint32_t m_height; - uint32_t m_format; + XB_FMT m_format; uint64_t m_packedSize; uint64_t m_unpackedSize; uint64_t m_offset; diff --git a/xbmc/guilib/XBTFReader.cpp b/xbmc/guilib/XBTFReader.cpp index cca8e834c1..6d74f46b0d 100644 --- a/xbmc/guilib/XBTFReader.cpp +++ b/xbmc/guilib/XBTFReader.cpp @@ -134,7 +134,7 @@ bool CXBTFReader::Open(const std::string& path) if (!ReadUInt32(m_file, u32)) return false; - frame.SetFormat(u32); + frame.SetFormat(static_cast<XB_FMT>(u32)); if (!ReadUInt64(m_file, u64)) return false; diff --git a/xbmc/music/windows/GUIWindowMusicPlaylist.cpp b/xbmc/music/windows/GUIWindowMusicPlaylist.cpp index 87afafa240..ed256b58a7 100644 --- a/xbmc/music/windows/GUIWindowMusicPlaylist.cpp +++ b/xbmc/music/windows/GUIWindowMusicPlaylist.cpp @@ -325,7 +325,7 @@ void CGUIWindowMusicPlayList::SavePlayList() } std::string strOldDirectory = m_vecItems->GetPath(); - m_history.SetSelectedItem(strSelectedItem, strOldDirectory); + m_history.SetSelectedItem(strSelectedItem, strOldDirectory, iItem); PLAYLIST::CPlayListM3U playlist; for (int i = 0; i < m_vecItems->Size(); ++i) diff --git a/xbmc/platform/linux/threads/ThreadImplLinux.cpp b/xbmc/platform/linux/threads/ThreadImplLinux.cpp index f4810cfe5c..8c7edbb591 100644 --- a/xbmc/platform/linux/threads/ThreadImplLinux.cpp +++ b/xbmc/platform/linux/threads/ThreadImplLinux.cpp @@ -13,7 +13,7 @@ #include <algorithm> #include <array> -#include <limits.h> +#include <mutex> #include <sys/resource.h> #include <unistd.h> @@ -61,57 +61,11 @@ static pid_t gettid() #endif #endif -} // namespace - -static int s_maxPriority; -static bool s_maxPriorityIsSet{false}; - -// We need to return what the best number than can be passed -// to SetPriority is. It will basically be relative to the -// the main thread's nice level, inverted (since "higher" priority -// nice levels are actually lower numbers). -static int GetUserMaxPriority(int maxPriority) -{ - if (s_maxPriorityIsSet) - return s_maxPriority; +std::once_flag flag; - // if we're root, then we can do anything. So we'll allow - // max priority. - if (geteuid() == 0) - return maxPriority; +} // namespace - // get user max prio - struct rlimit limit; - if (getrlimit(RLIMIT_NICE, &limit) != 0) - { - // If we fail getting the limit for nice we just assume we can't raise the priority - return 0; - } - - const int appNice = getpriority(PRIO_PROCESS, getpid()); - const int rlimVal = limit.rlim_cur; - - // according to the docs, limit.rlim_cur shouldn't be zero, yet, here we are. - // if a user has no entry in limits.conf rlim_cur is zero. In this case the best - // nice value we can hope to achieve is '0' as a regular user - const int userBestNiceValue = (rlimVal == 0) ? 0 : (20 - rlimVal); - - // running the app with nice -n 10 -> - // e.g. +10 10 - 0 // default non-root user. - // e.g. +30 10 - -20 // if root with rlimits set. - // running the app default -> - // e.g. 0 0 - 0 // default non-root user. - // e.g. +20 0 - -20 // if root with rlimits set. - const int bestUserSetPriority = appNice - userBestNiceValue; // nice is inverted from prio. - - // static because we only need to check this once. - // we shouldn't expect a user to change RLIMIT_NICE while running - // and it won't work anyway for threads that already set their priority. - s_maxPriority = std::min(maxPriority, bestUserSetPriority); - s_maxPriorityIsSet = true; - - return s_maxPriority; -} +static int s_appPriority = getpriority(PRIO_PROCESS, getpid()); std::unique_ptr<IThreadImpl> IThreadImpl::CreateThreadImpl(std::thread::native_handle_type handle) { @@ -129,43 +83,23 @@ void CThreadImplLinux::SetThreadInfo(const std::string& name) pthread_setname_np(m_handle, name.c_str()); #endif - // get user max prio - const int maxPrio = ThreadPriorityToNativePriority(ThreadPriority::HIGHEST); - const int userMaxPrio = GetUserMaxPriority(maxPrio); - - // if the user does not have an entry in limits.conf the following - // call will fail - if (userMaxPrio > 0) - { - // start thread with nice level of application - const int appNice = getpriority(PRIO_PROCESS, getpid()); - if (setpriority(PRIO_PROCESS, m_threadID, appNice) != 0) - CLog::Log(LOGERROR, "[threads] failed to set priority: {}", strerror(errno)); - } + m_name = name; } bool CThreadImplLinux::SetPriority(const ThreadPriority& priority) { - // keep priority in bounds + std::call_once(flag, + []() { CLog::Log(LOGDEBUG, "[threads] app priority: '{}'", s_appPriority); }); + const int prio = ThreadPriorityToNativePriority(priority); - const int maxPrio = ThreadPriorityToNativePriority(ThreadPriority::HIGHEST); - const int minPrio = ThreadPriorityToNativePriority(ThreadPriority::LOWEST); - // get user max prio given max prio (will take the min) - const int userMaxPrio = GetUserMaxPriority(maxPrio); + const int newPriority = s_appPriority - prio; - // clamp to min and max priorities - const int adjustedPrio = std::clamp(prio, minPrio, userMaxPrio); + setpriority(PRIO_PROCESS, m_threadID, newPriority); - // nice level of application - const int appNice = getpriority(PRIO_PROCESS, getpid()); - const int newNice = appNice - adjustedPrio; + const int actualPriority = getpriority(PRIO_PROCESS, m_threadID); - if (setpriority(PRIO_PROCESS, m_threadID, newNice) != 0) - { - CLog::Log(LOGERROR, "[threads] failed to set priority: {}", strerror(errno)); - return false; - } + CLog::Log(LOGDEBUG, "[threads] name: '{}' priority: '{}'", m_name, actualPriority); return true; } diff --git a/xbmc/platform/linux/threads/ThreadImplLinux.h b/xbmc/platform/linux/threads/ThreadImplLinux.h index 986ffe5ef3..2307b3f5cb 100644 --- a/xbmc/platform/linux/threads/ThreadImplLinux.h +++ b/xbmc/platform/linux/threads/ThreadImplLinux.h @@ -23,4 +23,5 @@ public: private: pid_t m_threadID; + std::string m_name; }; diff --git a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp index afc33543e5..f7866278e7 100644 --- a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp +++ b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp @@ -445,6 +445,23 @@ void CGUIDialogPVRTimerSettings::OnSettingChanged(const std::shared_ptr<const CS { m_timerType = it->second; + // reset certain settings to the defaults of the new timer type + + if (m_timerType->SupportsPriority()) + m_iPriority = m_timerType->GetPriorityDefault(); + + if (m_timerType->SupportsLifetime()) + m_iLifetime = m_timerType->GetLifetimeDefault(); + + if (m_timerType->SupportsMaxRecordings()) + m_iMaxRecordings = m_timerType->GetMaxRecordingsDefault(); + + if (m_timerType->SupportsRecordingGroup()) + m_iRecordingGroup = m_timerType->GetRecordingGroupDefault(); + + if (m_timerType->SupportsRecordOnlyNewEpisodes()) + m_iPreventDupEpisodes = m_timerType->GetPreventDuplicateEpisodesDefault(); + if (m_timerType->IsTimerRule() && (m_iWeekdays == PVR_WEEKDAY_ALLDAYS)) SetButtonLabels(); // update "Any day" vs. "Every day" } diff --git a/xbmc/settings/DisplaySettings.cpp b/xbmc/settings/DisplaySettings.cpp index 7611719b10..42f7ba3469 100644 --- a/xbmc/settings/DisplaySettings.cpp +++ b/xbmc/settings/DisplaySettings.cpp @@ -797,8 +797,8 @@ void CDisplaySettings::SettingOptionsResolutionsFiller(const SettingConstPtr& se for (std::vector<RESOLUTION_WHR>::const_iterator resolution = resolutions.begin(); resolution != resolutions.end(); ++resolution) { std::string resLabel = - !resolution->id.empty() - ? resolution->id + !resolution->label.empty() + ? resolution->label : StringUtils::Format("{}x{}{}", resolution->width, resolution->height, ModeFlagsToString(resolution->flags, false)); list.emplace_back(resLabel, resolution->ResInfo_Index); diff --git a/xbmc/video/VideoUtils.cpp b/xbmc/video/VideoUtils.cpp index a0d31032d7..808cd61fac 100644 --- a/xbmc/video/VideoUtils.cpp +++ b/xbmc/video/VideoUtils.cpp @@ -186,6 +186,20 @@ void CAsyncGetItemsForPlaylist::GetItemsForPlaylist(const std::shared_ptr<CFileI items.Sort(sortDesc); } + if (items.GetContent().empty() && !items.IsVideoDb() && !items.IsVirtualDirectoryRoot() && + !items.IsSourcesPath() && !items.IsLibraryFolder()) + { + CVideoDatabase db; + if (db.Open()) + { + std::string content = db.GetContentForPath(items.GetPath()); + if (content.empty() && !items.IsPlugin()) + content = "files"; + + items.SetContent(content); + } + } + if (m_resume) { // put last played item at the begin of the playlist; add start offsets for videos @@ -227,6 +241,7 @@ void CAsyncGetItemsForPlaylist::GetItemsForPlaylist(const std::shared_ptr<CFileI const bool unwatchedOnly = watchedMode == WatchedModeUnwatched; const bool watchedOnly = watchedMode == WatchedModeWatched; + bool fetchedPlayCounts = false; for (const auto& i : items) { if (i->m_bIsFolder) @@ -236,11 +251,25 @@ void CAsyncGetItemsForPlaylist::GetItemsForPlaylist(const std::shared_ptr<CFileI if (StringUtils::EndsWithNoCase(path, "sample")) // skip sample folders continue; } - else if (i->HasVideoInfoTag() && - ((unwatchedOnly && i->GetVideoInfoTag()->GetPlayCount() > 0) || - (watchedOnly && i->GetVideoInfoTag()->GetPlayCount() <= 0))) - continue; - + else + { + if (!fetchedPlayCounts && + (!i->HasVideoInfoTag() || !i->GetVideoInfoTag()->IsPlayCountSet())) + { + CVideoDatabase db; + if (db.Open()) + { + fetchedPlayCounts = true; + db.GetPlayCounts(items.GetPath(), items); + } + } + if (i->HasVideoInfoTag() && i->GetVideoInfoTag()->IsPlayCountSet()) + { + const int playCount = i->GetVideoInfoTag()->GetPlayCount(); + if ((unwatchedOnly && playCount > 0) || (watchedOnly && playCount <= 0)) + continue; + } + } GetItemsForPlaylist(i); } } diff --git a/xbmc/windowing/Resolution.cpp b/xbmc/windowing/Resolution.cpp index 951ec08035..e4fdae74dc 100644 --- a/xbmc/windowing/Resolution.cpp +++ b/xbmc/windowing/Resolution.cpp @@ -53,7 +53,8 @@ RESOLUTION_INFO::RESOLUTION_INFO(const RESOLUTION_INFO& res) guiInsets(res.guiInsets), strMode(res.strMode), strOutput(res.strOutput), - strId(res.strId) + strId(res.strId), + label(res.label) { bFullScreen = res.bFullScreen; iWidth = res.iWidth; iHeight = res.iHeight; diff --git a/xbmc/windowing/Resolution.h b/xbmc/windowing/Resolution.h index b66202ea71..36fef9406b 100644 --- a/xbmc/windowing/Resolution.h +++ b/xbmc/windowing/Resolution.h @@ -110,6 +110,12 @@ struct RESOLUTION_INFO //!< Resolution ID std::string strId; + //! @brief Resolution label + //! @note This label is shown to the user (display settings) and takes precedence over the computation of the label based on the internal properties. + //! e.g. sometimes, as the example of HiDPI resolutions, it is preferable to show a custom label/string instead of the one computed using the width/height + //! of the resolution + std::string label; + public: RESOLUTION_INFO(int width = 1280, int height = 720, float aspect = 0, const std::string &mode = ""); float DisplayRatio() const; diff --git a/xbmc/windowing/WinSystem.cpp b/xbmc/windowing/WinSystem.cpp index a99ca2a084..2da986affb 100644 --- a/xbmc/windowing/WinSystem.cpp +++ b/xbmc/windowing/WinSystem.cpp @@ -106,26 +106,29 @@ static void AddResolution(std::vector<RESOLUTION_WHR> &resolutions, unsigned int int width = resInfo.iScreenWidth; int height = resInfo.iScreenHeight; int flags = resInfo.dwFlags & D3DPRESENTFLAG_MODEMASK; - std::string id = resInfo.strId; + const std::string id = resInfo.strId; + const std::string label = resInfo.label; float refreshrate = resInfo.fRefreshRate; // don't touch RES_DESKTOP - for (unsigned int idx = 1; idx < resolutions.size(); idx++) - if (resolutions[idx].width == width && resolutions[idx].height == height && - (resolutions[idx].flags & D3DPRESENTFLAG_MODEMASK) == flags && resolutions[idx].id == id) + for (auto& resolution : resolutions) + { + if (resolution.width == width && resolution.height == height && + (resolution.flags & D3DPRESENTFLAG_MODEMASK) == flags && resolution.label == label) { // check if the refresh rate of this resolution is better suited than // the refresh rate of the resolution with the same width/height/interlaced // property and if so replace it if (bestRefreshrate > 0.0f && refreshrate == bestRefreshrate) - resolutions[idx].ResInfo_Index = addindex; + resolution.ResInfo_Index = addindex; // no need to add the resolution again return; } + } - RESOLUTION_WHR res = {width, height, flags, static_cast<int>(addindex), id}; - resolutions.push_back(res); + RESOLUTION_WHR res = {width, height, flags, static_cast<int>(addindex), id, label}; + resolutions.emplace_back(res); } static bool resSortPredicate(RESOLUTION_WHR i, RESOLUTION_WHR j) diff --git a/xbmc/windowing/WinSystem.h b/xbmc/windowing/WinSystem.h index b6be4819ab..2baa7a6c61 100644 --- a/xbmc/windowing/WinSystem.h +++ b/xbmc/windowing/WinSystem.h @@ -28,6 +28,7 @@ struct RESOLUTION_WHR int flags; //< only D3DPRESENTFLAG_MODEMASK flags int ResInfo_Index; std::string id; + std::string label; }; struct REFRESHRATE diff --git a/xbmc/windowing/osx/WinSystemOSX.mm b/xbmc/windowing/osx/WinSystemOSX.mm index 637af92e19..5b87e6195b 100644 --- a/xbmc/windowing/osx/WinSystemOSX.mm +++ b/xbmc/windowing/osx/WinSystemOSX.mm @@ -1111,6 +1111,7 @@ void CWinSystemOSX::FillInVideoModes() if (disp == dispIdx) { res.strId = ComputeVideoModeId(resWidth, resHeight, pixelWidth, pixelHeight, interlaced); + res.label = res.strId; UpdateDesktopResolution(res, (dispName != nil) ? dispName.UTF8String : "Unknown", static_cast<int>(pixelWidth), static_cast<int>(pixelHeight), refreshrate, 0); @@ -1139,6 +1140,7 @@ void CWinSystemOSX::UpdateResolutions() resInfo.strId = ComputeVideoModeId(screenResolution.resWidth, screenResolution.resHeight, screenResolution.pixelWidth, screenResolution.pixelHeight, screenResolution.interlaced); + resInfo.label = resInfo.strId; UpdateDesktopResolution( resInfo, dispName.UTF8String, static_cast<int>(screenResolution.pixelWidth), static_cast<int>(screenResolution.pixelHeight), screenResolution.refreshrate, 0); diff --git a/xbmc/windows/GUIMediaWindow.cpp b/xbmc/windows/GUIMediaWindow.cpp index babb9d9cf1..8a95881c85 100644 --- a/xbmc/windows/GUIMediaWindow.cpp +++ b/xbmc/windows/GUIMediaWindow.cpp @@ -1310,7 +1310,7 @@ void CGUIMediaWindow::SaveSelectedItemInHistory() GetDirectoryHistoryString(pItem.get(), strSelectedItem); } - m_history.SetSelectedItem(strSelectedItem, m_vecItems->GetPath()); + m_history.SetSelectedItem(strSelectedItem, m_vecItems->GetPath(), iItem); } void CGUIMediaWindow::RestoreSelectedItemFromHistory() @@ -1333,7 +1333,17 @@ void CGUIMediaWindow::RestoreSelectedItemFromHistory() } } - // if we haven't found the selected item, select the first item + // Exact item not found - maybe deleted, watched status change, filtered out, ... + // Attempt to restore the position of the selection + int selectedItemIndex = m_history.GetSelectedItemIndex(m_vecItems->GetPath()); + if (selectedItemIndex >= 0 && m_vecItems->Size() > 0) + { + int newIndex = std::min(selectedItemIndex, m_vecItems->Size() - 1); + m_viewControl.SetSelectedItem(newIndex); + return; + } + + // Fallback: select the first item m_viewControl.SetSelectedItem(0); } diff --git a/xbmc/windows/GUIWindowFileManager.cpp b/xbmc/windows/GUIWindowFileManager.cpp index ef5cd3590e..1b1ac0113b 100644 --- a/xbmc/windows/GUIWindowFileManager.cpp +++ b/xbmc/windows/GUIWindowFileManager.cpp @@ -460,7 +460,7 @@ bool CGUIWindowFileManager::Update(int iList, const std::string &strDirectory) if (!pItem->IsParentFolder()) { GetDirectoryHistoryString(pItem.get(), strSelectedItem); - m_history[iList].SetSelectedItem(strSelectedItem, m_Directory[iList]->GetPath()); + m_history[iList].SetSelectedItem(strSelectedItem, m_Directory[iList]->GetPath(), iItem); } } |