[videodb] remove unused seasons table from episode_view
[xbmc.git] / xbmc / windowing / Resolution.cpp
blob951ec08035377903a180764bdd9c6c973986961a
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "Resolution.h"
11 #include "GraphicContext.h"
12 #include "ServiceBroker.h"
13 #include "settings/AdvancedSettings.h"
14 #include "settings/DisplaySettings.h"
15 #include "settings/Settings.h"
16 #include "settings/SettingsComponent.h"
17 #include "utils/MathUtils.h"
18 #include "utils/Variant.h"
19 #include "utils/log.h"
21 #include <cstdlib>
22 #include <limits>
24 namespace
27 const char* SETTING_VIDEOSCREEN_WHITELIST_PULLDOWN{"videoscreen.whitelistpulldown"};
28 const char* SETTING_VIDEOSCREEN_WHITELIST_DOUBLEREFRESHRATE{
29 "videoscreen.whitelistdoublerefreshrate"};
31 } // namespace
33 EdgeInsets::EdgeInsets(float l, float t, float r, float b) : left(l), top(t), right(r), bottom(b)
37 RESOLUTION_INFO::RESOLUTION_INFO(int width, int height, float aspect, const std::string &mode) :
38 strMode(mode)
40 iWidth = width;
41 iHeight = height;
42 iBlanking = 0;
43 iScreenWidth = width;
44 iScreenHeight = height;
45 fPixelRatio = aspect ? ((float)width)/height / aspect : 1.0f;
46 bFullScreen = true;
47 fRefreshRate = 0;
48 dwFlags = iSubtitles = 0;
51 RESOLUTION_INFO::RESOLUTION_INFO(const RESOLUTION_INFO& res)
52 : Overscan(res.Overscan),
53 guiInsets(res.guiInsets),
54 strMode(res.strMode),
55 strOutput(res.strOutput),
56 strId(res.strId)
58 bFullScreen = res.bFullScreen;
59 iWidth = res.iWidth; iHeight = res.iHeight;
60 iScreenWidth = res.iScreenWidth; iScreenHeight = res.iScreenHeight;
61 iSubtitles = res.iSubtitles; dwFlags = res.dwFlags;
62 fPixelRatio = res.fPixelRatio; fRefreshRate = res.fRefreshRate;
63 iBlanking = res.iBlanking;
66 float RESOLUTION_INFO::DisplayRatio() const
68 return iWidth * fPixelRatio / iHeight;
71 RESOLUTION CResolutionUtils::ChooseBestResolution(float fps, int width, int height, bool is3D)
73 RESOLUTION res = CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution();
74 float weight = 0.0f;
76 if (!FindResolutionFromOverride(fps, width, is3D, res, weight, false)) //find a refreshrate from overrides
78 if (!FindResolutionFromOverride(fps, width, is3D, res, weight, true)) //if that fails find it from a fallback
80 FindResolutionFromWhitelist(fps, width, height, is3D, res); //find a refreshrate from whitelist
84 CLog::Log(LOGINFO, "Display resolution ADJUST : {} ({}) (weight: {:.3f})",
85 CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(res).strMode, res, weight);
86 return res;
89 void CResolutionUtils::FindResolutionFromWhitelist(float fps, int width, int height, bool is3D, RESOLUTION &resolution)
91 RESOLUTION_INFO curr = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(resolution);
92 CLog::Log(LOGINFO,
93 "[WHITELIST] Searching the whitelist for: width: {}, height: {}, fps: {:0.3f}, 3D: {}",
94 width, height, fps, is3D ? "true" : "false");
96 std::vector<CVariant> indexList = CServiceBroker::GetSettingsComponent()->GetSettings()->GetList(CSettings::SETTING_VIDEOSCREEN_WHITELIST);
98 bool noWhiteList = indexList.empty();
100 if (noWhiteList)
102 CLog::Log(LOGDEBUG,
103 "[WHITELIST] Using the default whitelist because the user whitelist is empty");
104 std::vector<RESOLUTION> candidates;
105 RESOLUTION_INFO info;
106 std::string resString;
107 CServiceBroker::GetWinSystem()->GetGfxContext().GetAllowedResolutions(candidates);
108 for (const auto& c : candidates)
110 info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(c);
111 if (info.iScreenHeight >= curr.iScreenHeight && info.iScreenWidth >= curr.iScreenWidth &&
112 (info.dwFlags & D3DPRESENTFLAG_MODEMASK) == (curr.dwFlags & D3DPRESENTFLAG_MODEMASK))
114 // do not add half refreshrates (25, 29.97 by default) as kodi cannot cope with
115 // them on playback start. Especially interlaced content is not properly detected
116 // and this causes ugly double switching.
117 // This won't allow 25p / 30p playback on native refreshrate by default
118 if ((info.fRefreshRate > 30) || (MathUtils::FloatEquals(info.fRefreshRate, 24.0f, 0.1f)))
120 resString = CDisplaySettings::GetInstance().GetStringFromRes(c);
121 indexList.emplace_back(resString);
127 CLog::Log(LOGDEBUG, "[WHITELIST] Searching for an exact resolution with an exact refresh rate");
129 unsigned int penalty = std::numeric_limits<unsigned int>::max();
130 bool found = false;
132 for (const auto& mode : indexList)
134 auto i = CDisplaySettings::GetInstance().GetResFromString(mode.asString());
135 const RESOLUTION_INFO info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i);
137 // allow resolutions that are exact and have the correct refresh rate
138 // allow macroblock alignment / padding errors (e.g. 1080 mod16 == 8)
139 if (((height == info.iScreenHeight && width <= info.iScreenWidth + 8) ||
140 (width == info.iScreenWidth && height <= info.iScreenHeight + 8)) &&
141 (info.dwFlags & D3DPRESENTFLAG_MODEMASK) == (curr.dwFlags & D3DPRESENTFLAG_MODEMASK) &&
142 MathUtils::FloatEquals(info.fRefreshRate, fps, 0.01f))
144 CLog::Log(LOGDEBUG,
145 "[WHITELIST] Matched an exact resolution with an exact refresh rate {} ({})",
146 info.strMode, i);
147 unsigned int pen = abs(info.iScreenHeight - height) + abs(info.iScreenWidth - width);
148 if (pen < penalty)
150 resolution = i;
151 found = true;
152 penalty = pen;
157 if (!found)
158 CLog::Log(LOGDEBUG, "[WHITELIST] No match for an exact resolution with an exact refresh rate");
160 if (noWhiteList || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
161 SETTING_VIDEOSCREEN_WHITELIST_DOUBLEREFRESHRATE))
163 CLog::Log(LOGDEBUG,
164 "[WHITELIST] Searching for an exact resolution with double the refresh rate");
166 for (const auto& mode : indexList)
168 auto i = CDisplaySettings::GetInstance().GetResFromString(mode.asString());
169 const RESOLUTION_INFO info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i);
171 // allow resolutions that are exact and have double the refresh rate
172 // allow macroblock alignment / padding errors (e.g. 1080 mod16 == 8)
173 if (((height == info.iScreenHeight && width <= info.iScreenWidth + 8) ||
174 (width == info.iScreenWidth && height <= info.iScreenHeight + 8)) &&
175 (info.dwFlags & D3DPRESENTFLAG_MODEMASK) == (curr.dwFlags & D3DPRESENTFLAG_MODEMASK) &&
176 MathUtils::FloatEquals(info.fRefreshRate, fps * 2, 0.01f))
178 CLog::Log(LOGDEBUG,
179 "[WHITELIST] Matched an exact resolution with double the refresh rate {} ({})",
180 info.strMode, i);
181 unsigned int pen = abs(info.iScreenHeight - height) + abs(info.iScreenWidth - width);
182 if (pen < penalty)
184 resolution = i;
185 found = true;
186 penalty = pen;
190 if (found)
191 return;
193 CLog::Log(LOGDEBUG,
194 "[WHITELIST] No match for an exact resolution with double the refresh rate");
196 else if (found)
197 return;
199 if (noWhiteList || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
200 SETTING_VIDEOSCREEN_WHITELIST_PULLDOWN))
202 CLog::Log(LOGDEBUG,
203 "[WHITELIST] Searching for an exact resolution with a 3:2 pulldown refresh rate");
205 for (const auto& mode : indexList)
207 auto i = CDisplaySettings::GetInstance().GetResFromString(mode.asString());
208 const RESOLUTION_INFO info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i);
210 // allow resolutions that are exact and have 2.5 times the refresh rate
211 // allow macroblock alignment / padding errors (e.g. 1080 mod16 == 8)
212 if (((height == info.iScreenHeight && width <= info.iScreenWidth + 8) ||
213 (width == info.iScreenWidth && height <= info.iScreenHeight + 8)) &&
214 (info.dwFlags & D3DPRESENTFLAG_MODEMASK) == (curr.dwFlags & D3DPRESENTFLAG_MODEMASK) &&
215 MathUtils::FloatEquals(info.fRefreshRate, fps * 2.5f, 0.01f))
217 CLog::Log(
218 LOGDEBUG,
219 "[WHITELIST] Matched an exact resolution with a 3:2 pulldown refresh rate {} ({})",
220 info.strMode, i);
221 unsigned int pen = abs(info.iScreenHeight - height) + abs(info.iScreenWidth - width);
222 if (pen < penalty)
224 resolution = i;
225 found = true;
226 penalty = pen;
230 if (found)
231 return;
233 CLog::Log(LOGDEBUG, "[WHITELIST] No match for a resolution with a 3:2 pulldown refresh rate");
237 CLog::Log(LOGDEBUG, "[WHITELIST] Searching for a desktop resolution with an exact refresh rate");
239 const RESOLUTION_INFO desktop_info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(CDisplaySettings::GetInstance().GetCurrentResolution());
241 for (const auto& mode : indexList)
243 auto i = CDisplaySettings::GetInstance().GetResFromString(mode.asString());
244 const RESOLUTION_INFO info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i);
246 // allow resolutions that are desktop resolution but have the correct refresh rate
247 if (info.iScreenWidth == desktop_info.iScreenWidth &&
248 (info.dwFlags & D3DPRESENTFLAG_MODEMASK) == (desktop_info.dwFlags & D3DPRESENTFLAG_MODEMASK) &&
249 MathUtils::FloatEquals(info.fRefreshRate, fps, 0.01f))
251 CLog::Log(LOGDEBUG,
252 "[WHITELIST] Matched a desktop resolution with an exact refresh rate {} ({})",
253 info.strMode, i);
254 resolution = i;
255 return;
259 CLog::Log(LOGDEBUG, "[WHITELIST] No match for a desktop resolution with an exact refresh rate");
261 if (noWhiteList || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
262 SETTING_VIDEOSCREEN_WHITELIST_DOUBLEREFRESHRATE))
264 CLog::Log(LOGDEBUG,
265 "[WHITELIST] Searching for a desktop resolution with double the refresh rate");
267 for (const auto& mode : indexList)
269 auto i = CDisplaySettings::GetInstance().GetResFromString(mode.asString());
270 const RESOLUTION_INFO info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i);
272 // allow resolutions that are desktop resolution but have double the refresh rate
273 if (info.iScreenWidth == desktop_info.iScreenWidth &&
274 (info.dwFlags & D3DPRESENTFLAG_MODEMASK) ==
275 (desktop_info.dwFlags & D3DPRESENTFLAG_MODEMASK) &&
276 MathUtils::FloatEquals(info.fRefreshRate, fps * 2, 0.01f))
278 CLog::Log(LOGDEBUG,
279 "[WHITELIST] Matched a desktop resolution with double the refresh rate {} ({})",
280 info.strMode, i);
281 resolution = i;
282 return;
286 CLog::Log(LOGDEBUG,
287 "[WHITELIST] No match for a desktop resolution with double the refresh rate");
290 if (noWhiteList || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
291 SETTING_VIDEOSCREEN_WHITELIST_PULLDOWN))
293 CLog::Log(LOGDEBUG,
294 "[WHITELIST] Searching for a desktop resolution with a 3:2 pulldown refresh rate");
296 for (const auto& mode : indexList)
298 auto i = CDisplaySettings::GetInstance().GetResFromString(mode.asString());
299 const RESOLUTION_INFO info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i);
301 // allow resolutions that are desktop resolution but have 2.5 times the refresh rate
302 if (info.iScreenWidth == desktop_info.iScreenWidth &&
303 (info.dwFlags & D3DPRESENTFLAG_MODEMASK) ==
304 (desktop_info.dwFlags & D3DPRESENTFLAG_MODEMASK) &&
305 MathUtils::FloatEquals(info.fRefreshRate, fps * 2.5f, 0.01f))
307 CLog::Log(
308 LOGDEBUG,
309 "[WHITELIST] Matched a desktop resolution with a 3:2 pulldown refresh rate {} ({})",
310 info.strMode, i);
311 resolution = i;
312 return;
316 CLog::Log(LOGDEBUG,
317 "[WHITELIST] No match for a desktop resolution with a 3:2 pulldown refresh rate");
320 CLog::Log(LOGDEBUG, "[WHITELIST] No resolution matched");
323 bool CResolutionUtils::FindResolutionFromOverride(float fps, int width, bool is3D, RESOLUTION &resolution, float& weight, bool fallback)
325 RESOLUTION_INFO curr = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(resolution);
327 //try to find a refreshrate from the override
328 for (int i = 0; i < (int)CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoAdjustRefreshOverrides.size(); i++)
330 RefreshOverride& override = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoAdjustRefreshOverrides[i];
332 if (override.fallback != fallback)
333 continue;
335 //if we're checking for overrides, check if the fps matches
336 if (!fallback && (fps < override.fpsmin || fps > override.fpsmax))
337 continue;
339 for (size_t j = (int)RES_DESKTOP; j < CDisplaySettings::GetInstance().ResolutionInfoSize(); j++)
341 RESOLUTION_INFO info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo((RESOLUTION)j);
343 if (info.iScreenWidth == curr.iScreenWidth &&
344 info.iScreenHeight == curr.iScreenHeight &&
345 (info.dwFlags & D3DPRESENTFLAG_MODEMASK) == (curr.dwFlags & D3DPRESENTFLAG_MODEMASK))
347 if (info.fRefreshRate <= override.refreshmax &&
348 info.fRefreshRate >= override.refreshmin)
350 resolution = (RESOLUTION)j;
352 if (fallback)
354 CLog::Log(
355 LOGDEBUG,
356 "Found Resolution {} ({}) from fallback (refreshmin:{:.3f} refreshmax:{:.3f})",
357 info.strMode, resolution, override.refreshmin, override.refreshmax);
359 else
361 CLog::Log(LOGDEBUG,
362 "Found Resolution {} ({}) from override of fps {:.3f} (fpsmin:{:.3f} "
363 "fpsmax:{:.3f} refreshmin:{:.3f} refreshmax:{:.3f})",
364 info.strMode, resolution, fps, override.fpsmin, override.fpsmax,
365 override.refreshmin, override.refreshmax);
368 weight = RefreshWeight(info.fRefreshRate, fps);
370 return true; //fps and refresh match with this override, use this resolution
376 return false; //no override found
379 //distance of refresh to the closest multiple of fps (multiple is 1 or higher), as a multiplier of fps
380 float CResolutionUtils::RefreshWeight(float refresh, float fps)
382 float div = refresh / fps;
383 int round = MathUtils::round_int(static_cast<double>(div));
385 float weight = 0.0f;
387 if (round < 1)
388 weight = (fps - refresh) / fps;
389 else
390 weight = fabs(div / round - 1.0f);
392 // punish higher refreshrates and prefer better matching
393 // e.g. 30 fps content at 60 hz is better than
394 // 30 fps at 120 hz - as we sometimes don't know if
395 // the content is interlaced at the start, only
396 // punish when refreshrate > 60 hz to not have to switch
397 // twice for 30i content
398 if (refresh > 60 && round > 1)
399 weight += round / 10000.0f;
401 return weight;
404 bool CResolutionUtils::HasWhitelist()
406 std::vector<CVariant> indexList = CServiceBroker::GetSettingsComponent()->GetSettings()->GetList(CSettings::SETTING_VIDEOSCREEN_WHITELIST);
407 return !indexList.empty();
410 void CResolutionUtils::PrintWhitelist()
412 std::string modeStr;
413 auto indexList = CServiceBroker::GetSettingsComponent()->GetSettings()->GetList(
414 CSettings::SETTING_VIDEOSCREEN_WHITELIST);
415 if (!indexList.empty())
417 for (const auto& mode : indexList)
419 auto i = CDisplaySettings::GetInstance().GetResFromString(mode.asString());
420 const RESOLUTION_INFO info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(i);
421 modeStr.append("\n" + info.strMode);
424 CLog::Log(LOGDEBUG, "[WHITELIST] whitelisted modes:{}", modeStr);
428 void CResolutionUtils::GetMaxAllowedScreenResolution(unsigned int& width, unsigned int& height)
430 if (!CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot())
431 return;
433 std::vector<RESOLUTION_INFO> resList;
435 auto indexList = CServiceBroker::GetSettingsComponent()->GetSettings()->GetList(
436 CSettings::SETTING_VIDEOSCREEN_WHITELIST);
438 unsigned int maxWidth{0};
439 unsigned int maxHeight{0};
441 if (!indexList.empty())
443 for (const auto& mode : indexList)
445 RESOLUTION res = CDisplaySettings::GetInstance().GetResFromString(mode.asString());
446 RESOLUTION_INFO resInfo{CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(res)};
447 if (static_cast<unsigned int>(resInfo.iScreenWidth) > maxWidth &&
448 static_cast<unsigned int>(resInfo.iScreenHeight) > maxHeight)
450 maxWidth = static_cast<unsigned int>(resInfo.iScreenWidth);
451 maxHeight = static_cast<unsigned int>(resInfo.iScreenHeight);
455 else
457 std::vector<RESOLUTION> resList;
458 CServiceBroker::GetWinSystem()->GetGfxContext().GetAllowedResolutions(resList);
460 for (const auto& res : resList)
462 RESOLUTION_INFO resInfo{CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(res)};
463 if (static_cast<unsigned int>(resInfo.iScreenWidth) > maxWidth &&
464 static_cast<unsigned int>(resInfo.iScreenHeight) > maxHeight)
466 maxWidth = static_cast<unsigned int>(resInfo.iScreenWidth);
467 maxHeight = static_cast<unsigned int>(resInfo.iScreenHeight);
472 width = maxWidth;
473 height = maxHeight;