[Test] Added tests for CUtil::SplitParams
[xbmc.git] / xbmc / windowing / android / AndroidUtils.cpp
blob6b3cdf856817841afc2cd7408742e2a701740784
1 /*
2 * Copyright (C) 2011-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 */
8 #include "AndroidUtils.h"
10 #include "ServiceBroker.h"
11 #include "settings/DisplaySettings.h"
12 #include "settings/Settings.h"
13 #include "settings/SettingsComponent.h"
14 #include "settings/lib/SettingsManager.h"
15 #include "utils/StringUtils.h"
16 #include "utils/log.h"
17 #include "windowing/GraphicContext.h"
19 #include "platform/android/activity/XBMCApp.h"
21 #include <androidjni/MediaCodecInfo.h>
22 #include <androidjni/MediaCodecList.h>
23 #include <androidjni/System.h>
24 #include <androidjni/SystemProperties.h>
25 #include <androidjni/View.h>
26 #include <androidjni/Window.h>
27 #include <androidjni/WindowManager.h>
29 static bool s_hasModeApi = false;
30 static std::vector<RESOLUTION_INFO> s_res_displayModes;
31 static RESOLUTION_INFO s_res_cur_displayMode;
33 static float currentRefreshRate()
35 if (s_hasModeApi)
36 return s_res_cur_displayMode.fRefreshRate;
38 CJNIWindow window = CXBMCApp::getWindow();
39 if (window)
41 float preferredRate = window.getAttributes().getpreferredRefreshRate();
42 if (preferredRate > 20.0f)
44 CLog::Log(LOGINFO, "CAndroidUtils: Preferred refresh rate: {:f}", preferredRate);
45 return preferredRate;
47 CJNIView view(window.getDecorView());
48 if (view)
50 CJNIDisplay display(view.getDisplay());
51 if (display)
53 float reportedRate = display.getRefreshRate();
54 if (reportedRate > 20.0f)
56 CLog::Log(LOGINFO, "CAndroidUtils: Current display refresh rate: {:f}", reportedRate);
57 return reportedRate;
62 CLog::Log(LOGDEBUG, "found no refresh rate");
63 return 60.0;
66 static void fetchDisplayModes()
68 s_hasModeApi = false;
69 s_res_displayModes.clear();
71 CJNIDisplay display = CXBMCApp::getWindow().getDecorView().getDisplay();
73 if (display)
75 CJNIDisplayMode m = display.getMode();
76 if (m)
78 if (m.getPhysicalWidth() > m.getPhysicalHeight()) // Assume unusable if portrait is returned
80 s_hasModeApi = true;
82 CLog::Log(LOGDEBUG, "CAndroidUtils: current mode: {}: {}x{}@{:f}", m.getModeId(),
83 m.getPhysicalWidth(), m.getPhysicalHeight(), m.getRefreshRate());
84 s_res_cur_displayMode.strId = std::to_string(m.getModeId());
85 s_res_cur_displayMode.iWidth = s_res_cur_displayMode.iScreenWidth = m.getPhysicalWidth();
86 s_res_cur_displayMode.iHeight = s_res_cur_displayMode.iScreenHeight = m.getPhysicalHeight();
87 s_res_cur_displayMode.fRefreshRate = m.getRefreshRate();
88 s_res_cur_displayMode.dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
89 s_res_cur_displayMode.bFullScreen = true;
90 s_res_cur_displayMode.iSubtitles = s_res_cur_displayMode.iHeight;
91 s_res_cur_displayMode.fPixelRatio = 1.0f;
92 s_res_cur_displayMode.strMode = StringUtils::Format(
93 "{}x{} @ {:.6f}{} - Full Screen", s_res_cur_displayMode.iScreenWidth,
94 s_res_cur_displayMode.iScreenHeight, s_res_cur_displayMode.fRefreshRate,
95 s_res_cur_displayMode.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
97 std::vector<CJNIDisplayMode> modes = display.getSupportedModes();
98 for (CJNIDisplayMode& m : modes)
100 CLog::Log(LOGDEBUG, "CAndroidUtils: available mode: {}: {}x{}@{:f}", m.getModeId(),
101 m.getPhysicalWidth(), m.getPhysicalHeight(), m.getRefreshRate());
103 RESOLUTION_INFO res;
104 res.strId = std::to_string(m.getModeId());
105 res.iWidth = res.iScreenWidth = m.getPhysicalWidth();
106 res.iHeight = res.iScreenHeight = m.getPhysicalHeight();
107 res.fRefreshRate = m.getRefreshRate();
108 res.dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
109 res.bFullScreen = true;
110 res.iSubtitles = res.iHeight;
111 res.fPixelRatio = 1.0f;
112 res.strMode = StringUtils::Format("{}x{} @ {:.6f}{} - Full Screen", res.iScreenWidth,
113 res.iScreenHeight, res.fRefreshRate,
114 res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
116 s_res_displayModes.push_back(res);
123 namespace
125 std::string HdrTypeString(int type)
127 const std::map<int, std::string> hdrTypeMap = {
128 {CJNIDisplayHdrCapabilities::HDR_TYPE_HDR10, "HDR10"},
129 {CJNIDisplayHdrCapabilities::HDR_TYPE_HLG, "HLG"},
130 {CJNIDisplayHdrCapabilities::HDR_TYPE_HDR10_PLUS, "HDR10+"},
131 {CJNIDisplayHdrCapabilities::HDR_TYPE_DOLBY_VISION, "Dolby Vision"}};
133 auto hdr = hdrTypeMap.find(type);
134 if (hdr != hdrTypeMap.end())
135 return hdr->second;
137 return "Unknown";
139 } // unnamed namespace
141 const std::string CAndroidUtils::SETTING_LIMITGUI = "videoscreen.limitgui";
143 CAndroidUtils::CAndroidUtils()
145 std::string displaySize;
146 m_width = m_height = 0;
148 if (CJNIBase::GetSDKVersion() >= 23)
150 fetchDisplayModes();
151 for (const RESOLUTION_INFO& res : s_res_displayModes)
153 if (res.iWidth > m_width || res.iHeight > m_height)
155 m_width = res.iWidth;
156 m_height = res.iHeight;
161 if (!m_width || !m_height)
163 // Property available on some devices
164 displaySize = CJNISystemProperties::get("sys.display-size", "");
165 if (!displaySize.empty())
167 std::vector<std::string> aSize = StringUtils::Split(displaySize, "x");
168 if (aSize.size() == 2)
170 m_width = StringUtils::IsInteger(aSize[0]) ? atoi(aSize[0].c_str()) : 0;
171 m_height = StringUtils::IsInteger(aSize[1]) ? atoi(aSize[1].c_str()) : 0;
173 CLog::Log(LOGDEBUG, "CAndroidUtils: display-size: {}({}x{})", displaySize, m_width, m_height);
177 CLog::Log(LOGDEBUG, "CAndroidUtils: maximum/current resolution: {}x{}", m_width, m_height);
178 int limit = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
179 CAndroidUtils::SETTING_LIMITGUI);
180 switch (limit)
182 case 0: // auto
183 m_width = 0;
184 m_height = 0;
185 break;
187 case 9999: // unlimited
188 break;
190 case 720:
191 if (m_height > 720)
193 m_width = 1280;
194 m_height = 720;
196 break;
198 case 1080:
199 if (m_height > 1080)
201 m_width = 1920;
202 m_height = 1080;
204 break;
206 CLog::Log(LOGDEBUG, "CAndroidUtils: selected resolution: {}x{}", m_width, m_height);
208 CServiceBroker::GetSettingsComponent()->GetSettings()->GetSettingsManager()->RegisterCallback(
209 this, {CAndroidUtils::SETTING_LIMITGUI});
211 LogDisplaySupportedHdrTypes();
214 bool CAndroidUtils::GetNativeResolution(RESOLUTION_INFO* res) const
216 const std::shared_ptr<CNativeWindow> nativeWindow = CXBMCApp::Get().GetNativeWindow(30000);
217 if (!nativeWindow)
218 return false;
220 if (!m_width || !m_height)
222 m_width = nativeWindow->GetWidth();
223 m_height = nativeWindow->GetHeight();
224 CLog::Log(LOGINFO, "CAndroidUtils: window resolution: {}x{}", m_width, m_height);
227 if (s_hasModeApi)
229 *res = s_res_cur_displayMode;
230 res->iWidth = m_width;
231 res->iHeight = m_height;
233 else
235 res->strId = "-1";
236 res->fRefreshRate = currentRefreshRate();
237 res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
238 res->bFullScreen = true;
239 res->iWidth = m_width;
240 res->iHeight = m_height;
241 res->fPixelRatio = 1.0f;
242 res->iScreenWidth = res->iWidth;
243 res->iScreenHeight = res->iHeight;
245 res->iSubtitles = res->iHeight;
246 res->strMode =
247 StringUtils::Format("{}x{} @ {:.6f}{} - Full Screen", res->iScreenWidth, res->iScreenHeight,
248 res->fRefreshRate, res->dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
249 CLog::Log(LOGINFO, "CAndroidUtils: Current resolution: {}x{} {}", res->iWidth, res->iHeight,
250 res->strMode);
251 return true;
254 bool CAndroidUtils::SetNativeResolution(const RESOLUTION_INFO& res)
256 CLog::Log(LOGINFO, "CAndroidUtils: SetNativeResolution: {}: {}x{} {}x{}@{:f}", res.strId,
257 res.iWidth, res.iHeight, res.iScreenWidth, res.iScreenHeight, res.fRefreshRate);
259 if (s_hasModeApi)
261 CXBMCApp::Get().SetDisplayMode(std::atoi(res.strId.c_str()), res.fRefreshRate);
262 s_res_cur_displayMode = res;
264 else
265 CXBMCApp::Get().SetRefreshRate(res.fRefreshRate);
267 CXBMCApp::Get().SetBuffersGeometry(res.iWidth, res.iHeight, 0);
269 return true;
272 bool CAndroidUtils::ProbeResolutions(std::vector<RESOLUTION_INFO>& resolutions)
274 RESOLUTION_INFO cur_res;
275 bool ret = GetNativeResolution(&cur_res);
277 CLog::Log(LOGDEBUG, "CAndroidUtils: ProbeResolutions: {}x{}", m_width, m_height);
279 if (s_hasModeApi)
281 for (RESOLUTION_INFO res : s_res_displayModes)
283 if (m_width && m_height)
285 res.iWidth = std::min(res.iWidth, m_width);
286 res.iHeight = std::min(res.iHeight, m_height);
287 res.iSubtitles = res.iHeight;
289 resolutions.push_back(res);
291 return true;
294 if (ret && cur_res.iWidth > 1 && cur_res.iHeight > 1)
296 std::vector<float> refreshRates;
297 CJNIWindow window = CXBMCApp::getWindow();
298 if (window)
300 CJNIView view = window.getDecorView();
301 if (view)
303 CJNIDisplay display = view.getDisplay();
304 if (display)
306 refreshRates = display.getSupportedRefreshRates();
310 if (!refreshRates.empty())
312 for (unsigned int i = 0; i < refreshRates.size(); i++)
314 if (refreshRates[i] < 20.0f)
315 continue;
316 cur_res.fRefreshRate = refreshRates[i];
317 cur_res.strMode = StringUtils::Format(
318 "{}x{} @ {:.6f}{} - Full Screen", cur_res.iScreenWidth, cur_res.iScreenHeight,
319 cur_res.fRefreshRate, cur_res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
320 resolutions.push_back(cur_res);
324 if (resolutions.empty())
326 /* No valid refresh rates available, just provide the current one */
327 resolutions.push_back(cur_res);
329 return true;
331 return false;
334 bool CAndroidUtils::UpdateDisplayModes()
336 if (CJNIBase::GetSDKVersion() >= 23)
337 fetchDisplayModes();
338 return true;
341 bool CAndroidUtils::IsHDRDisplay()
343 CJNIWindow window = CXBMCApp::getWindow();
344 bool ret = false;
346 if (window)
348 CJNIView view = window.getDecorView();
349 if (view)
351 CJNIDisplay display = view.getDisplay();
352 if (display)
353 ret = display.isHdr();
356 CLog::Log(LOGDEBUG, "CAndroidUtils: IsHDRDisplay: {}", ret ? "true" : "false");
357 return ret;
360 void CAndroidUtils::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
362 const std::string& settingId = setting->GetId();
363 /* Calibration (overscan / subtitles) are based on GUI size -> reset required */
364 if (settingId == CAndroidUtils::SETTING_LIMITGUI)
365 CDisplaySettings::GetInstance().ClearCalibrations();
368 std::vector<int> CAndroidUtils::GetDisplaySupportedHdrTypes()
370 CJNIWindow window = CXBMCApp::getWindow();
372 if (window)
374 CJNIView view = window.getDecorView();
375 if (view)
377 CJNIDisplay display = view.getDisplay();
378 if (display)
380 CJNIDisplayHdrCapabilities caps = display.getHdrCapabilities();
381 if (caps)
382 return caps.getSupportedHdrTypes();
387 return {};
390 void CAndroidUtils::LogDisplaySupportedHdrTypes()
392 const std::vector<int> hdrTypes = GetDisplaySupportedHdrTypes();
393 std::string text;
395 for (const int& type : hdrTypes)
397 text += " " + HdrTypeString(type);
400 CLog::Log(LOGDEBUG, "CAndroidUtils: Display supported HDR types:{}",
401 text.empty() ? " None" : text);
404 CHDRCapabilities CAndroidUtils::GetDisplayHDRCapabilities()
406 CHDRCapabilities caps;
407 const std::vector<int> types = GetDisplaySupportedHdrTypes();
409 if (std::find(types.begin(), types.end(), CJNIDisplayHdrCapabilities::HDR_TYPE_HDR10) !=
410 types.end())
411 caps.SetHDR10();
413 if (std::find(types.begin(), types.end(), CJNIDisplayHdrCapabilities::HDR_TYPE_HLG) !=
414 types.end())
415 caps.SetHLG();
417 if (std::find(types.begin(), types.end(), CJNIDisplayHdrCapabilities::HDR_TYPE_HDR10_PLUS) !=
418 types.end())
419 caps.SetHDR10Plus();
421 if (std::find(types.begin(), types.end(), CJNIDisplayHdrCapabilities::HDR_TYPE_DOLBY_VISION) !=
422 types.end())
423 caps.SetDolbyVision();
425 return caps;
428 bool CAndroidUtils::SupportsMediaCodecMimeType(const std::string& mimeType)
430 const std::vector<CJNIMediaCodecInfo> codecInfos =
431 CJNIMediaCodecList(CJNIMediaCodecList::REGULAR_CODECS).getCodecInfos();
433 for (const CJNIMediaCodecInfo& codec_info : codecInfos)
435 if (codec_info.isEncoder())
436 continue;
438 std::vector<std::string> types = codec_info.getSupportedTypes();
439 if (std::find(types.begin(), types.end(), mimeType) != types.end())
440 return true;
443 return false;
446 std::pair<bool, bool> CAndroidUtils::GetDolbyVisionCapabilities()
448 const bool displaySupportsDovi = GetDisplayHDRCapabilities().SupportsDolbyVision();
449 const bool mediaCodecSupportsDovi = SupportsMediaCodecMimeType("video/dolby-vision");
451 CLog::Log(LOGDEBUG, "CAndroidUtils::GetDolbyVisionCapabilities Display: {}, MediaCodec: {}",
452 displaySupportsDovi, mediaCodecSupportsDovi);
454 return std::make_pair(displaySupportsDovi, mediaCodecSupportsDovi);