summaryrefslogtreecommitdiffstats
path: root/xbmc/windowing/android
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/windowing/android
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/windowing/android')
-rw-r--r--xbmc/windowing/android/AndroidUtils.cpp440
-rw-r--r--xbmc/windowing/android/AndroidUtils.h56
-rw-r--r--xbmc/windowing/android/CMakeLists.txt18
-rw-r--r--xbmc/windowing/android/OSScreenSaverAndroid.cpp21
-rw-r--r--xbmc/windowing/android/OSScreenSaverAndroid.h19
-rw-r--r--xbmc/windowing/android/VideoSyncAndroid.cpp78
-rw-r--r--xbmc/windowing/android/VideoSyncAndroid.h34
-rw-r--r--xbmc/windowing/android/WinEventsAndroid.cpp191
-rw-r--r--xbmc/windowing/android/WinEventsAndroid.h43
-rw-r--r--xbmc/windowing/android/WinSystemAndroid.cpp329
-rw-r--r--xbmc/windowing/android/WinSystemAndroid.h85
-rw-r--r--xbmc/windowing/android/WinSystemAndroidGLESContext.cpp270
-rw-r--r--xbmc/windowing/android/WinSystemAndroidGLESContext.h63
13 files changed, 1647 insertions, 0 deletions
diff --git a/xbmc/windowing/android/AndroidUtils.cpp b/xbmc/windowing/android/AndroidUtils.cpp
new file mode 100644
index 0000000..8c61fae
--- /dev/null
+++ b/xbmc/windowing/android/AndroidUtils.cpp
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2011-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+#include "AndroidUtils.h"
+
+#include "ServiceBroker.h"
+#include "settings/DisplaySettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+
+#include "platform/android/activity/XBMCApp.h"
+
+#include <androidjni/MediaCodecInfo.h>
+#include <androidjni/MediaCodecList.h>
+#include <androidjni/System.h>
+#include <androidjni/SystemProperties.h>
+#include <androidjni/View.h>
+#include <androidjni/Window.h>
+#include <androidjni/WindowManager.h>
+
+static bool s_hasModeApi = false;
+static std::vector<RESOLUTION_INFO> s_res_displayModes;
+static RESOLUTION_INFO s_res_cur_displayMode;
+
+static float currentRefreshRate()
+{
+ if (s_hasModeApi)
+ return s_res_cur_displayMode.fRefreshRate;
+
+ CJNIWindow window = CXBMCApp::getWindow();
+ if (window)
+ {
+ float preferredRate = window.getAttributes().getpreferredRefreshRate();
+ if (preferredRate > 20.0f)
+ {
+ CLog::Log(LOGINFO, "CAndroidUtils: Preferred refresh rate: {:f}", preferredRate);
+ return preferredRate;
+ }
+ CJNIView view(window.getDecorView());
+ if (view)
+ {
+ CJNIDisplay display(view.getDisplay());
+ if (display)
+ {
+ float reportedRate = display.getRefreshRate();
+ if (reportedRate > 20.0f)
+ {
+ CLog::Log(LOGINFO, "CAndroidUtils: Current display refresh rate: {:f}", reportedRate);
+ return reportedRate;
+ }
+ }
+ }
+ }
+ CLog::Log(LOGDEBUG, "found no refresh rate");
+ return 60.0;
+}
+
+static void fetchDisplayModes()
+{
+ s_hasModeApi = false;
+ s_res_displayModes.clear();
+
+ CJNIDisplay display = CXBMCApp::getWindow().getDecorView().getDisplay();
+
+ if (display)
+ {
+ CJNIDisplayMode m = display.getMode();
+ if (m)
+ {
+ if (m.getPhysicalWidth() > m.getPhysicalHeight()) // Assume unusable if portrait is returned
+ {
+ s_hasModeApi = true;
+
+ CLog::Log(LOGDEBUG, "CAndroidUtils: current mode: {}: {}x{}@{:f}", m.getModeId(),
+ m.getPhysicalWidth(), m.getPhysicalHeight(), m.getRefreshRate());
+ s_res_cur_displayMode.strId = std::to_string(m.getModeId());
+ s_res_cur_displayMode.iWidth = s_res_cur_displayMode.iScreenWidth = m.getPhysicalWidth();
+ s_res_cur_displayMode.iHeight = s_res_cur_displayMode.iScreenHeight = m.getPhysicalHeight();
+ s_res_cur_displayMode.fRefreshRate = m.getRefreshRate();
+ s_res_cur_displayMode.dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ s_res_cur_displayMode.bFullScreen = true;
+ s_res_cur_displayMode.iSubtitles = s_res_cur_displayMode.iHeight;
+ s_res_cur_displayMode.fPixelRatio = 1.0f;
+ s_res_cur_displayMode.strMode = StringUtils::Format(
+ "{}x{} @ {:.6f}{} - Full Screen", s_res_cur_displayMode.iScreenWidth,
+ s_res_cur_displayMode.iScreenHeight, s_res_cur_displayMode.fRefreshRate,
+ s_res_cur_displayMode.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
+
+ std::vector<CJNIDisplayMode> modes = display.getSupportedModes();
+ for (CJNIDisplayMode& m : modes)
+ {
+ CLog::Log(LOGDEBUG, "CAndroidUtils: available mode: {}: {}x{}@{:f}", m.getModeId(),
+ m.getPhysicalWidth(), m.getPhysicalHeight(), m.getRefreshRate());
+
+ RESOLUTION_INFO res;
+ res.strId = std::to_string(m.getModeId());
+ res.iWidth = res.iScreenWidth = m.getPhysicalWidth();
+ res.iHeight = res.iScreenHeight = m.getPhysicalHeight();
+ res.fRefreshRate = m.getRefreshRate();
+ res.dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ res.bFullScreen = true;
+ res.iSubtitles = res.iHeight;
+ res.fPixelRatio = 1.0f;
+ res.strMode = StringUtils::Format("{}x{} @ {:.6f}{} - Full Screen", res.iScreenWidth,
+ res.iScreenHeight, res.fRefreshRate,
+ res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
+
+ s_res_displayModes.push_back(res);
+ }
+ }
+ }
+ }
+}
+
+namespace
+{
+const std::map<int, std::string> hdrTypeMap = {
+ {CAndroidUtils::HDRTypes::HDR10, "HDR10"},
+ {CAndroidUtils::HDRTypes::HLG, "HLG"},
+ {CAndroidUtils::HDRTypes::HDR10_PLUS, "HDR10+"},
+ {CAndroidUtils::HDRTypes::DOLBY_VISION, "Dolby Vision"}};
+
+std::string HdrTypeString(int type)
+{
+ auto hdr = hdrTypeMap.find(type);
+ if (hdr != hdrTypeMap.end())
+ return hdr->second;
+
+ return "Unknown";
+}
+} // unnamed namespace
+
+const std::string CAndroidUtils::SETTING_LIMITGUI = "videoscreen.limitgui";
+
+CAndroidUtils::CAndroidUtils()
+{
+ std::string displaySize;
+ m_width = m_height = 0;
+
+ if (CJNIBase::GetSDKVersion() >= 23)
+ {
+ fetchDisplayModes();
+ for (const RESOLUTION_INFO& res : s_res_displayModes)
+ {
+ if (res.iWidth > m_width || res.iHeight > m_height)
+ {
+ m_width = res.iWidth;
+ m_height = res.iHeight;
+ }
+ }
+ }
+
+ if (!m_width || !m_height)
+ {
+ // Property available on some devices
+ displaySize = CJNISystemProperties::get("sys.display-size", "");
+ if (!displaySize.empty())
+ {
+ std::vector<std::string> aSize = StringUtils::Split(displaySize, "x");
+ if (aSize.size() == 2)
+ {
+ m_width = StringUtils::IsInteger(aSize[0]) ? atoi(aSize[0].c_str()) : 0;
+ m_height = StringUtils::IsInteger(aSize[1]) ? atoi(aSize[1].c_str()) : 0;
+ }
+ CLog::Log(LOGDEBUG, "CAndroidUtils: display-size: {}({}x{})", displaySize, m_width, m_height);
+ }
+ }
+
+ CLog::Log(LOGDEBUG, "CAndroidUtils: maximum/current resolution: {}x{}", m_width, m_height);
+ int limit = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
+ CAndroidUtils::SETTING_LIMITGUI);
+ switch (limit)
+ {
+ case 0: // auto
+ m_width = 0;
+ m_height = 0;
+ break;
+
+ case 9999: // unlimited
+ break;
+
+ case 720:
+ if (m_height > 720)
+ {
+ m_width = 1280;
+ m_height = 720;
+ }
+ break;
+
+ case 1080:
+ if (m_height > 1080)
+ {
+ m_width = 1920;
+ m_height = 1080;
+ }
+ break;
+ }
+ CLog::Log(LOGDEBUG, "CAndroidUtils: selected resolution: {}x{}", m_width, m_height);
+
+ CServiceBroker::GetSettingsComponent()->GetSettings()->GetSettingsManager()->RegisterCallback(
+ this, {CAndroidUtils::SETTING_LIMITGUI});
+
+ LogDisplaySupportedHdrTypes();
+}
+
+bool CAndroidUtils::GetNativeResolution(RESOLUTION_INFO* res) const
+{
+ const std::shared_ptr<CNativeWindow> nativeWindow = CXBMCApp::Get().GetNativeWindow(30000);
+ if (!nativeWindow)
+ return false;
+
+ if (!m_width || !m_height)
+ {
+ m_width = nativeWindow->GetWidth();
+ m_height = nativeWindow->GetHeight();
+ CLog::Log(LOGINFO, "CAndroidUtils: window resolution: {}x{}", m_width, m_height);
+ }
+
+ if (s_hasModeApi)
+ {
+ *res = s_res_cur_displayMode;
+ res->iWidth = m_width;
+ res->iHeight = m_height;
+ }
+ else
+ {
+ res->strId = "-1";
+ res->fRefreshRate = currentRefreshRate();
+ res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
+ res->bFullScreen = true;
+ res->iWidth = m_width;
+ res->iHeight = m_height;
+ res->fPixelRatio = 1.0f;
+ res->iScreenWidth = res->iWidth;
+ res->iScreenHeight = res->iHeight;
+ }
+ res->iSubtitles = res->iHeight;
+ res->strMode =
+ StringUtils::Format("{}x{} @ {:.6f}{} - Full Screen", res->iScreenWidth, res->iScreenHeight,
+ res->fRefreshRate, res->dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
+ CLog::Log(LOGINFO, "CAndroidUtils: Current resolution: {}x{} {}", res->iWidth, res->iHeight,
+ res->strMode);
+ return true;
+}
+
+bool CAndroidUtils::SetNativeResolution(const RESOLUTION_INFO& res)
+{
+ CLog::Log(LOGINFO, "CAndroidUtils: SetNativeResolution: {}: {}x{} {}x{}@{:f}", res.strId,
+ res.iWidth, res.iHeight, res.iScreenWidth, res.iScreenHeight, res.fRefreshRate);
+
+ if (s_hasModeApi)
+ {
+ CXBMCApp::Get().SetDisplayMode(std::atoi(res.strId.c_str()), res.fRefreshRate);
+ s_res_cur_displayMode = res;
+ }
+ else
+ CXBMCApp::Get().SetRefreshRate(res.fRefreshRate);
+
+ CXBMCApp::Get().SetBuffersGeometry(res.iWidth, res.iHeight, 0);
+
+ return true;
+}
+
+bool CAndroidUtils::ProbeResolutions(std::vector<RESOLUTION_INFO>& resolutions)
+{
+ RESOLUTION_INFO cur_res;
+ bool ret = GetNativeResolution(&cur_res);
+
+ CLog::Log(LOGDEBUG, "CAndroidUtils: ProbeResolutions: {}x{}", m_width, m_height);
+
+ if (s_hasModeApi)
+ {
+ for (RESOLUTION_INFO res : s_res_displayModes)
+ {
+ if (m_width && m_height)
+ {
+ res.iWidth = std::min(res.iWidth, m_width);
+ res.iHeight = std::min(res.iHeight, m_height);
+ res.iSubtitles = res.iHeight;
+ }
+ resolutions.push_back(res);
+ }
+ return true;
+ }
+
+ if (ret && cur_res.iWidth > 1 && cur_res.iHeight > 1)
+ {
+ std::vector<float> refreshRates;
+ CJNIWindow window = CXBMCApp::getWindow();
+ if (window)
+ {
+ CJNIView view = window.getDecorView();
+ if (view)
+ {
+ CJNIDisplay display = view.getDisplay();
+ if (display)
+ {
+ refreshRates = display.getSupportedRefreshRates();
+ }
+ }
+
+ if (!refreshRates.empty())
+ {
+ for (unsigned int i = 0; i < refreshRates.size(); i++)
+ {
+ if (refreshRates[i] < 20.0f)
+ continue;
+ cur_res.fRefreshRate = refreshRates[i];
+ cur_res.strMode = StringUtils::Format(
+ "{}x{} @ {:.6f}{} - Full Screen", cur_res.iScreenWidth, cur_res.iScreenHeight,
+ cur_res.fRefreshRate, cur_res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
+ resolutions.push_back(cur_res);
+ }
+ }
+ }
+ if (resolutions.empty())
+ {
+ /* No valid refresh rates available, just provide the current one */
+ resolutions.push_back(cur_res);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool CAndroidUtils::UpdateDisplayModes()
+{
+ if (CJNIBase::GetSDKVersion() >= 23)
+ fetchDisplayModes();
+ return true;
+}
+
+bool CAndroidUtils::IsHDRDisplay()
+{
+ CJNIWindow window = CXBMCApp::getWindow();
+ bool ret = false;
+
+ if (window)
+ {
+ CJNIView view = window.getDecorView();
+ if (view)
+ {
+ CJNIDisplay display = view.getDisplay();
+ if (display)
+ ret = display.isHdr();
+ }
+ }
+ CLog::Log(LOGDEBUG, "CAndroidUtils: IsHDRDisplay: {}", ret ? "true" : "false");
+ return ret;
+}
+
+void CAndroidUtils::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ const std::string& settingId = setting->GetId();
+ /* Calibration (overscan / subtitles) are based on GUI size -> reset required */
+ if (settingId == CAndroidUtils::SETTING_LIMITGUI)
+ CDisplaySettings::GetInstance().ClearCalibrations();
+}
+
+std::vector<int> CAndroidUtils::GetDisplaySupportedHdrTypes()
+{
+ CJNIWindow window = CXBMCApp::getWindow();
+
+ if (window)
+ {
+ CJNIView view = window.getDecorView();
+ if (view)
+ {
+ CJNIDisplay display = view.getDisplay();
+ if (display)
+ {
+ CJNIDisplayHdrCapabilities caps = display.getHdrCapabilities();
+ if (caps)
+ return caps.getSupportedHdrTypes();
+ }
+ }
+ }
+
+ return {};
+}
+
+void CAndroidUtils::LogDisplaySupportedHdrTypes()
+{
+ const std::vector<int> hdrTypes = GetDisplaySupportedHdrTypes();
+ std::string text;
+
+ for (const int& type : hdrTypes)
+ {
+ text += " " + HdrTypeString(type);
+ }
+
+ CLog::Log(LOGDEBUG, "CAndroidUtils: Display supported HDR types:{}",
+ text.empty() ? " None" : text);
+}
+
+CHDRCapabilities CAndroidUtils::GetDisplayHDRCapabilities()
+{
+ CHDRCapabilities caps;
+ const std::vector<int> types = GetDisplaySupportedHdrTypes();
+
+ if (std::find(types.begin(), types.end(), CAndroidUtils::HDRTypes::HDR10) != types.end())
+ caps.SetHDR10();
+
+ if (std::find(types.begin(), types.end(), CAndroidUtils::HDRTypes::HLG) != types.end())
+ caps.SetHLG();
+
+ if (std::find(types.begin(), types.end(), CAndroidUtils::HDRTypes::HDR10_PLUS) != types.end())
+ caps.SetHDR10Plus();
+
+ if (std::find(types.begin(), types.end(), CAndroidUtils::HDRTypes::DOLBY_VISION) != types.end())
+ caps.SetDolbyVision();
+
+ return caps;
+}
+
+bool CAndroidUtils::SupportsMediaCodecMimeType(const std::string& mimeType)
+{
+ const std::vector<CJNIMediaCodecInfo> codecInfos =
+ CJNIMediaCodecList(CJNIMediaCodecList::REGULAR_CODECS).getCodecInfos();
+
+ for (const CJNIMediaCodecInfo& codec_info : codecInfos)
+ {
+ if (codec_info.isEncoder())
+ continue;
+
+ std::vector<std::string> types = codec_info.getSupportedTypes();
+ if (std::find(types.begin(), types.end(), mimeType) != types.end())
+ return true;
+ }
+
+ return false;
+}
diff --git a/xbmc/windowing/android/AndroidUtils.h b/xbmc/windowing/android/AndroidUtils.h
new file mode 100644
index 0000000..426baee
--- /dev/null
+++ b/xbmc/windowing/android/AndroidUtils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "settings/lib/ISettingCallback.h"
+#include "utils/HDRCapabilities.h"
+#include "windowing/Resolution.h"
+
+#include <string>
+#include <vector>
+
+#include <androidjni/Display.h>
+
+class CAndroidUtils : public ISettingCallback
+{
+public:
+ CAndroidUtils();
+ ~CAndroidUtils() override = default;
+ bool GetNativeResolution(RESOLUTION_INFO* res) const;
+ bool SetNativeResolution(const RESOLUTION_INFO& res);
+ bool ProbeResolutions(std::vector<RESOLUTION_INFO>& resolutions);
+ bool UpdateDisplayModes();
+ bool IsHDRDisplay();
+
+ // Implementation of ISettingCallback
+ static const std::string SETTING_LIMITGUI;
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+
+ static bool SupportsMediaCodecMimeType(const std::string& mimeType);
+
+ // Android specific HDR type mapping
+ // https://developer.android.com/reference/android/view/Display.HdrCapabilities#constants_1
+ enum HDRTypes
+ {
+ DOLBY_VISION = 1,
+ HDR10 = 2,
+ HLG = 3,
+ HDR10_PLUS = 4
+ };
+
+ static std::vector<int> GetDisplaySupportedHdrTypes();
+ static CHDRCapabilities GetDisplayHDRCapabilities();
+
+protected:
+ mutable int m_width;
+ mutable int m_height;
+
+private:
+ static void LogDisplaySupportedHdrTypes();
+};
diff --git a/xbmc/windowing/android/CMakeLists.txt b/xbmc/windowing/android/CMakeLists.txt
new file mode 100644
index 0000000..fd489d1
--- /dev/null
+++ b/xbmc/windowing/android/CMakeLists.txt
@@ -0,0 +1,18 @@
+set(SOURCES OSScreenSaverAndroid.cpp
+ WinEventsAndroid.cpp
+ WinSystemAndroid.cpp
+ AndroidUtils.cpp
+ VideoSyncAndroid.cpp)
+
+set(HEADERS OSScreenSaverAndroid.h
+ WinEventsAndroid.h
+ WinSystemAndroid.h
+ AndroidUtils.h
+ VideoSyncAndroid.h)
+
+if(OPENGLES_FOUND)
+ list(APPEND SOURCES WinSystemAndroidGLESContext.cpp)
+ list(APPEND HEADERS WinSystemAndroidGLESContext.h)
+endif()
+
+core_add_library(windowing_android)
diff --git a/xbmc/windowing/android/OSScreenSaverAndroid.cpp b/xbmc/windowing/android/OSScreenSaverAndroid.cpp
new file mode 100644
index 0000000..aa6dfc5
--- /dev/null
+++ b/xbmc/windowing/android/OSScreenSaverAndroid.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 Chris Browet
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "OSScreenSaverAndroid.h"
+
+#include "platform/android/activity/XBMCApp.h"
+
+void COSScreenSaverAndroid::Inhibit()
+{
+ CXBMCApp::Get().EnableWakeLock(true);
+}
+
+void COSScreenSaverAndroid::Uninhibit()
+{
+ CXBMCApp::Get().EnableWakeLock(false);
+}
diff --git a/xbmc/windowing/android/OSScreenSaverAndroid.h b/xbmc/windowing/android/OSScreenSaverAndroid.h
new file mode 100644
index 0000000..1b67d85
--- /dev/null
+++ b/xbmc/windowing/android/OSScreenSaverAndroid.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 Chris Browet
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "../OSScreenSaver.h"
+
+class COSScreenSaverAndroid : public KODI::WINDOWING::IOSScreenSaver
+{
+public:
+ explicit COSScreenSaverAndroid() = default;
+ void Inhibit() override;
+ void Uninhibit() override;
+};
diff --git a/xbmc/windowing/android/VideoSyncAndroid.cpp b/xbmc/windowing/android/VideoSyncAndroid.cpp
new file mode 100644
index 0000000..cee6f3e
--- /dev/null
+++ b/xbmc/windowing/android/VideoSyncAndroid.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "VideoSyncAndroid.h"
+
+#include "ServiceBroker.h"
+#include "cores/VideoPlayer/VideoReferenceClock.h"
+#include "utils/MathUtils.h"
+#include "utils/TimeUtils.h"
+#include "utils/XTimeUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/WinSystem.h"
+
+#include "platform/android/activity/XBMCApp.h"
+
+
+bool CVideoSyncAndroid::Setup(PUPDATECLOCK func)
+{
+ CLog::Log(LOGDEBUG, "CVideoSyncAndroid::{} setting up", __FUNCTION__);
+
+ //init the vblank timestamp
+ m_LastVBlankTime = CurrentHostCounter();
+ UpdateClock = func;
+ m_abortEvent.Reset();
+
+ CXBMCApp::Get().InitFrameCallback(this);
+ CServiceBroker::GetWinSystem()->Register(this);
+
+ return true;
+}
+
+void CVideoSyncAndroid::Run(CEvent& stopEvent)
+{
+ XbmcThreads::CEventGroup waitGroup{&stopEvent, &m_abortEvent};
+ waitGroup.wait();
+}
+
+void CVideoSyncAndroid::Cleanup()
+{
+ CLog::Log(LOGDEBUG, "CVideoSyncAndroid::{} cleaning up", __FUNCTION__);
+ CXBMCApp::Get().DeinitFrameCallback();
+ CServiceBroker::GetWinSystem()->Unregister(this);
+}
+
+float CVideoSyncAndroid::GetFps()
+{
+ m_fps = CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS();
+ CLog::Log(LOGDEBUG, "CVideoSyncAndroid::{} Detected refreshrate: {:f} hertz", __FUNCTION__,
+ m_fps);
+ return m_fps;
+}
+
+void CVideoSyncAndroid::OnResetDisplay()
+{
+ m_abortEvent.Set();
+}
+
+void CVideoSyncAndroid::FrameCallback(int64_t frameTimeNanos)
+{
+ int NrVBlanks;
+ double VBlankTime;
+
+ //calculate how many vblanks happened
+ VBlankTime = (double)(frameTimeNanos - m_LastVBlankTime) / (double)CurrentHostFrequency();
+ NrVBlanks = MathUtils::round_int(VBlankTime * static_cast<double>(m_fps));
+
+ //save the timestamp of this vblank so we can calculate how many happened next time
+ m_LastVBlankTime = frameTimeNanos;
+
+ //update the vblank timestamp, update the clock and send a signal that we got a vblank
+ UpdateClock(NrVBlanks, frameTimeNanos, m_refClock);
+}
diff --git a/xbmc/windowing/android/VideoSyncAndroid.h b/xbmc/windowing/android/VideoSyncAndroid.h
new file mode 100644
index 0000000..6bf76c0
--- /dev/null
+++ b/xbmc/windowing/android/VideoSyncAndroid.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "guilib/DispResource.h"
+#include "windowing/VideoSync.h"
+
+class CVideoSyncAndroid : public CVideoSync, IDispResource
+{
+public:
+ CVideoSyncAndroid(void *clock) : CVideoSync(clock), m_LastVBlankTime(0) {}
+
+ // CVideoSync interface
+ bool Setup(PUPDATECLOCK func) override;
+ void Run(CEvent& stop) override;
+ void Cleanup() override;
+ float GetFps() override;
+
+ // IDispResource interface
+ void OnResetDisplay() override;
+
+ // Choreographer callback
+ void FrameCallback(int64_t frameTimeNanos);
+
+private:
+ int64_t m_LastVBlankTime; //timestamp of the last vblank, used for calculating how many vblanks happened
+ CEvent m_abortEvent;
+};
diff --git a/xbmc/windowing/android/WinEventsAndroid.cpp b/xbmc/windowing/android/WinEventsAndroid.cpp
new file mode 100644
index 0000000..a384902
--- /dev/null
+++ b/xbmc/windowing/android/WinEventsAndroid.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2010-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinEventsAndroid.h"
+
+#include "ServiceBroker.h"
+#include "application/AppInboundProtocol.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "input/InputManager.h"
+#include "input/XBMC_vkeys.h"
+#include "utils/log.h"
+
+#include <mutex>
+
+#define ALMOST_ZERO 0.125f
+enum {
+ EVENT_STATE_TEST,
+ EVENT_STATE_HOLD,
+ EVENT_STATE_REPEAT
+};
+
+/************************************************************************/
+/************************************************************************/
+static bool different_event(XBMC_Event &curEvent, XBMC_Event &newEvent)
+{
+ // different type
+ if (curEvent.type != newEvent.type)
+ return true;
+
+ return false;
+}
+
+/************************************************************************/
+/************************************************************************/
+CWinEventsAndroid::CWinEventsAndroid()
+: CThread("CWinEventsAndroid")
+{
+ CLog::Log(LOGDEBUG, "CWinEventsAndroid::CWinEventsAndroid");
+ Create();
+}
+
+CWinEventsAndroid::~CWinEventsAndroid()
+{
+ m_bStop = true;
+ StopThread(true);
+}
+
+void CWinEventsAndroid::MessagePush(XBMC_Event *newEvent)
+{
+ std::unique_lock<CCriticalSection> lock(m_eventsCond);
+
+ m_events.push_back(*newEvent);
+}
+
+void CWinEventsAndroid::MessagePushRepeat(XBMC_Event *repeatEvent)
+{
+ std::unique_lock<CCriticalSection> lock(m_eventsCond);
+
+ std::list<XBMC_Event>::iterator itt;
+ for (itt = m_events.begin(); itt != m_events.end(); ++itt)
+ {
+ // we have events pending, if we we just
+ // repush, we might push the repeat event
+ // in back of a canceling non-active event.
+ // do not repush if pending are different event.
+ if (different_event(*itt, *repeatEvent))
+ return;
+ }
+
+ // is a repeat, push it
+ m_events.push_back(*repeatEvent);
+}
+
+bool CWinEventsAndroid::MessagePump()
+{
+ bool ret = false;
+
+ // Do not always loop, only pump the initial queued count events. else if ui keep pushing
+ // events the loop won't finish then it will block xbmc main message loop.
+ for (size_t pumpEventCount = GetQueueSize(); pumpEventCount > 0; --pumpEventCount)
+ {
+ // Pop up only one event per time since in App::OnEvent it may init modal dialog which init
+ // deeper message loop and call the deeper MessagePump from there.
+ XBMC_Event pumpEvent;
+ {
+ std::unique_lock<CCriticalSection> lock(m_eventsCond);
+ if (m_events.empty())
+ return ret;
+ pumpEvent = m_events.front();
+ m_events.pop_front();
+ }
+
+ std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
+ if (appPort)
+ ret |= appPort->OnEvent(pumpEvent);
+
+ if (pumpEvent.type == XBMC_MOUSEBUTTONUP)
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_UNFOCUS_ALL, 0, 0, 0, 0);
+ }
+
+ return ret;
+}
+
+size_t CWinEventsAndroid::GetQueueSize()
+{
+ std::unique_lock<CCriticalSection> lock(m_eventsCond);
+ return m_events.size();
+}
+
+void CWinEventsAndroid::Process()
+{
+ uint32_t timeout = 10;
+ uint32_t holdTimeout = 500;
+ uint32_t repeatTimeout = 100;
+ uint32_t repeatDuration = 0;
+
+ XBMC_Event cur_event;
+ int state = EVENT_STATE_TEST;
+ while (!m_bStop)
+ {
+ // run a 10ms (timeout) wait cycle
+ CThread::Sleep(std::chrono::milliseconds(timeout));
+
+ std::unique_lock<CCriticalSection> lock(m_lasteventCond);
+
+ switch(state)
+ {
+ default:
+ case EVENT_STATE_TEST:
+ // non-active event, eat it
+ if (!m_lastevent.empty())
+ m_lastevent.pop();
+ break;
+
+ case EVENT_STATE_HOLD:
+ repeatDuration += timeout;
+ if (!m_lastevent.empty())
+ {
+ if (different_event(cur_event, m_lastevent.front()))
+ {
+ // different event, cycle back to test
+ state = EVENT_STATE_TEST;
+ break;
+ }
+
+ // same event, eat it
+ m_lastevent.pop();
+ }
+
+ if (repeatDuration >= holdTimeout)
+ {
+ CLog::Log(LOGDEBUG, "hold ->repeat, size({}), repeatDuration({})", m_lastevent.size(),
+ repeatDuration);
+ state = EVENT_STATE_REPEAT;
+ }
+ break;
+
+ case EVENT_STATE_REPEAT:
+ repeatDuration += timeout;
+ if (!m_lastevent.empty())
+ {
+ if (different_event(cur_event, m_lastevent.front()))
+ {
+ // different event, cycle back to test
+ state = EVENT_STATE_TEST;
+ break;
+ }
+
+ // same event, eat it
+ m_lastevent.pop();
+ }
+
+ if (repeatDuration >= holdTimeout)
+ {
+ // this is a repeat, push it
+ MessagePushRepeat(&cur_event);
+ // assuming holdTimeout > repeatTimeout,
+ // just subtract the repeatTimeout
+ // to get the next cycle time
+ repeatDuration -= repeatTimeout;
+ }
+ break;
+ }
+ }
+}
diff --git a/xbmc/windowing/android/WinEventsAndroid.h b/xbmc/windowing/android/WinEventsAndroid.h
new file mode 100644
index 0000000..62b04b5
--- /dev/null
+++ b/xbmc/windowing/android/WinEventsAndroid.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "threads/CriticalSection.h"
+#include "threads/Event.h"
+#include "threads/Thread.h"
+#include "windowing/WinEvents.h"
+
+#include <list>
+#include <queue>
+#include <string>
+#include <vector>
+
+class CWinEventsAndroid : public IWinEvents, public CThread
+{
+public:
+ CWinEventsAndroid();
+ ~CWinEventsAndroid() override;
+
+ void MessagePush(XBMC_Event *newEvent);
+ void MessagePushRepeat(XBMC_Event *repeatEvent);
+ bool MessagePump() override;
+
+private:
+ size_t GetQueueSize();
+
+ // for CThread
+ void Process() override;
+
+ CCriticalSection m_eventsCond;
+ std::list<XBMC_Event> m_events;
+
+ CCriticalSection m_lasteventCond;
+ std::queue<XBMC_Event> m_lastevent;
+};
+
diff --git a/xbmc/windowing/android/WinSystemAndroid.cpp b/xbmc/windowing/android/WinSystemAndroid.cpp
new file mode 100644
index 0000000..d8ddbb0
--- /dev/null
+++ b/xbmc/windowing/android/WinSystemAndroid.cpp
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinSystemAndroid.h"
+
+#include "OSScreenSaverAndroid.h"
+#include "ServiceBroker.h"
+#include "WinEventsAndroid.h"
+#include "addons/interfaces/platform/android/System.h"
+#include "cores/RetroPlayer/process/android/RPProcessInfoAndroid.h"
+#include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGLES.h"
+#include "cores/VideoPlayer/DVDCodecs/Audio/DVDAudioCodecAndroidMediaCodec.h"
+#include "cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.h"
+#include "cores/VideoPlayer/Process/android/ProcessInfoAndroid.h"
+#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererMediaCodec.h"
+#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererMediaCodecSurface.h"
+#include "guilib/DispResource.h"
+#include "rendering/gles/ScreenshotSurfaceGLES.h"
+#include "settings/DisplaySettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "system_egl.h"
+#include "utils/HDRCapabilities.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/Resolution.h"
+
+#include "platform/android/activity/XBMCApp.h"
+#include "platform/android/media/decoderfilter/MediaCodecDecoderFilterManager.h"
+#include "platform/android/media/drm/MediaDrmCryptoSession.h"
+
+#include <float.h>
+#include <mutex>
+#include <string.h>
+
+#include <EGL/eglplatform.h>
+
+using namespace KODI;
+using namespace std::chrono_literals;
+
+CWinSystemAndroid::CWinSystemAndroid()
+{
+ m_displayWidth = 0;
+ m_displayHeight = 0;
+
+ m_stereo_mode = RENDER_STEREO_MODE_OFF;
+
+ m_dispResetTimer = new CTimer(this);
+
+ m_android = nullptr;
+
+ m_winEvents.reset(new CWinEventsAndroid());
+}
+
+CWinSystemAndroid::~CWinSystemAndroid()
+{
+ delete m_dispResetTimer;
+}
+
+bool CWinSystemAndroid::InitWindowSystem()
+{
+ m_nativeDisplay = EGL_DEFAULT_DISPLAY;
+
+ m_android = new CAndroidUtils();
+
+ m_decoderFilterManager = new(CMediaCodecDecoderFilterManager);
+ CServiceBroker::RegisterDecoderFilterManager(m_decoderFilterManager);
+
+ CDVDVideoCodecAndroidMediaCodec::Register();
+ CDVDAudioCodecAndroidMediaCodec::Register();
+
+ CLinuxRendererGLES::Register();
+ RETRO::CRPProcessInfoAndroid::Register();
+ RETRO::CRPProcessInfoAndroid::RegisterRendererFactory(new RETRO::CRendererFactoryOpenGLES);
+ CRendererMediaCodec::Register();
+ CRendererMediaCodecSurface::Register();
+ ADDON::Interface_Android::Register();
+ DRM::CMediaDrmCryptoSession::Register();
+ VIDEOPLAYER::CProcessInfoAndroid::Register();
+
+ CScreenshotSurfaceGLES::Register();
+
+ return CWinSystemBase::InitWindowSystem();
+}
+
+bool CWinSystemAndroid::DestroyWindowSystem()
+{
+ CLog::Log(LOGINFO, "CWinSystemAndroid::{}", __FUNCTION__);
+
+ delete m_android;
+ m_android = nullptr;
+
+ CServiceBroker::RegisterDecoderFilterManager(nullptr);
+ delete m_decoderFilterManager;
+ m_decoderFilterManager = nullptr;
+
+ return true;
+}
+
+bool CWinSystemAndroid::CreateNewWindow(const std::string& name,
+ bool fullScreen,
+ RESOLUTION_INFO& res)
+{
+ RESOLUTION_INFO current_resolution;
+ current_resolution.iWidth = current_resolution.iHeight = 0;
+ RENDER_STEREO_MODE stereo_mode = CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode();
+
+ m_nWidth = res.iWidth;
+ m_nHeight = res.iHeight;
+ m_displayWidth = res.iScreenWidth;
+ m_displayHeight = res.iScreenHeight;
+ m_fRefreshRate = res.fRefreshRate;
+
+ if ((m_bWindowCreated && m_android->GetNativeResolution(&current_resolution)) &&
+ current_resolution.iWidth == res.iWidth && current_resolution.iHeight == res.iHeight &&
+ current_resolution.iScreenWidth == res.iScreenWidth && current_resolution.iScreenHeight == res.iScreenHeight &&
+ m_bFullScreen == fullScreen && current_resolution.fRefreshRate == res.fRefreshRate &&
+ (current_resolution.dwFlags & D3DPRESENTFLAG_MODEMASK) == (res.dwFlags & D3DPRESENTFLAG_MODEMASK) &&
+ m_stereo_mode == stereo_mode)
+ {
+ CLog::Log(LOGDEBUG, "CWinSystemAndroid::CreateNewWindow: No need to create a new window");
+ return true;
+ }
+
+ m_dispResetTimer->Stop();
+ m_HdmiModeTriggered = false;
+
+ m_stereo_mode = stereo_mode;
+ m_bFullScreen = fullScreen;
+
+ m_nativeWindow = CXBMCApp::Get().GetNativeWindow(2000);
+ if (!m_nativeWindow)
+ {
+ CLog::Log(LOGERROR, "CWinSystemAndroid::CreateNewWindow: failed");
+ return false;
+ }
+
+ m_android->SetNativeResolution(res);
+
+ return true;
+}
+
+bool CWinSystemAndroid::DestroyWindow()
+{
+ CLog::Log(LOGINFO, "CWinSystemAndroid::{}", __FUNCTION__);
+ m_nativeWindow.reset();
+ m_bWindowCreated = false;
+ return true;
+}
+
+void CWinSystemAndroid::UpdateResolutions()
+{
+ UpdateResolutions(true);
+}
+
+void CWinSystemAndroid::UpdateResolutions(bool bUpdateDesktopRes)
+{
+ CWinSystemBase::UpdateResolutions();
+
+ std::vector<RESOLUTION_INFO> resolutions;
+ if (!m_android->ProbeResolutions(resolutions) || resolutions.empty())
+ {
+ CLog::Log(LOGWARNING, "CWinSystemAndroid::{} failed.", __FUNCTION__);
+ }
+
+ const RESOLUTION_INFO resWindow = CDisplaySettings::GetInstance().GetResolutionInfo(RES_WINDOW);
+
+ RESOLUTION_INFO resDesktop;
+ if (bUpdateDesktopRes)
+ {
+ // ProbeResolutions includes already all resolutions.
+ // Only get desktop resolution so we can replace Kodi's desktop res.
+ RESOLUTION_INFO curDisplay;
+ if (m_android->GetNativeResolution(&curDisplay))
+ resDesktop = curDisplay;
+ }
+ else
+ {
+ // Do not replace Kodi's desktop res, just update the data.
+ resDesktop = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP);
+ }
+
+ CDisplaySettings::GetInstance().ClearCustomResolutions();
+
+ for (auto& res : resolutions)
+ {
+ CServiceBroker::GetWinSystem()->GetGfxContext().ResetOverscan(res);
+ CDisplaySettings::GetInstance().AddResolutionInfo(res);
+
+ if (resDesktop.iScreenWidth == res.iScreenWidth &&
+ resDesktop.iScreenHeight == res.iScreenHeight &&
+ (resDesktop.dwFlags & D3DPRESENTFLAG_MODEMASK) == (res.dwFlags & D3DPRESENTFLAG_MODEMASK) &&
+ std::fabs(resDesktop.fRefreshRate - res.fRefreshRate) < FLT_EPSILON)
+ {
+ CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP) = res;
+ }
+
+ if (resWindow.iScreenWidth == res.iScreenWidth &&
+ resWindow.iScreenHeight == res.iScreenHeight &&
+ (resWindow.dwFlags & D3DPRESENTFLAG_MODEMASK) == (res.dwFlags & D3DPRESENTFLAG_MODEMASK) &&
+ std::fabs(resWindow.fRefreshRate - res.fRefreshRate) < FLT_EPSILON)
+ {
+ CDisplaySettings::GetInstance().GetResolutionInfo(RES_WINDOW) = res;
+ }
+ }
+
+ CDisplaySettings::GetInstance().ApplyCalibrations();
+}
+
+void CWinSystemAndroid::OnTimeout()
+{
+ m_HdmiModeTriggered = true;
+}
+
+void CWinSystemAndroid::InitiateModeChange()
+{
+ auto delay =
+ std::chrono::milliseconds(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
+ "videoscreen.delayrefreshchange") *
+ 100);
+
+ if (delay < 2000ms)
+ delay = 2000ms;
+ m_dispResetTimer->Stop();
+ m_dispResetTimer->Start(delay);
+
+ SetHdmiState(false);
+}
+
+void CWinSystemAndroid::SetHdmiState(bool connected)
+{
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ CLog::Log(LOGDEBUG, "CWinSystemAndroid::SetHdmiState: state: {}", static_cast<int>(connected));
+
+ if (connected)
+ {
+ if (m_dispResetTimer->IsRunning())
+ {
+ // We stop the timer if OS supports HDMI_AUDIO_PLUG intent
+ // and configured delay is smaller than the time HDMI_PLUG took.
+ // Note that timer is always started with minimum of 2 seconds
+ // regardless if the configured delay is smaller
+ if (m_dispResetTimer->GetElapsedMilliseconds() >=
+ CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
+ "videoscreen.delayrefreshchange") *
+ 100)
+ m_dispResetTimer->Stop();
+ else
+ return;
+ }
+
+ for (auto resource : m_resources)
+ resource->OnResetDisplay();
+ m_dispResetTimer->Stop();
+ m_HdmiModeTriggered = false;
+ }
+ else
+ {
+ for (auto resource : m_resources)
+ resource->OnLostDisplay();
+ }
+}
+
+void CWinSystemAndroid::UpdateDisplayModes()
+{
+ // re-fetch display modes
+ m_android->UpdateDisplayModes();
+
+ if (m_nativeWindow)
+ {
+ // update display settings
+ UpdateResolutions(false);
+ }
+}
+
+bool CWinSystemAndroid::Hide()
+{
+ return false;
+}
+
+bool CWinSystemAndroid::Show(bool raise)
+{
+ return false;
+}
+
+void CWinSystemAndroid::Register(IDispResource *resource)
+{
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ m_resources.push_back(resource);
+}
+
+void CWinSystemAndroid::Unregister(IDispResource *resource)
+{
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ std::vector<IDispResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
+ if (i != m_resources.end())
+ m_resources.erase(i);
+}
+
+void CWinSystemAndroid::MessagePush(XBMC_Event *newEvent)
+{
+ dynamic_cast<CWinEventsAndroid&>(*m_winEvents).MessagePush(newEvent);
+}
+
+bool CWinSystemAndroid::MessagePump()
+{
+ return m_winEvents->MessagePump();
+}
+
+bool CWinSystemAndroid::IsHDRDisplay()
+{
+ return m_android->IsHDRDisplay();
+}
+
+std::unique_ptr<WINDOWING::IOSScreenSaver> CWinSystemAndroid::GetOSScreenSaverImpl()
+{
+ std::unique_ptr<KODI::WINDOWING::IOSScreenSaver> ret(new COSScreenSaverAndroid());
+ return ret;
+}
+
+CHDRCapabilities CWinSystemAndroid::GetDisplayHDRCapabilities() const
+{
+ return CAndroidUtils::GetDisplayHDRCapabilities();
+}
diff --git a/xbmc/windowing/android/WinSystemAndroid.h b/xbmc/windowing/android/WinSystemAndroid.h
new file mode 100644
index 0000000..0e39193
--- /dev/null
+++ b/xbmc/windowing/android/WinSystemAndroid.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "AndroidUtils.h"
+#include "rendering/gles/RenderSystemGLES.h"
+#include "system_egl.h"
+#include "threads/CriticalSection.h"
+#include "threads/Timer.h"
+#include "utils/HDRCapabilities.h"
+#include "windowing/WinSystem.h"
+
+#include <memory>
+
+class CDecoderFilterManager;
+class IDispResource;
+class CNativeWindow;
+
+class CWinSystemAndroid : public CWinSystemBase, public ITimerCallback
+{
+public:
+ CWinSystemAndroid();
+ ~CWinSystemAndroid() override;
+
+ bool InitWindowSystem() override;
+ bool DestroyWindowSystem() override;
+
+ bool CreateNewWindow(const std::string& name,
+ bool fullScreen,
+ RESOLUTION_INFO& res) override;
+
+ bool DestroyWindow() override;
+ void UpdateResolutions() override;
+
+ void InitiateModeChange();
+ bool IsHdmiModeTriggered() const { return m_HdmiModeTriggered; }
+ void SetHdmiState(bool connected);
+
+ void UpdateDisplayModes();
+
+ bool HasCursor() override { return false; }
+
+ bool Hide() override;
+ bool Show(bool raise = true) override;
+ void Register(IDispResource *resource) override;
+ void Unregister(IDispResource *resource) override;
+
+ void MessagePush(XBMC_Event *newEvent);
+
+ // winevents override
+ bool MessagePump() override;
+ bool IsHDRDisplay() override;
+
+ CHDRCapabilities GetDisplayHDRCapabilities() const override;
+
+protected:
+ std::unique_ptr<KODI::WINDOWING::IOSScreenSaver> GetOSScreenSaverImpl() override;
+ void OnTimeout() override;
+
+ CAndroidUtils *m_android;
+
+ EGLDisplay m_nativeDisplay = EGL_NO_DISPLAY;
+ std::shared_ptr<CNativeWindow> m_nativeWindow;
+
+ int m_displayWidth;
+ int m_displayHeight;
+
+ RENDER_STEREO_MODE m_stereo_mode;
+
+ CTimer *m_dispResetTimer;
+
+ CCriticalSection m_resourceSection;
+ std::vector<IDispResource*> m_resources;
+ CDecoderFilterManager *m_decoderFilterManager;
+
+private:
+ bool m_HdmiModeTriggered = false;
+ void UpdateResolutions(bool bUpdateDesktopRes);
+};
diff --git a/xbmc/windowing/android/WinSystemAndroidGLESContext.cpp b/xbmc/windowing/android/WinSystemAndroidGLESContext.cpp
new file mode 100644
index 0000000..ac562ae
--- /dev/null
+++ b/xbmc/windowing/android/WinSystemAndroidGLESContext.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinSystemAndroidGLESContext.h"
+
+#include "ServiceBroker.h"
+#include "VideoSyncAndroid.h"
+#include "cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodec.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+#include "windowing/WindowSystemFactory.h"
+
+#include "platform/android/activity/XBMCApp.h"
+
+#include <EGL/eglext.h>
+#include <unistd.h>
+
+#include "PlatformDefs.h"
+
+void CWinSystemAndroidGLESContext::Register()
+{
+ KODI::WINDOWING::CWindowSystemFactory::RegisterWindowSystem(CreateWinSystem);
+}
+
+std::unique_ptr<CWinSystemBase> CWinSystemAndroidGLESContext::CreateWinSystem()
+{
+ return std::make_unique<CWinSystemAndroidGLESContext>();
+}
+
+bool CWinSystemAndroidGLESContext::InitWindowSystem()
+{
+ if (!CWinSystemAndroid::InitWindowSystem())
+ {
+ return false;
+ }
+
+ if (!m_pGLContext.CreateDisplay(m_nativeDisplay))
+ {
+ return false;
+ }
+
+ if (!m_pGLContext.InitializeDisplay(EGL_OPENGL_ES_API))
+ {
+ return false;
+ }
+
+ if (!m_pGLContext.ChooseConfig(EGL_OPENGL_ES2_BIT))
+ {
+ return false;
+ }
+
+ m_hasHDRConfig = m_pGLContext.ChooseConfig(EGL_OPENGL_ES2_BIT, 0, true);
+
+ m_hasEGL_BT2020_PQ_Colorspace_Extension =
+ CEGLUtils::HasExtension(m_pGLContext.GetEGLDisplay(), "EGL_EXT_gl_colorspace_bt2020_pq");
+ m_hasEGL_ST2086_Extension =
+ CEGLUtils::HasExtension(m_pGLContext.GetEGLDisplay(), "EGL_EXT_surface_SMPTE2086_metadata");
+
+ bool hasEGLHDRExtensions = m_hasEGL_BT2020_PQ_Colorspace_Extension && m_hasEGL_ST2086_Extension;
+
+ CLog::Log(LOGDEBUG,
+ "CWinSystemAndroidGLESContext::InitWindowSystem: HDRConfig: {}, HDRExtensions: {}",
+ static_cast<int>(m_hasHDRConfig), static_cast<int>(hasEGLHDRExtensions));
+
+ CEGLAttributesVec contextAttribs;
+ contextAttribs.Add({{EGL_CONTEXT_CLIENT_VERSION, 2}});
+
+ if (!m_pGLContext.CreateContext(contextAttribs))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool CWinSystemAndroidGLESContext::CreateNewWindow(const std::string& name,
+ bool fullScreen,
+ RESOLUTION_INFO& res)
+{
+ m_pGLContext.DestroySurface();
+
+ if (!CWinSystemAndroid::CreateNewWindow(name, fullScreen, res))
+ {
+ return false;
+ }
+
+ if (!CreateSurface())
+ {
+ return false;
+ }
+
+ if (!m_pGLContext.BindContext())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool CWinSystemAndroidGLESContext::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
+{
+ CRenderSystemGLES::ResetRenderSystem(newWidth, newHeight);
+ return true;
+}
+
+bool CWinSystemAndroidGLESContext::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
+{
+ CreateNewWindow("", fullScreen, res);
+ CRenderSystemGLES::ResetRenderSystem(res.iWidth, res.iHeight);
+ return true;
+}
+
+void CWinSystemAndroidGLESContext::SetVSyncImpl(bool enable)
+{
+ // We use Choreographer for timing
+ m_pGLContext.SetVSync(false);
+}
+
+void CWinSystemAndroidGLESContext::PresentRenderImpl(bool rendered)
+{
+ if (!m_nativeWindow)
+ {
+ usleep(10000);
+ return;
+ }
+
+ // Mode change finalization was triggered by timer
+ if (IsHdmiModeTriggered())
+ SetHdmiState(true);
+
+ // Ignore EGL_BAD_SURFACE: It seems to happen during/after mode changes, but
+ // we can't actually do anything about it
+ if (rendered && !m_pGLContext.TrySwapBuffers())
+ CEGLUtils::Log(LOGERROR, "eglSwapBuffers failed");
+
+ CXBMCApp::Get().WaitVSync(1000);
+}
+
+float CWinSystemAndroidGLESContext::GetFrameLatencyAdjustment()
+{
+ return CXBMCApp::Get().GetFrameLatencyMs();
+}
+
+EGLDisplay CWinSystemAndroidGLESContext::GetEGLDisplay() const
+{
+ return m_pGLContext.GetEGLDisplay();
+}
+
+EGLSurface CWinSystemAndroidGLESContext::GetEGLSurface() const
+{
+ return m_pGLContext.GetEGLSurface();
+}
+
+EGLContext CWinSystemAndroidGLESContext::GetEGLContext() const
+{
+ return m_pGLContext.GetEGLContext();
+}
+
+EGLConfig CWinSystemAndroidGLESContext::GetEGLConfig() const
+{
+ return m_pGLContext.GetEGLConfig();
+}
+
+std::unique_ptr<CVideoSync> CWinSystemAndroidGLESContext::GetVideoSync(void *clock)
+{
+ std::unique_ptr<CVideoSync> pVSync(new CVideoSyncAndroid(clock));
+ return pVSync;
+}
+
+bool CWinSystemAndroidGLESContext::CreateSurface()
+{
+ if (!m_pGLContext.CreateSurface(static_cast<EGLNativeWindowType>(m_nativeWindow->m_window),
+ m_HDRColorSpace))
+ {
+ if (m_HDRColorSpace != EGL_NONE)
+ {
+ m_HDRColorSpace = EGL_NONE;
+ m_displayMetadata = nullptr;
+ m_lightMetadata = nullptr;
+ if (!m_pGLContext.CreateSurface(static_cast<EGLNativeWindowType>(m_nativeWindow->m_window)))
+ return false;
+ }
+ else
+ return false;
+ }
+
+#if EGL_EXT_surface_SMPTE2086_metadata
+ if (m_displayMetadata)
+ {
+ m_pGLContext.SurfaceAttrib(EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT, static_cast<int>(av_q2d(m_displayMetadata->display_primaries[0][0]) * EGL_METADATA_SCALING_EXT + 0.5));
+ m_pGLContext.SurfaceAttrib(EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT, static_cast<int>(av_q2d(m_displayMetadata->display_primaries[0][1]) * EGL_METADATA_SCALING_EXT + 0.5));
+ m_pGLContext.SurfaceAttrib(EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT, static_cast<int>(av_q2d(m_displayMetadata->display_primaries[1][0]) * EGL_METADATA_SCALING_EXT + 0.5));
+ m_pGLContext.SurfaceAttrib(EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT, static_cast<int>(av_q2d(m_displayMetadata->display_primaries[1][1]) * EGL_METADATA_SCALING_EXT + 0.5));
+ m_pGLContext.SurfaceAttrib(EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT, static_cast<int>(av_q2d(m_displayMetadata->display_primaries[2][0]) * EGL_METADATA_SCALING_EXT + 0.5));
+ m_pGLContext.SurfaceAttrib(EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT, static_cast<int>(av_q2d(m_displayMetadata->display_primaries[2][1]) * EGL_METADATA_SCALING_EXT + 0.5));
+ m_pGLContext.SurfaceAttrib(EGL_SMPTE2086_WHITE_POINT_X_EXT, static_cast<int>(av_q2d(m_displayMetadata->white_point[0]) * EGL_METADATA_SCALING_EXT + 0.5));
+ m_pGLContext.SurfaceAttrib(EGL_SMPTE2086_WHITE_POINT_Y_EXT, static_cast<int>(av_q2d(m_displayMetadata->white_point[1]) * EGL_METADATA_SCALING_EXT + 0.5));
+ m_pGLContext.SurfaceAttrib(EGL_SMPTE2086_MAX_LUMINANCE_EXT, static_cast<int>(av_q2d(m_displayMetadata->max_luminance) * EGL_METADATA_SCALING_EXT + 0.5));
+ m_pGLContext.SurfaceAttrib(EGL_SMPTE2086_MIN_LUMINANCE_EXT, static_cast<int>(av_q2d(m_displayMetadata->min_luminance) * EGL_METADATA_SCALING_EXT + 0.5));
+ }
+ if (m_lightMetadata)
+ {
+ m_pGLContext.SurfaceAttrib(EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT, static_cast<int>(m_lightMetadata->MaxCLL * EGL_METADATA_SCALING_EXT));
+ m_pGLContext.SurfaceAttrib(EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT, static_cast<int>(m_lightMetadata->MaxFALL * EGL_METADATA_SCALING_EXT));
+ }
+#endif
+ return true;
+}
+
+bool CWinSystemAndroidGLESContext::IsHDRDisplay()
+{
+ return m_hasHDRConfig && (m_hasEGL_BT2020_PQ_Colorspace_Extension || m_hasEGL_ST2086_Extension) &&
+ CWinSystemAndroid::IsHDRDisplay();
+}
+
+bool CWinSystemAndroidGLESContext::SetHDR(const VideoPicture* videoPicture)
+{
+ if (!CWinSystemAndroid::IsHDRDisplay() || !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(SETTING_WINSYSTEM_IS_HDR_DISPLAY))
+ return false;
+
+ EGLint HDRColorSpace = 0;
+
+#if EGL_EXT_gl_colorspace_bt2020_linear
+ if (m_hasHDRConfig && m_hasEGL_BT2020_PQ_Colorspace_Extension && m_hasEGL_ST2086_Extension)
+ {
+ HDRColorSpace = EGL_NONE;
+ if (videoPicture && videoPicture->hasDisplayMetadata)
+ {
+ switch (videoPicture->color_space)
+ {
+ case AVCOL_SPC_BT2020_NCL:
+ case AVCOL_SPC_BT2020_CL:
+ case AVCOL_SPC_BT709:
+ HDRColorSpace = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+ break;
+ default:
+ m_displayMetadata = nullptr;
+ m_lightMetadata = nullptr;
+ }
+ }
+ else
+ {
+ m_displayMetadata = nullptr;
+ m_lightMetadata = nullptr;
+ }
+
+ if (HDRColorSpace != m_HDRColorSpace)
+ {
+ CLog::Log(LOGDEBUG, "CWinSystemAndroidGLESContext::SetHDR: ColorSpace: {}", HDRColorSpace);
+
+ m_HDRColorSpace = HDRColorSpace;
+ m_displayMetadata = m_HDRColorSpace == EGL_NONE ? nullptr : std::unique_ptr<AVMasteringDisplayMetadata>(new AVMasteringDisplayMetadata(videoPicture->displayMetadata));
+ // TODO: discuss with NVIDIA why this prevent turning HDR display off
+ //m_lightMetadata = !videoPicture || m_HDRColorSpace == EGL_NONE ? nullptr : std::unique_ptr<AVContentLightMetadata>(new AVContentLightMetadata(videoPicture->lightMetadata));
+ m_pGLContext.DestroySurface();
+ CreateSurface();
+ m_pGLContext.BindContext();
+ }
+ }
+#endif
+
+ return m_HDRColorSpace == HDRColorSpace;
+}
diff --git a/xbmc/windowing/android/WinSystemAndroidGLESContext.h b/xbmc/windowing/android/WinSystemAndroidGLESContext.h
new file mode 100644
index 0000000..b04ff41
--- /dev/null
+++ b/xbmc/windowing/android/WinSystemAndroidGLESContext.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "WinSystemAndroid.h"
+#include "rendering/gles/RenderSystemGLES.h"
+#include "utils/EGLUtils.h"
+#include "utils/GlobalsHandling.h"
+
+struct AVMasteringDisplayMetadata;
+struct AVContentLightMetadata;
+
+class CWinSystemAndroidGLESContext : public CWinSystemAndroid, public CRenderSystemGLES
+{
+public:
+ CWinSystemAndroidGLESContext() = default;
+ ~CWinSystemAndroidGLESContext() override = default;
+
+ static void Register();
+ static std::unique_ptr<CWinSystemBase> CreateWinSystem();
+
+ // Implementation of CWinSystemBase via CWinSystemAndroid
+ CRenderSystemBase *GetRenderSystem() override { return this; }
+ bool InitWindowSystem() override;
+ bool CreateNewWindow(const std::string& name,
+ bool fullScreen,
+ RESOLUTION_INFO& res) override;
+
+ bool ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) override;
+ bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) override;
+
+ std::unique_ptr<CVideoSync> GetVideoSync(void* clock) override;
+
+ float GetFrameLatencyAdjustment() override;
+ bool IsHDRDisplay() override;
+ bool SetHDR(const VideoPicture* videoPicture) override;
+
+ EGLDisplay GetEGLDisplay() const;
+ EGLSurface GetEGLSurface() const;
+ EGLContext GetEGLContext() const;
+ EGLConfig GetEGLConfig() const;
+protected:
+ void SetVSyncImpl(bool enable) override;
+ void PresentRenderImpl(bool rendered) override;
+
+private:
+ bool CreateSurface();
+
+ CEGLContextUtils m_pGLContext;
+ bool m_hasHDRConfig = false;
+
+ std::unique_ptr<AVMasteringDisplayMetadata> m_displayMetadata;
+ std::unique_ptr<AVContentLightMetadata> m_lightMetadata;
+ EGLint m_HDRColorSpace = EGL_NONE;
+ bool m_hasEGL_ST2086_Extension = false;
+ bool m_hasEGL_BT2020_PQ_Colorspace_Extension = false;
+};