aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/skin.estuary/xml/Custom_1109_TopBarOverlay.xml4
-rw-r--r--addons/skin.estuary/xml/Timers.xml6
-rw-r--r--system/shaders/output_d3d.fx35
-rw-r--r--tools/depends/native/TexturePacker/src/TexturePacker.cpp4
-rw-r--r--tools/depends/target/hwdata/Makefile2
-rw-r--r--tools/depends/target/libdisplay-info/Makefile2
-rw-r--r--xbmc/addons/gui/skin/SkinTimerManager.cpp3
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp40
-rw-r--r--xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h3
-rw-r--r--xbmc/cores/AudioEngine/Sinks/pipewire/Pipewire.cpp17
-rw-r--r--xbmc/cores/RetroPlayer/buffers/video/RenderBufferGuiTexture.cpp2
-rw-r--r--xbmc/cores/RetroPlayer/buffers/video/RenderBufferGuiTexture.h4
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.cpp41
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/DXVAHD.h6
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererBase.cpp3
-rw-r--r--xbmc/cores/VideoPlayer/VideoRenderers/windows/RendererDXVA.cpp21
-rw-r--r--xbmc/dialogs/GUIDialogFileBrowser.cpp4
-rw-r--r--xbmc/filesystem/DirectoryHistory.cpp16
-rw-r--r--xbmc/filesystem/DirectoryHistory.h14
-rw-r--r--xbmc/filesystem/XbtFile.cpp4
-rw-r--r--xbmc/filesystem/XbtFile.h2
-rw-r--r--xbmc/guilib/DDSImage.cpp16
-rw-r--r--xbmc/guilib/DDSImage.h14
-rw-r--r--xbmc/guilib/Texture.cpp10
-rw-r--r--xbmc/guilib/Texture.h29
-rw-r--r--xbmc/guilib/TextureBundle.cpp19
-rw-r--r--xbmc/guilib/TextureBundle.h31
-rw-r--r--xbmc/guilib/TextureBundleXBT.cpp68
-rw-r--r--xbmc/guilib/TextureBundleXBT.h34
-rw-r--r--xbmc/guilib/TextureDX.cpp4
-rw-r--r--xbmc/guilib/TextureDX.h2
-rw-r--r--xbmc/guilib/TextureFormats.h30
-rw-r--r--xbmc/guilib/TextureGL.cpp4
-rw-r--r--xbmc/guilib/TextureGL.h2
-rw-r--r--xbmc/guilib/TextureManager.cpp21
-rw-r--r--xbmc/guilib/XBTF.cpp6
-rw-r--r--xbmc/guilib/XBTF.h6
-rw-r--r--xbmc/guilib/XBTFReader.cpp2
-rw-r--r--xbmc/music/windows/GUIWindowMusicPlaylist.cpp2
-rw-r--r--xbmc/platform/linux/threads/ThreadImplLinux.cpp90
-rw-r--r--xbmc/platform/linux/threads/ThreadImplLinux.h1
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp17
-rw-r--r--xbmc/settings/DisplaySettings.cpp4
-rw-r--r--xbmc/video/VideoUtils.cpp39
-rw-r--r--xbmc/windowing/Resolution.cpp3
-rw-r--r--xbmc/windowing/Resolution.h6
-rw-r--r--xbmc/windowing/WinSystem.cpp17
-rw-r--r--xbmc/windowing/WinSystem.h1
-rw-r--r--xbmc/windowing/osx/WinSystemOSX.mm2
-rw-r--r--xbmc/windows/GUIMediaWindow.cpp14
-rw-r--r--xbmc/windows/GUIWindowFileManager.cpp2
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);
}
}