From 21912bb37d22edef271b8969d08380f43f9c1911 Mon Sep 17 00:00:00 2001 From: peak3d Date: Mon, 18 Mar 2019 18:01:05 +0100 Subject: Decoderfilter implementation --- cmake/treedata/android/subdirs.txt | 38 +-- cmake/treedata/common/drm.txt | 1 - cmake/treedata/common/media.txt | 2 + cmake/treedata/common/subdirs.txt | 1 - xbmc/ServiceBroker.cpp | 12 + xbmc/ServiceBroker.h | 5 + .../Video/DVDVideoCodecAndroidMediaCodec.cpp | 134 ++++----- xbmc/drm/CMakeLists.txt | 5 - xbmc/drm/CryptoSession.cpp | 27 -- xbmc/drm/CryptoSession.h | 52 ---- xbmc/interfaces/legacy/DrmCryptoSession.cpp | 2 +- xbmc/media/decoderfilter/CMakeLists.txt | 5 + xbmc/media/decoderfilter/DecoderFilterManager.cpp | 175 ++++++++++++ xbmc/media/decoderfilter/DecoderFilterManager.h | 140 ++++++++++ xbmc/media/drm/CMakeLists.txt | 5 + xbmc/media/drm/CryptoSession.cpp | 27 ++ xbmc/media/drm/CryptoSession.h | 52 ++++ xbmc/platform/android/drm/CMakeLists.txt | 5 - .../platform/android/drm/MediaDrmCryptoSession.cpp | 310 --------------------- xbmc/platform/android/drm/MediaDrmCryptoSession.h | 76 ----- .../android/media/decoderfilter/CMakeLists.txt | 5 + .../MediaCodecDecoderFilterManager.cpp | 58 ++++ .../decoderfilter/MediaCodecDecoderFilterManager.h | 18 ++ xbmc/platform/android/media/drm/CMakeLists.txt | 5 + .../android/media/drm/MediaDrmCryptoSession.cpp | 310 +++++++++++++++++++++ .../android/media/drm/MediaDrmCryptoSession.h | 76 +++++ xbmc/windowing/android/WinSystemAndroid.cpp | 54 ++-- xbmc/windowing/android/WinSystemAndroid.h | 2 + 28 files changed, 996 insertions(+), 606 deletions(-) delete mode 100644 cmake/treedata/common/drm.txt create mode 100644 cmake/treedata/common/media.txt delete mode 100644 xbmc/drm/CMakeLists.txt delete mode 100644 xbmc/drm/CryptoSession.cpp delete mode 100644 xbmc/drm/CryptoSession.h create mode 100644 xbmc/media/decoderfilter/CMakeLists.txt create mode 100644 xbmc/media/decoderfilter/DecoderFilterManager.cpp create mode 100644 xbmc/media/decoderfilter/DecoderFilterManager.h create mode 100644 xbmc/media/drm/CMakeLists.txt create mode 100644 xbmc/media/drm/CryptoSession.cpp create mode 100644 xbmc/media/drm/CryptoSession.h delete mode 100644 xbmc/platform/android/drm/CMakeLists.txt delete mode 100644 xbmc/platform/android/drm/MediaDrmCryptoSession.cpp delete mode 100644 xbmc/platform/android/drm/MediaDrmCryptoSession.h create mode 100644 xbmc/platform/android/media/decoderfilter/CMakeLists.txt create mode 100644 xbmc/platform/android/media/decoderfilter/MediaCodecDecoderFilterManager.cpp create mode 100644 xbmc/platform/android/media/decoderfilter/MediaCodecDecoderFilterManager.h create mode 100644 xbmc/platform/android/media/drm/CMakeLists.txt create mode 100644 xbmc/platform/android/media/drm/MediaDrmCryptoSession.cpp create mode 100644 xbmc/platform/android/media/drm/MediaDrmCryptoSession.h diff --git a/cmake/treedata/android/subdirs.txt b/cmake/treedata/android/subdirs.txt index 9c4232caff..53a04f04be 100644 --- a/cmake/treedata/android/subdirs.txt +++ b/cmake/treedata/android/subdirs.txt @@ -1,18 +1,20 @@ -xbmc/cores/RetroPlayer/process/android cores/RetroPlayer/process/android -xbmc/input/touch input/touch -xbmc/input/touch/generic input/touch/generic -xbmc/windowing/android windowing/android -xbmc/platform/posix platform/posix -xbmc/platform/posix/filesystem platform/posix/filesystem -xbmc/platform/posix/utils platform/posix/utils -xbmc/platform/linux platform/linux -xbmc/platform/linux/peripherals platform/linux/peripherals -xbmc/platform/android/activity platform/android/activity -xbmc/platform/android/bionic_supplement platform/android/bionicsupplement -xbmc/platform/android/drm platform/android/drm -xbmc/platform/android/filesystem platform/android/filesystem -xbmc/platform/android/network platform/android/network -xbmc/platform/android/peripherals platform/android/peripherals -xbmc/platform/android/powermanagement platform/android/powermanagement -xbmc/platform/android/storage platform/android/storage -xbmc/cores/VideoPlayer/Process/android/ cores/VideoPlayer/Process/android/ +xbmc/cores/RetroPlayer/process/android cores/RetroPlayer/process/android +xbmc/cores/VideoPlayer/Process/android/ cores/VideoPlayer/Process/android/ +xbmc/input/touch input/touch +xbmc/input/touch/generic input/touch/generic +xbmc/media/decoderfilter media/decoderfilter +xbmc/windowing/android windowing/android +xbmc/platform/posix platform/posix +xbmc/platform/posix/filesystem platform/posix/filesystem +xbmc/platform/posix/utils platform/posix/utils +xbmc/platform/linux platform/linux +xbmc/platform/linux/peripherals platform/linux/peripherals +xbmc/platform/android/activity platform/android/activity +xbmc/platform/android/bionic_supplement platform/android/bionicsupplement +xbmc/platform/android/media/drm platform/android/media/drm +xbmc/platform/android/media/decoderfilter platform/android/media/decoderfilter +xbmc/platform/android/filesystem platform/android/filesystem +xbmc/platform/android/network platform/android/network +xbmc/platform/android/peripherals platform/android/peripherals +xbmc/platform/android/powermanagement platform/android/powermanagement +xbmc/platform/android/storage platform/android/storage diff --git a/cmake/treedata/common/drm.txt b/cmake/treedata/common/drm.txt deleted file mode 100644 index fbcc906b9b..0000000000 --- a/cmake/treedata/common/drm.txt +++ /dev/null @@ -1 +0,0 @@ -xbmc/drm drm diff --git a/cmake/treedata/common/media.txt b/cmake/treedata/common/media.txt new file mode 100644 index 0000000000..519fece955 --- /dev/null +++ b/cmake/treedata/common/media.txt @@ -0,0 +1,2 @@ +xbmc/media media +xbmc/media/drm drm diff --git a/cmake/treedata/common/subdirs.txt b/cmake/treedata/common/subdirs.txt index 4a5f3a8434..083e8ef314 100644 --- a/cmake/treedata/common/subdirs.txt +++ b/cmake/treedata/common/subdirs.txt @@ -30,7 +30,6 @@ xbmc/input/keyboard/generic input/keyboard/generic xbmc/input/mouse input/mouse xbmc/input/mouse/generic input/mouse/generic xbmc/listproviders listproviders -xbmc/media media xbmc/messaging messaging xbmc/messaging/helpers messagingHelpers xbmc/pictures pictures diff --git a/xbmc/ServiceBroker.cpp b/xbmc/ServiceBroker.cpp index 8bf3f087eb..f5265dffcb 100644 --- a/xbmc/ServiceBroker.cpp +++ b/xbmc/ServiceBroker.cpp @@ -251,3 +251,15 @@ void CServiceBroker::UnregisterAppPort() { m_pAppPort.reset(); } + + +CDecoderFilterManager* CServiceBroker::m_decoderFilterManager = nullptr; +void CServiceBroker::RegisterDecoderFilterManager(CDecoderFilterManager* manager) +{ + m_decoderFilterManager = manager; +} + +CDecoderFilterManager* CServiceBroker::GetDecoderFilterManager() +{ + return m_decoderFilterManager; +} diff --git a/xbmc/ServiceBroker.h b/xbmc/ServiceBroker.h index 9e11d323d5..66a3b7eec9 100644 --- a/xbmc/ServiceBroker.h +++ b/xbmc/ServiceBroker.h @@ -52,6 +52,7 @@ class CEventLog; class CGUIComponent; class CAppInboundProtocol; class CSettingsComponent; +class CDecoderFilterManager; namespace KODI { @@ -127,6 +128,9 @@ public: static void RegisterAppPort(std::shared_ptr port); static void UnregisterAppPort(); + static void RegisterDecoderFilterManager(CDecoderFilterManager* manager); + static CDecoderFilterManager* GetDecoderFilterManager(); + private: static std::shared_ptr m_pAnnouncementManager; static CGUIComponent* m_pGUI; @@ -134,4 +138,5 @@ private: static IAE* m_pActiveAE; static std::shared_ptr m_pAppPort; static CSettingsComponent* m_pSettingsComponent; + static CDecoderFilterManager* m_decoderFilterManager; }; diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp index eb51c164ba..70a565b438 100644 --- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp +++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp @@ -14,43 +14,43 @@ #include "DVDVideoCodecAndroidMediaCodec.h" +#include +#include + #include #include #include #include #include -#include -#include "Application.h" -#include "ServiceBroker.h" -#include "messaging/ApplicationMessenger.h" -#include "cores/VideoPlayer/Interface/Addon/TimingConstants.h" +#include +#include -#include "utils/BitstreamConverter.h" -#include "utils/BitstreamWriter.h" -#include "utils/CPUInfo.h" -#include "utils/log.h" +#include +#include "Application.h" #include "DVDCodecs/DVDFactoryCodec.h" #include "platform/android/activity/XBMCApp.h" #include "cores/VideoPlayer/VideoRenderers/RenderManager.h" #include "cores/VideoPlayer/VideoRenderers/RenderFlags.h" #include "cores/VideoPlayer/Interface/Addon/DemuxCrypto.h" +#include "cores/VideoPlayer/Interface/Addon/TimingConstants.h" #include "cores/VideoPlayer/Process/VideoBuffer.h" +#include "media/decoderfilter/DecoderFilterManager.h" +#include "messaging/ApplicationMessenger.h" #include "platform/android/activity/AndroidFeatures.h" #include "platform/android/activity/JNIXBMCSurfaceTextureOnFrameAvailableListener.h" +#include "ServiceBroker.h" #include "settings/AdvancedSettings.h" #include "settings/Settings.h" #include "settings/SettingsComponent.h" #include "system.h" - +#include "utils/BitstreamConverter.h" +#include "utils/BitstreamWriter.h" +#include "utils/CPUInfo.h" +#include "utils/log.h" #include "utils/TimeUtils.h" -#include -#include - -#include -#include #define XMEDIAFORMAT_KEY_ROTATION "rotation-degrees" #define XMEDIAFORMAT_KEY_SLICE "slice-height" @@ -73,31 +73,6 @@ enum MEDIACODEC_STATES MEDIACODEC_STATE_STOPPED }; -static bool IsBlacklisted(const std::string &name) -{ - static const char *blacklisted_decoders[] = { - // No software decoders - "OMX.google", - // For Rockchip non-standard components - "AVCDecoder", - "AVCDecoder_FLASH", - "FLVDecoder", - "M2VDecoder", - "M4vH263Decoder", - "RVDecoder", - "VC1Decoder", - "VPXDecoder", - // End of Rockchip - NULL - }; - for (const char **ptr = blacklisted_decoders; *ptr; ptr++) - { - if (!strnicmp(*ptr, name.c_str(), strlen(*ptr))) - return true; - } - return false; -} - static bool IsSupportedColorFormat(int color_format) { static const int supported_colorformats[] = { @@ -407,11 +382,6 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open - %s\n", "null size, cannot handle"); goto FAIL; } - else if (hints.stills) - { - // Won't work reliably - goto FAIL; - } else if (hints.orientation && m_render_surface && CJNIBase::GetSDKVersion() < 23) { CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open - %s\n", "Surface does not support orientation before API 23"); @@ -599,42 +569,7 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio } if (m_hints.cryptoSession) - { - CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::Open Initializing MediaCrypto"); - - const AMediaUUID *uuid(nullptr); - const AMediaUUID wvuuid = {0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE,0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED}; - const AMediaUUID pruuid = {0x9A,0x04,0xF0,0x79,0x98,0x40,0x42,0x86,0xAB,0x92,0xE6,0x5B,0xE0,0x88,0x5F,0x95}; - - if (m_hints.cryptoSession->keySystem == CRYPTO_SESSION_SYSTEM_WIDEVINE) - uuid = &wvuuid; - else if (m_hints.cryptoSession->keySystem == CRYPTO_SESSION_SYSTEM_PLAYREADY) - uuid = &pruuid; - else - { - CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open Unsupported crypto-keysystem %u", m_hints.cryptoSession->keySystem); - goto FAIL; - } - - m_crypto = AMediaCrypto_new(*uuid, m_hints.cryptoSession->sessionId, m_hints.cryptoSession->sessionIdSize); - - if (!m_crypto) - { - CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open: MediaCrypto creation failed"); - goto FAIL; - } needSecureDecoder = AMediaCrypto_requiresSecureDecoderComponent(m_mime.c_str()) && (m_hints.cryptoSession->flags & DemuxCryptoSession::FLAG_SECURE_DECODER) != 0; - } - - if (m_render_surface) - { - m_jnivideoview.reset(CJNIXBMCVideoView::createVideoView(this)); - if (!m_jnivideoview || !m_jnivideoview->waitForSurface(2000)) - { - CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec: VideoView creation failed!!"); - goto FAIL; - } - } m_codec = nullptr; m_colorFormat = -1; @@ -647,7 +582,7 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio continue; m_codecname = codec_info.getName(); - if (IsBlacklisted(m_codecname)) + if (!CServiceBroker::GetDecoderFilterManager()->isValid(m_codecname, m_hints)) continue; CLog::Log(LOGNOTICE, "CDVDVideoCodecAndroidMediaCodec::Open Testing codec:%s", m_codecname.c_str()); @@ -714,6 +649,43 @@ bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptio goto FAIL; } + if (m_hints.cryptoSession) + { + CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::Open Initializing MediaCrypto"); + + const AMediaUUID *uuid(nullptr); + const AMediaUUID wvuuid = {0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE,0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED}; + const AMediaUUID pruuid = {0x9A,0x04,0xF0,0x79,0x98,0x40,0x42,0x86,0xAB,0x92,0xE6,0x5B,0xE0,0x88,0x5F,0x95}; + + if (m_hints.cryptoSession->keySystem == CRYPTO_SESSION_SYSTEM_WIDEVINE) + uuid = &wvuuid; + else if (m_hints.cryptoSession->keySystem == CRYPTO_SESSION_SYSTEM_PLAYREADY) + uuid = &pruuid; + else + { + CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open Unsupported crypto-keysystem %u", m_hints.cryptoSession->keySystem); + goto FAIL; + } + + m_crypto = AMediaCrypto_new(*uuid, m_hints.cryptoSession->sessionId, m_hints.cryptoSession->sessionIdSize); + + if (!m_crypto) + { + CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open: MediaCrypto creation failed"); + goto FAIL; + } + } + + if (m_render_surface) + { + m_jnivideoview.reset(CJNIXBMCVideoView::createVideoView(this)); + if (!m_jnivideoview || !m_jnivideoview->waitForSurface(2000)) + { + CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec: VideoView creation failed!!"); + goto FAIL; + } + } + // setup a YUV420P VideoPicture buffer. // first make sure all properties are reset. m_videobuffer.Reset(); diff --git a/xbmc/drm/CMakeLists.txt b/xbmc/drm/CMakeLists.txt deleted file mode 100644 index 15b2a8429f..0000000000 --- a/xbmc/drm/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -set(SOURCES CryptoSession.cpp) - -set(HEADERS CryptoSession.h) - -core_add_library(drm) diff --git a/xbmc/drm/CryptoSession.cpp b/xbmc/drm/CryptoSession.cpp deleted file mode 100644 index 03c3f133e4..0000000000 --- a/xbmc/drm/CryptoSession.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 "CryptoSession.h" - -using namespace DRM; - -std::vector CCryptoSession::s_registeredInterfaces; - -void CCryptoSession::RegisterInterface(GET_CRYPTO_SESSION_INTERFACE_FN fn) -{ - s_registeredInterfaces.push_back(fn); -} - -CCryptoSession* CCryptoSession::GetCryptoSession(const std::string &UUID, const std::string &cipherAlgo, const std::string &macAlgo) -{ - CCryptoSession* retVal = nullptr; - for (auto fn : s_registeredInterfaces) - if ((retVal = fn(UUID, cipherAlgo, macAlgo))) - break; - return retVal; -} diff --git a/xbmc/drm/CryptoSession.h b/xbmc/drm/CryptoSession.h deleted file mode 100644 index 21ba599f6f..0000000000 --- a/xbmc/drm/CryptoSession.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include "commons/Buffer.h" - -namespace DRM -{ - class CCryptoSession; - - typedef CCryptoSession* (*GET_CRYPTO_SESSION_INTERFACE_FN)(const std::string &UUID, const std::string &cipherAlgo, const std::string &hmacAlgo); - - class CCryptoSession - { - public: - // Interface registration - static CCryptoSession* GetCryptoSession(const std::string &UUID, const std::string &cipherAlgo, const std::string &macAlgo); - virtual ~CCryptoSession() {}; - - // Interface methods - virtual XbmcCommons::Buffer GetKeyRequest(const XbmcCommons::Buffer &init, const std::string &mimeType, bool offlineKey, const std::map ¶meters) = 0; - virtual std::string GetPropertyString(const std::string &name) = 0; - virtual std::string ProvideKeyResponse(const XbmcCommons::Buffer &response) = 0; - virtual void RemoveKeys() = 0; - virtual void RestoreKeys(const std::string &keySetId) = 0; - virtual void SetPropertyString(const std::string &name, const std::string &value) = 0; - - // Crypto methods - virtual XbmcCommons::Buffer Decrypt(const XbmcCommons::Buffer &cipherKeyId, const XbmcCommons::Buffer &input, const XbmcCommons::Buffer &iv) = 0; - virtual XbmcCommons::Buffer Encrypt(const XbmcCommons::Buffer &cipherKeyId, const XbmcCommons::Buffer &input, const XbmcCommons::Buffer &iv) = 0; - virtual XbmcCommons::Buffer Sign(const XbmcCommons::Buffer &macKeyId, const XbmcCommons::Buffer &message) = 0; - virtual bool Verify(const XbmcCommons::Buffer &macKeyId, const XbmcCommons::Buffer &message, const XbmcCommons::Buffer &signature ) = 0; - - protected: - static void RegisterInterface(GET_CRYPTO_SESSION_INTERFACE_FN fn); - - private: - static std::vector s_registeredInterfaces; - }; - -} //namespace diff --git a/xbmc/interfaces/legacy/DrmCryptoSession.cpp b/xbmc/interfaces/legacy/DrmCryptoSession.cpp index f5c3677fa7..5bd4de163f 100644 --- a/xbmc/interfaces/legacy/DrmCryptoSession.cpp +++ b/xbmc/interfaces/legacy/DrmCryptoSession.cpp @@ -7,7 +7,7 @@ */ #include "DrmCryptoSession.h" -#include "drm/CryptoSession.h" +#include "media/drm/CryptoSession.h" using namespace XbmcCommons; diff --git a/xbmc/media/decoderfilter/CMakeLists.txt b/xbmc/media/decoderfilter/CMakeLists.txt new file mode 100644 index 0000000000..48febb7c34 --- /dev/null +++ b/xbmc/media/decoderfilter/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SOURCES DecoderFilterManager.cpp) + +set(HEADERS DecoderFilterManager.h) + +core_add_library(decoderfilter) diff --git a/xbmc/media/decoderfilter/DecoderFilterManager.cpp b/xbmc/media/decoderfilter/DecoderFilterManager.cpp new file mode 100644 index 0000000000..5b3dc0c2f2 --- /dev/null +++ b/xbmc/media/decoderfilter/DecoderFilterManager.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2013-2019 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. + */ + + + /** + * \file media\hwdecoder\DecoderFilterManager.cpp + * \brief Implements CDecoderFilterManager class. + * + */ + +#include "DecoderFilterManager.h" +#include "cores/VideoPlayer/DVDStreamInfo.h" + +#include "filesystem/File.h" +#include "threads/SingleLock.h" +#include "utils/log.h" +#include "utils/XMLUtils.h" +#include "Util.h" + +static const char* TAG_ROOT = "decoderfilter"; +static const char* TAG_FILTER = "filter"; +static const char* TAG_NAME = "name"; +static const char* TAG_GENERAL = "allowed"; +static const char* TAG_STILLS = "stills-allowed"; +static const char* TAG_DVD = "dvd-allowed"; +static const char* TAG_MINHEIGHT = "min-height"; +static const char* HWDFFileName = "special://masterprofile/decoderfilter.xml"; +static const char* CLASSNAME = "CDecoderFilter"; + +CDecoderFilter::CDecoderFilter(const std::string& name, uint32_t flags, int minHeight) + : m_name(name) + , m_flags(flags) + , m_minHeight(minHeight) +{ +} + +bool CDecoderFilter::isValid(const CDVDStreamInfo& streamInfo) const +{ + uint32_t flags = FLAG_GENERAL_ALLOWED; + + if (streamInfo.stills) + flags |= FLAG_STILLS_ALLOWED; + + if (streamInfo.dvd) + flags |= FLAG_DVD_ALLOWED; + + if ((flags & m_flags) != flags) + return false; + + // remove codec pitch for comparision + if (m_minHeight && (streamInfo.height & ~31) <= m_minHeight) + return false; + + return true; +} + +bool CDecoderFilter::Load(const TiXmlNode *node) +{ + bool flagBool = false; + + XMLUtils::GetString(node, TAG_NAME, m_name); + XMLUtils::GetBoolean(node, TAG_GENERAL, flagBool); + if (flagBool) + m_flags |= FLAG_GENERAL_ALLOWED; + flagBool = false; + + XMLUtils::GetBoolean(node, TAG_STILLS, flagBool); + if (flagBool) + m_flags |= FLAG_STILLS_ALLOWED; + flagBool = false; + + XMLUtils::GetBoolean(node, TAG_DVD, flagBool); + if (flagBool) + m_flags |= FLAG_DVD_ALLOWED; + + XMLUtils::GetInt(node, TAG_MINHEIGHT, m_minHeight); + + return true; +} + +bool CDecoderFilter::Save(TiXmlNode *node) const +{ + // Now write each of the pieces of information we need... + XMLUtils::SetString(node, TAG_NAME, m_name); + XMLUtils::SetBoolean(node, TAG_GENERAL, (m_flags & FLAG_GENERAL_ALLOWED) != 0); + XMLUtils::SetBoolean(node, TAG_STILLS, (m_flags & FLAG_STILLS_ALLOWED) != 0); + XMLUtils::SetBoolean(node, TAG_DVD, (m_flags & FLAG_DVD_ALLOWED) != 0); + XMLUtils::SetInt(node, TAG_MINHEIGHT, m_minHeight); + return true; +} + + +/****************************************/ + +bool CDecoderFilterManager::isValid(const std::string& name, const CDVDStreamInfo& streamInfo) +{ + CSingleLock lock(m_critical); + std::set::const_iterator filter(m_filters.find(name)); + return filter != m_filters.end() ? filter->isValid(streamInfo) : m_filters.empty(); +} + +void CDecoderFilterManager::add(const CDecoderFilter& filter) +{ + CSingleLock lock(m_critical); + std::pair::iterator, bool> res = m_filters.insert(filter); + m_dirty = m_dirty || res.second; +} + +bool CDecoderFilterManager::Load() +{ + CSingleLock lock(m_critical); + + m_filters.clear(); + + std::string fileName = CUtil::TranslateSpecialSource(HWDFFileName); + if (!XFILE::CFile::Exists(fileName)) + return true; + + CLog::Log(LOGNOTICE, "%s: loading filters from %s", CLASSNAME, fileName.c_str()); + + CXBMCTinyXML xmlDoc; + if (!xmlDoc.LoadFile(fileName)) + { + CLog::Log(LOGERROR, "%s: error loading: line %d, %s", CLASSNAME, xmlDoc.ErrorRow(), xmlDoc.ErrorDesc()); + return false; + } + + const TiXmlElement *pRootElement = xmlDoc.RootElement(); + if (!pRootElement || !StringUtils::EqualsNoCase(pRootElement->ValueStr(), TAG_ROOT)) + { + CLog::Log(LOGERROR, "%s: invalid root element (%s)", CLASSNAME, pRootElement->ValueStr().c_str()); + return false; + } + + const TiXmlElement *pFilter = pRootElement->FirstChildElement(TAG_FILTER); + while (pFilter) + { + CDecoderFilter filter(""); + if (filter.Load(pFilter)) + m_filters.insert(filter); + pFilter = pFilter->NextSiblingElement(TAG_FILTER); + } + return true; +} + +bool CDecoderFilterManager::Save() const +{ + CSingleLock lock(m_critical); + if (!m_dirty || m_filters.empty()) + return true; + + CXBMCTinyXML doc; + TiXmlElement xmlRootElement(TAG_ROOT); + TiXmlNode *pRoot = doc.InsertEndChild(xmlRootElement); + if (pRoot == NULL) + return false; + + for (const CDecoderFilter& filter : m_filters) + { + // Write the resolution tag + TiXmlElement filterElem(TAG_FILTER); + TiXmlNode *pNode = pRoot->InsertEndChild(filterElem); + if (pNode == NULL) + return false; + + filter.Save(pNode); + } + std::string fileName = CUtil::TranslateSpecialSource(HWDFFileName); + return doc.SaveFile(fileName); +} diff --git a/xbmc/media/decoderfilter/DecoderFilterManager.h b/xbmc/media/decoderfilter/DecoderFilterManager.h new file mode 100644 index 0000000000..ba43750b61 --- /dev/null +++ b/xbmc/media/decoderfilter/DecoderFilterManager.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2013-2019 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 + +/** + * \file platform\DecoderFilter.h + * \brief Declares CDecoderFilterManager which gives control about how / when to use platform decoder. + * + */ +#include +#include +#include + +#include "threads/CriticalSection.h" + +class TiXmlNode; +class CDVDStreamInfo; + +/** +* @class CDecoderFilter +* +* @brief Declaration of CDecoderFilter. +* +*/ +class CDecoderFilter +{ +public: + /** + * @brief Flags to control decoder validity. + */ + enum : uint32_t + { + FLAG_GENERAL_ALLOWED = 1, ///< early false exit if set + FLAG_STILLS_ALLOWED = 2, ///< early false exit if set and stream is marked as "has stillframes" + FLAG_DVD_ALLOWED = 4 ///< early false exit if set and stream is marked as dvd + }; + + /** + * \fn CDecoderFilter::CDecoderFilter(const std::string& name); + * \brief constructs a CDecoderFilter + * \param name decodername + * \return nothing. + */ + CDecoderFilter(const std::string& name) : m_name(name) {}; + + /** + * \fn CDecoderFilter::CDecoderFilter(const std::string& name, uint32_t flags, uint32_t maxWidth, uint32_t maxHeight); + * \brief constructs a CDecoderFilter + * \param name decodername + * \param flags collection of FLAG_ values, bitwise OR + * \param minHeight minimum height of stream allowed by this decoder + * \return nothing. + */ + CDecoderFilter(const std::string& name, uint32_t flags, int minHeight); + + virtual ~CDecoderFilter() = default; + + /** + * \fn CDecoderFilter::operator < (const CDecoderFilter& other); + * \brief used for sorting / replacing / find + */ + bool operator < (const CDecoderFilter& other) const { return m_name < other.m_name; }; + + /** + * \fn CDecoderFilter::isValid(const CDVDStreamInfo& streamInfo); + * \brief test if stream is allowed by filter. + * \return true if valid, false otherwise + */ + virtual bool isValid(const CDVDStreamInfo& streamInfo) const; + + /** + * \fn CDecoderFilter::Load(const TiXmlNode *settings); + * \brief load all members from XML node + * \param node filter node from where to get the values + * \return true if operation was successful, false on error + */ + virtual bool Load(const TiXmlNode *node); + + /** + * \fn CDecoderFilter::Save(TiXmlNode *settings); + * \brief store all members in XML node + * \param node a ready to use filter setting node + * \return true if operation was successful, false on error + */ + virtual bool Save(TiXmlNode *node) const; + + +private: + std::string m_name; + + uint32_t m_flags = 0; + int m_minHeight = 0; +}; + + +/** +* @class CDecoderFilterManager +* +* @brief Class which handles multiple CDecoderFilter elements. +* +*/ + +class CDecoderFilterManager +{ +public: + CDecoderFilterManager() { Load(); }; + virtual ~CDecoderFilterManager() { Save(); }; + + /** + * \fn bool CDecoderFilterManager::add(const CDecoderFilter& filter); + * \brief adds an CDecoderFilter if key [filter.name] is not yet existing. + * \param filter the decoder filter to add / replace. + * \return nothing. + */ + void add(const CDecoderFilter& filter); + + + /** + * \fn bool CDecoderFilterManager::validate(const std::string& name, const CDVDStreamInfo& streamInfo); + * \brief Validates if decoder with name [name] is allowed to be used. + * \param streamInfo Stream information used to validate(). + * \return true if HardwarDecoder could be used, false otherwise. + */ + bool isValid(const std::string& name, const CDVDStreamInfo& streamInfo); + +protected: + bool Load(); + bool Save() const; + +private: + bool m_dirty = false; + std::set m_filters; + mutable CCriticalSection m_critical; +}; diff --git a/xbmc/media/drm/CMakeLists.txt b/xbmc/media/drm/CMakeLists.txt new file mode 100644 index 0000000000..15b2a8429f --- /dev/null +++ b/xbmc/media/drm/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SOURCES CryptoSession.cpp) + +set(HEADERS CryptoSession.h) + +core_add_library(drm) diff --git a/xbmc/media/drm/CryptoSession.cpp b/xbmc/media/drm/CryptoSession.cpp new file mode 100644 index 0000000000..9e5e8888d7 --- /dev/null +++ b/xbmc/media/drm/CryptoSession.cpp @@ -0,0 +1,27 @@ +/* + * 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 "CryptoSession.h" + +using namespace DRM; + +std::vector CCryptoSession::s_registeredInterfaces; + +void CCryptoSession::RegisterInterface(GET_CRYPTO_SESSION_INTERFACE_FN fn) +{ + s_registeredInterfaces.push_back(fn); +} + +CCryptoSession* CCryptoSession::GetCryptoSession(const std::string& UUID, const std::string& cipherAlgo, const std::string& macAlgo) +{ + CCryptoSession* retVal = nullptr; + for (auto fn : s_registeredInterfaces) + if ((retVal = fn(UUID, cipherAlgo, macAlgo))) + break; + return retVal; +} diff --git a/xbmc/media/drm/CryptoSession.h b/xbmc/media/drm/CryptoSession.h new file mode 100644 index 0000000000..603907df61 --- /dev/null +++ b/xbmc/media/drm/CryptoSession.h @@ -0,0 +1,52 @@ +/* + * 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 +#include +#include +#include + +#include "commons/Buffer.h" + +namespace DRM +{ + class CCryptoSession; + + typedef CCryptoSession* (*GET_CRYPTO_SESSION_INTERFACE_FN)(const std::string& UUID, const std::string& cipherAlgo, const std::string& hmacAlgo); + + class CCryptoSession + { + public: + // Interface registration + static CCryptoSession* GetCryptoSession(const std::string& UUID, const std::string& cipherAlgo, const std::string& macAlgo); + virtual ~CCryptoSession() {}; + + // Interface methods + virtual XbmcCommons::Buffer GetKeyRequest(const XbmcCommons::Buffer& init, const std::string& mimeType, bool offlineKey, const std::map& parameters) = 0; + virtual std::string GetPropertyString(const std::string& name) = 0; + virtual std::string ProvideKeyResponse(const XbmcCommons::Buffer& response) = 0; + virtual void RemoveKeys() = 0; + virtual void RestoreKeys(const std::string& keySetId) = 0; + virtual void SetPropertyString(const std::string& name, const std::string& value) = 0; + + // Crypto methods + virtual XbmcCommons::Buffer Decrypt(const XbmcCommons::Buffer& cipherKeyId, const XbmcCommons::Buffer& input, const XbmcCommons::Buffer& iv) = 0; + virtual XbmcCommons::Buffer Encrypt(const XbmcCommons::Buffer& cipherKeyId, const XbmcCommons::Buffer& input, const XbmcCommons::Buffer& iv) = 0; + virtual XbmcCommons::Buffer Sign(const XbmcCommons::Buffer& macKeyId, const XbmcCommons::Buffer& message) = 0; + virtual bool Verify(const XbmcCommons::Buffer& macKeyId, const XbmcCommons::Buffer& message, const XbmcCommons::Buffer& signature ) = 0; + + protected: + static void RegisterInterface(GET_CRYPTO_SESSION_INTERFACE_FN fn); + + private: + static std::vector s_registeredInterfaces; + }; + +} //namespace diff --git a/xbmc/platform/android/drm/CMakeLists.txt b/xbmc/platform/android/drm/CMakeLists.txt deleted file mode 100644 index ed49ca12c3..0000000000 --- a/xbmc/platform/android/drm/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -set(SOURCES MediaDrmCryptoSession.cpp) - -set(HEADERS MediaDrmCryptoSession.h) - -core_add_library(anroid_drm) diff --git a/xbmc/platform/android/drm/MediaDrmCryptoSession.cpp b/xbmc/platform/android/drm/MediaDrmCryptoSession.cpp deleted file mode 100644 index 72f12a550d..0000000000 --- a/xbmc/platform/android/drm/MediaDrmCryptoSession.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/* - * 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 "MediaDrmCryptoSession.h" - -#include -#include -#include - -#include "filesystem/File.h" -#include "utils/StringUtils.h" -#include "utils/log.h" -#include "utils/Base64.h" - -using namespace DRM; -using namespace XbmcCommons; - - -class CharVecBuffer : public Buffer -{ -public: - inline CharVecBuffer(const Buffer &buf) : Buffer(buf) {}; - - inline CharVecBuffer(const std::vector &vec) - : Buffer(vec.size()) - { - memcpy(data(), vec.data(), vec.size()); - } - - inline operator std::vector() const - { - return std::vector(data(), data() + capacity()); - } -}; - - -void CMediaDrmCryptoSession::Register() -{ - CCryptoSession::RegisterInterface(CMediaDrmCryptoSession::Create); -} - -CMediaDrmCryptoSession::CMediaDrmCryptoSession(const std::string &UUID, const std::string &cipherAlgo, const std::string &macAlgo) - : m_mediaDrm(nullptr) - , m_cryptoSession(nullptr) - , m_cipherAlgo(cipherAlgo) - , m_macAlgo(macAlgo) - , m_hasKeys(false) - , m_sessionId(nullptr) -{ - if (!StringUtils::EqualsNoCase(UUID, "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed")) - throw std::runtime_error("mediaDrm: Invalid UUID size"); - - int64_t mostSigBits(0), leastSigBits(0); - const uint8_t uuidPtr[16] = {0xed,0xef,0x8b,0xa9,0x79,0xd6,0x4a,0xce,0xa3,0xc8,0x27,0xdc,0xd5,0x1d,0x21,0xed}; - for (unsigned int i(0); i < 8; ++i) - mostSigBits = (mostSigBits << 8) | uuidPtr[i]; - for (unsigned int i(8); i < 16; ++i) - leastSigBits = (leastSigBits << 8) | uuidPtr[i]; - - CJNIUUID uuid(mostSigBits, leastSigBits); - - m_mediaDrm = new CJNIMediaDrm(uuid); - - if (xbmc_jnienv()->ExceptionCheck()) - { - xbmc_jnienv()->ExceptionClear(); - CLog::Log(LOGERROR, "MediaDrm: Failure creating instance"); - throw std::runtime_error("Failure creating MediaDrm"); - } - - if (!OpenSession()) - { - CLog::Log(LOGERROR, "MediaDrm: Unable to create a session"); - throw std::runtime_error("Unable to create a session"); - } -} - -CMediaDrmCryptoSession::~CMediaDrmCryptoSession() -{ - if (!m_mediaDrm) - return; - - CloseSession(); - - m_mediaDrm->release(); - delete m_mediaDrm, m_mediaDrm = nullptr; -} - -CCryptoSession* CMediaDrmCryptoSession::Create(const std::string &UUID, const std::string &cipherAlgo, const std::string &macAlgo) -{ - CMediaDrmCryptoSession *res = nullptr;; - try - { - res = new CMediaDrmCryptoSession(UUID, cipherAlgo, macAlgo); - } - catch (std::runtime_error &e) - { - delete res, res = nullptr; - } - return res; -} - -/*****************/ - -// Interface methods -Buffer CMediaDrmCryptoSession::GetKeyRequest(const Buffer &init, - const std::string &mimeType, - bool offlineKey, - const std::map ¶meters) -{ - if (m_mediaDrm && m_sessionId) - { - CJNIMediaDrmKeyRequest req = m_mediaDrm->getKeyRequest(*m_sessionId, CharVecBuffer(init), mimeType, - offlineKey ? CJNIMediaDrm::KEY_TYPE_OFFLINE : CJNIMediaDrm::KEY_TYPE_STREAMING, parameters); - return CharVecBuffer(req.getData()); - } - - return Buffer(); -} - - -std::string CMediaDrmCryptoSession::GetPropertyString(const std::string &name) -{ - if (m_mediaDrm) - return m_mediaDrm->getPropertyString(name); - - return ""; -} - - -std::string CMediaDrmCryptoSession::ProvideKeyResponse(const Buffer &response) -{ - if (m_mediaDrm) - { - m_hasKeys = true; - std::vector res = m_mediaDrm->provideKeyResponse(*m_sessionId, CharVecBuffer(response)); - return std::string(res.data(), res.size()); - } - - return ""; -} - -void CMediaDrmCryptoSession::RemoveKeys() -{ - if (m_mediaDrm && m_sessionId && m_hasKeys) - { - CloseSession(); - OpenSession(); - } -} - -void CMediaDrmCryptoSession::RestoreKeys(const std::string &keySetId) -{ - if (m_mediaDrm && keySetId != m_keySetId) - { - m_mediaDrm->restoreKeys(*m_sessionId, std::vector(keySetId.begin(), keySetId.end())); - m_hasKeys = true; - m_keySetId = keySetId; - } -} - -void CMediaDrmCryptoSession::SetPropertyString(const std::string &name, const std::string &value) -{ - if (m_mediaDrm) - m_mediaDrm->setPropertyString(name, value); -} - -// Crypto methods -Buffer CMediaDrmCryptoSession::Decrypt(const Buffer &cipherKeyId, const Buffer &input, const Buffer &iv) -{ - if (m_cryptoSession) - return CharVecBuffer(m_cryptoSession->decrypt(CharVecBuffer(cipherKeyId), CharVecBuffer(input), CharVecBuffer(iv))); - - return Buffer(); -} - -Buffer CMediaDrmCryptoSession::Encrypt(const Buffer &cipherKeyId, const Buffer &input, const Buffer &iv) -{ - if (m_cryptoSession) - return CharVecBuffer(m_cryptoSession->encrypt(CharVecBuffer(cipherKeyId), CharVecBuffer(input), CharVecBuffer(iv))); - - return Buffer(); -} - -Buffer CMediaDrmCryptoSession::Sign(const Buffer &macKeyId, const Buffer &message) -{ - if (m_cryptoSession) - return CharVecBuffer(m_cryptoSession->sign(CharVecBuffer(macKeyId), CharVecBuffer(message))); - - return Buffer(); -} - -bool CMediaDrmCryptoSession::Verify(const Buffer &macKeyId, const Buffer &message, const Buffer &signature) -{ - if (m_cryptoSession) - return m_cryptoSession->verify(CharVecBuffer(macKeyId), CharVecBuffer(message), CharVecBuffer(signature)); - - return false; -} - -//Private stuff -bool CMediaDrmCryptoSession::OpenSession() -{ - bool provisioned = false; -TRYAGAIN: - m_sessionId = new CharVecBuffer(m_mediaDrm->openSession()); - if (xbmc_jnienv()->ExceptionCheck()) - { - xbmc_jnienv()->ExceptionClear(); - if (provisioned || !ProvisionRequest()) - { - delete m_sessionId, m_sessionId = nullptr; - return false; - } - else - { - provisioned = true; - goto TRYAGAIN; - } - } - - m_cryptoSession = new CJNIMediaDrmCryptoSession(m_mediaDrm->getCryptoSession(*m_sessionId, m_cipherAlgo, m_macAlgo)); - - if (xbmc_jnienv()->ExceptionCheck()) - { - CLog::Log(LOGERROR, "MediaDrm: getCryptoSession failed"); - xbmc_jnienv()->ExceptionClear(); - return false; - } - return true; -} - -void CMediaDrmCryptoSession::CloseSession() -{ - if (m_sessionId) - { - m_mediaDrm->removeKeys(*m_sessionId); - m_mediaDrm->closeSession(*m_sessionId); - - if (m_cryptoSession) - delete(m_cryptoSession), m_cryptoSession = nullptr; - - delete m_sessionId, m_sessionId = nullptr; - m_hasKeys = false; - m_keySetId.clear(); - } -} - -bool CMediaDrmCryptoSession::ProvisionRequest() -{ - CLog::Log(LOGINFO, "MediaDrm: starting provisioning"); - - CJNIMediaDrmProvisionRequest request = m_mediaDrm->getProvisionRequest(); - if (xbmc_jnienv()->ExceptionCheck()) - { - CLog::Log(LOGERROR, "MediaDrm: getProvisionRequest failed"); - xbmc_jnienv()->ExceptionClear(); - return false; - } - - std::vector provData = request.getData(); - std::string url = request.getDefaultUrl(); - - CLog::Log(LOGDEBUG, "MediaDrm: Provisioning: size: %lu, url: %s", provData.size(), url.c_str()); - - std::string tmp_str("{\"signedRequest\":\""); - tmp_str += std::string(provData.data(), provData.size()); - tmp_str += "\"}"; - - std::string encoded; - Base64::Encode(tmp_str.data(), tmp_str.size(), encoded); - - XFILE::CFile file; - if (!file.CURLCreate(url)) - { - CLog::Log(LOGERROR, "MediaDrm: CURLCreate failed!"); - return false; - } - - file.CURLAddOption(XFILE::CURL_OPTION_PROTOCOL, "Content-Type", "application/json"); - file.CURLAddOption(XFILE::CURL_OPTION_PROTOCOL, "seekable", "0"); - file.CURLAddOption(XFILE::CURL_OPTION_PROTOCOL, "postdata", encoded.c_str()); - - if (!file.CURLOpen(0)) - { - CLog::Log(LOGERROR, "MediaDrm: Provisioning server returned failure"); - return false; - } - provData.clear(); - char buf[8192]; - size_t nbRead; - - // read the file - while ((nbRead = file.Read(buf, 8192)) > 0) - provData.insert(provData.end(), buf, buf + nbRead); - - m_mediaDrm->provideProvisionResponse(provData); - if (xbmc_jnienv()->ExceptionCheck()) - { - CLog::Log(LOGERROR, "MediaDrm: provideProvisionResponse failed"); - xbmc_jnienv()->ExceptionClear(); - return false; - } - return true; -} diff --git a/xbmc/platform/android/drm/MediaDrmCryptoSession.h b/xbmc/platform/android/drm/MediaDrmCryptoSession.h deleted file mode 100644 index 4fd0d4cc35..0000000000 --- a/xbmc/platform/android/drm/MediaDrmCryptoSession.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 "drm/CryptoSession.h" - -class CJNIMediaDrm; -class CJNIMediaDrmCryptoSession; - -namespace DRM -{ - class CharVecBuffer : public XbmcCommons::Buffer - { - public: - inline CharVecBuffer(const XbmcCommons::Buffer &buf) : XbmcCommons::Buffer(buf) {}; - - inline CharVecBuffer(const std::vector &vec) - : XbmcCommons::Buffer(vec.size()) - { - memcpy(data(), vec.data(), vec.size()); - } - - inline operator std::vector() const - { - return std::vector(data(), data() + capacity()); - } - }; - - class CharVecBuffer; - - class CMediaDrmCryptoSession : public CCryptoSession - { - public: - static void Register(); - CMediaDrmCryptoSession(const std::string &UUID, const std::string &cipherAlgo, const std::string &macAlgo); - virtual ~CMediaDrmCryptoSession(); - - // Interface methods - XbmcCommons::Buffer GetKeyRequest(const XbmcCommons::Buffer &init, const std::string &mimeType, bool offlineKey, const std::map ¶meters) override; - std::string GetPropertyString(const std::string &name) override; - std::string ProvideKeyResponse(const XbmcCommons::Buffer &response) override; - void RemoveKeys() override; - void RestoreKeys(const std::string &keySetId) override; - void SetPropertyString(const std::string &name, const std::string &value) override; - - // Crypto methods - XbmcCommons::Buffer Decrypt(const XbmcCommons::Buffer &cipherKeyId, const XbmcCommons::Buffer &input, const XbmcCommons::Buffer &iv) override; - XbmcCommons::Buffer Encrypt(const XbmcCommons::Buffer &cipherKeyId, const XbmcCommons::Buffer &input, const XbmcCommons::Buffer &iv) override; - XbmcCommons::Buffer Sign(const XbmcCommons::Buffer &macKeyId, const XbmcCommons::Buffer &message) override; - bool Verify(const XbmcCommons::Buffer &macKeyId, const XbmcCommons::Buffer &message, const XbmcCommons::Buffer &signature ) override; - - private: - static CCryptoSession* Create(const std::string &UUID, const std::string &cipherAlgo, const std::string &hmacAlgo); - bool OpenSession(); - void CloseSession(); - bool ProvisionRequest(); - - CJNIMediaDrm *m_mediaDrm; - CJNIMediaDrmCryptoSession *m_cryptoSession; - - std::string m_cipherAlgo; - std::string m_macAlgo; - std::string m_keySetId; - - bool m_hasKeys; - - CharVecBuffer *m_sessionId; - }; - -} //namespace diff --git a/xbmc/platform/android/media/decoderfilter/CMakeLists.txt b/xbmc/platform/android/media/decoderfilter/CMakeLists.txt new file mode 100644 index 0000000000..0763e456b5 --- /dev/null +++ b/xbmc/platform/android/media/decoderfilter/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SOURCES MediaCodecDecoderFilterManager.cpp) + +set(HEADERS MediaCodecDecoderFilterManager.h) + +core_add_library(mediacodecdecoderfilter) diff --git a/xbmc/platform/android/media/decoderfilter/MediaCodecDecoderFilterManager.cpp b/xbmc/platform/android/media/decoderfilter/MediaCodecDecoderFilterManager.cpp new file mode 100644 index 0000000000..376f88043a --- /dev/null +++ b/xbmc/platform/android/media/decoderfilter/MediaCodecDecoderFilterManager.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013-2019 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. + */ + + + /** + * \file media\hwdecoder\DecoderFilterManager.cpp + * \brief Implements CDecoderFilterManager class. + * + */ + +#include "MediaCodecDecoderFilterManager.h" +#include +#include "utils/log.h" + + +CMediaCodecDecoderFilterManager::CMediaCodecDecoderFilterManager() +{ + static const char *blacklisted_decoders[] = { + // No software decoders + "OMX.google", + // For Rockchip non-standard components + "AVCDecoder", + "AVCDecoder_FLASH", + "FLVDecoder", + "M2VDecoder", + "M4vH263Decoder", + "RVDecoder", + "VC1Decoder", + "VPXDecoder", + // End of Rockchip + NULL + }; + + unsigned int num_codecs = CJNIMediaCodecList::getCodecCount(); + for (int i = 0; i < num_codecs; i++) + { + CJNIMediaCodecInfo codec_info = CJNIMediaCodecList::getCodecInfoAt(i); + if (codec_info.isEncoder()) + continue; + + std::string codecname = codec_info.getName(); + uint32_t flags = CDecoderFilter::FLAG_GENERAL_ALLOWED | CDecoderFilter::FLAG_DVD_ALLOWED; + for (const char **ptr = blacklisted_decoders; *ptr && flags; ptr++) + { + if (!strnicmp(*ptr, codecname.c_str(), strlen(*ptr))) + flags = 0; + } + add(CDecoderFilter(codecname, flags, 0)); + CLog::Log(LOGNOTICE, "Mediacodec decoder: %s", codecname.c_str()); + } + Save(); +} + diff --git a/xbmc/platform/android/media/decoderfilter/MediaCodecDecoderFilterManager.h b/xbmc/platform/android/media/decoderfilter/MediaCodecDecoderFilterManager.h new file mode 100644 index 0000000000..8114651bfe --- /dev/null +++ b/xbmc/platform/android/media/decoderfilter/MediaCodecDecoderFilterManager.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2013-2019 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 "media/decoderfilter/DecoderFilterManager.h" + + +class CMediaCodecDecoderFilterManager : public CDecoderFilterManager +{ +public: + CMediaCodecDecoderFilterManager(); +}; diff --git a/xbmc/platform/android/media/drm/CMakeLists.txt b/xbmc/platform/android/media/drm/CMakeLists.txt new file mode 100644 index 0000000000..ed49ca12c3 --- /dev/null +++ b/xbmc/platform/android/media/drm/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SOURCES MediaDrmCryptoSession.cpp) + +set(HEADERS MediaDrmCryptoSession.h) + +core_add_library(anroid_drm) diff --git a/xbmc/platform/android/media/drm/MediaDrmCryptoSession.cpp b/xbmc/platform/android/media/drm/MediaDrmCryptoSession.cpp new file mode 100644 index 0000000000..43d4711c74 --- /dev/null +++ b/xbmc/platform/android/media/drm/MediaDrmCryptoSession.cpp @@ -0,0 +1,310 @@ +/* + * 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 "MediaDrmCryptoSession.h" + +#include +#include +#include + +#include "filesystem/File.h" +#include "utils/StringUtils.h" +#include "utils/log.h" +#include "utils/Base64.h" + +using namespace DRM; +using namespace XbmcCommons; + + +class CharVecBuffer : public Buffer +{ +public: + inline CharVecBuffer(const Buffer& buf) : Buffer(buf) {}; + + inline CharVecBuffer(const std::vector& vec) + : Buffer(vec.size()) + { + memcpy(data(), vec.data(), vec.size()); + } + + inline operator std::vector() const + { + return std::vector(data(), data() + capacity()); + } +}; + + +void CMediaDrmCryptoSession::Register() +{ + CCryptoSession::RegisterInterface(CMediaDrmCryptoSession::Create); +} + +CMediaDrmCryptoSession::CMediaDrmCryptoSession(const std::string& UUID, const std::string& cipherAlgo, const std::string& macAlgo) + : m_mediaDrm(nullptr) + , m_cryptoSession(nullptr) + , m_cipherAlgo(cipherAlgo) + , m_macAlgo(macAlgo) + , m_hasKeys(false) + , m_sessionId(nullptr) +{ + if (!StringUtils::EqualsNoCase(UUID, "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed")) + throw std::runtime_error("mediaDrm: Invalid UUID size"); + + int64_t mostSigBits(0), leastSigBits(0); + const uint8_t uuidPtr[16] = {0xed,0xef,0x8b,0xa9,0x79,0xd6,0x4a,0xce,0xa3,0xc8,0x27,0xdc,0xd5,0x1d,0x21,0xed}; + for (unsigned int i(0); i < 8; ++i) + mostSigBits = (mostSigBits << 8) | uuidPtr[i]; + for (unsigned int i(8); i < 16; ++i) + leastSigBits = (leastSigBits << 8) | uuidPtr[i]; + + CJNIUUID uuid(mostSigBits, leastSigBits); + + m_mediaDrm = new CJNIMediaDrm(uuid); + + if (xbmc_jnienv()->ExceptionCheck()) + { + xbmc_jnienv()->ExceptionClear(); + CLog::Log(LOGERROR, "MediaDrm: Failure creating instance"); + throw std::runtime_error("Failure creating MediaDrm"); + } + + if (!OpenSession()) + { + CLog::Log(LOGERROR, "MediaDrm: Unable to create a session"); + throw std::runtime_error("Unable to create a session"); + } +} + +CMediaDrmCryptoSession::~CMediaDrmCryptoSession() +{ + if (!m_mediaDrm) + return; + + CloseSession(); + + m_mediaDrm->release(); + delete m_mediaDrm, m_mediaDrm = nullptr; +} + +CCryptoSession* CMediaDrmCryptoSession::Create(const std::string& UUID, const std::string& cipherAlgo, const std::string& macAlgo) +{ + CMediaDrmCryptoSession *res = nullptr;; + try + { + res = new CMediaDrmCryptoSession(UUID, cipherAlgo, macAlgo); + } + catch (std::runtime_error& e) + { + delete res, res = nullptr; + } + return res; +} + +/*****************/ + +// Interface methods +Buffer CMediaDrmCryptoSession::GetKeyRequest(const Buffer& init, + const std::string& mimeType, + bool offlineKey, + const std::map& parameters) +{ + if (m_mediaDrm && m_sessionId) + { + CJNIMediaDrmKeyRequest req = m_mediaDrm->getKeyRequest(*m_sessionId, CharVecBuffer(init), mimeType, + offlineKey ? CJNIMediaDrm::KEY_TYPE_OFFLINE : CJNIMediaDrm::KEY_TYPE_STREAMING, parameters); + return CharVecBuffer(req.getData()); + } + + return Buffer(); +} + + +std::string CMediaDrmCryptoSession::GetPropertyString(const std::string& name) +{ + if (m_mediaDrm) + return m_mediaDrm->getPropertyString(name); + + return ""; +} + + +std::string CMediaDrmCryptoSession::ProvideKeyResponse(const Buffer& response) +{ + if (m_mediaDrm) + { + m_hasKeys = true; + std::vector res = m_mediaDrm->provideKeyResponse(*m_sessionId, CharVecBuffer(response)); + return std::string(res.data(), res.size()); + } + + return ""; +} + +void CMediaDrmCryptoSession::RemoveKeys() +{ + if (m_mediaDrm && m_sessionId && m_hasKeys) + { + CloseSession(); + OpenSession(); + } +} + +void CMediaDrmCryptoSession::RestoreKeys(const std::string& keySetId) +{ + if (m_mediaDrm && keySetId != m_keySetId) + { + m_mediaDrm->restoreKeys(*m_sessionId, std::vector(keySetId.begin(), keySetId.end())); + m_hasKeys = true; + m_keySetId = keySetId; + } +} + +void CMediaDrmCryptoSession::SetPropertyString(const std::string& name, const std::string& value) +{ + if (m_mediaDrm) + m_mediaDrm->setPropertyString(name, value); +} + +// Crypto methods +Buffer CMediaDrmCryptoSession::Decrypt(const Buffer& cipherKeyId, const Buffer& input, const Buffer& iv) +{ + if (m_cryptoSession) + return CharVecBuffer(m_cryptoSession->decrypt(CharVecBuffer(cipherKeyId), CharVecBuffer(input), CharVecBuffer(iv))); + + return Buffer(); +} + +Buffer CMediaDrmCryptoSession::Encrypt(const Buffer& cipherKeyId, const Buffer& input, const Buffer& iv) +{ + if (m_cryptoSession) + return CharVecBuffer(m_cryptoSession->encrypt(CharVecBuffer(cipherKeyId), CharVecBuffer(input), CharVecBuffer(iv))); + + return Buffer(); +} + +Buffer CMediaDrmCryptoSession::Sign(const Buffer& macKeyId, const Buffer& message) +{ + if (m_cryptoSession) + return CharVecBuffer(m_cryptoSession->sign(CharVecBuffer(macKeyId), CharVecBuffer(message))); + + return Buffer(); +} + +bool CMediaDrmCryptoSession::Verify(const Buffer& macKeyId, const Buffer& message, const Buffer& signature) +{ + if (m_cryptoSession) + return m_cryptoSession->verify(CharVecBuffer(macKeyId), CharVecBuffer(message), CharVecBuffer(signature)); + + return false; +} + +//Private stuff +bool CMediaDrmCryptoSession::OpenSession() +{ + bool provisioned = false; +TRYAGAIN: + m_sessionId = new CharVecBuffer(m_mediaDrm->openSession()); + if (xbmc_jnienv()->ExceptionCheck()) + { + xbmc_jnienv()->ExceptionClear(); + if (provisioned || !ProvisionRequest()) + { + delete m_sessionId, m_sessionId = nullptr; + return false; + } + else + { + provisioned = true; + goto TRYAGAIN; + } + } + + m_cryptoSession = new CJNIMediaDrmCryptoSession(m_mediaDrm->getCryptoSession(*m_sessionId, m_cipherAlgo, m_macAlgo)); + + if (xbmc_jnienv()->ExceptionCheck()) + { + CLog::Log(LOGERROR, "MediaDrm: getCryptoSession failed"); + xbmc_jnienv()->ExceptionClear(); + return false; + } + return true; +} + +void CMediaDrmCryptoSession::CloseSession() +{ + if (m_sessionId) + { + m_mediaDrm->removeKeys(*m_sessionId); + m_mediaDrm->closeSession(*m_sessionId); + + if (m_cryptoSession) + delete(m_cryptoSession), m_cryptoSession = nullptr; + + delete m_sessionId, m_sessionId = nullptr; + m_hasKeys = false; + m_keySetId.clear(); + } +} + +bool CMediaDrmCryptoSession::ProvisionRequest() +{ + CLog::Log(LOGINFO, "MediaDrm: starting provisioning"); + + CJNIMediaDrmProvisionRequest request = m_mediaDrm->getProvisionRequest(); + if (xbmc_jnienv()->ExceptionCheck()) + { + CLog::Log(LOGERROR, "MediaDrm: getProvisionRequest failed"); + xbmc_jnienv()->ExceptionClear(); + return false; + } + + std::vector provData = request.getData(); + std::string url = request.getDefaultUrl(); + + CLog::Log(LOGDEBUG, "MediaDrm: Provisioning: size: %lu, url: %s", provData.size(), url.c_str()); + + std::string tmp_str("{\"signedRequest\":\""); + tmp_str += std::string(provData.data(), provData.size()); + tmp_str += "\"}"; + + std::string encoded; + Base64::Encode(tmp_str.data(), tmp_str.size(), encoded); + + XFILE::CFile file; + if (!file.CURLCreate(url)) + { + CLog::Log(LOGERROR, "MediaDrm: CURLCreate failed!"); + return false; + } + + file.CURLAddOption(XFILE::CURL_OPTION_PROTOCOL, "Content-Type", "application/json"); + file.CURLAddOption(XFILE::CURL_OPTION_PROTOCOL, "seekable", "0"); + file.CURLAddOption(XFILE::CURL_OPTION_PROTOCOL, "postdata", encoded.c_str()); + + if (!file.CURLOpen(0)) + { + CLog::Log(LOGERROR, "MediaDrm: Provisioning server returned failure"); + return false; + } + provData.clear(); + char buf[8192]; + size_t nbRead; + + // read the file + while ((nbRead = file.Read(buf, 8192)) > 0) + provData.insert(provData.end(), buf, buf + nbRead); + + m_mediaDrm->provideProvisionResponse(provData); + if (xbmc_jnienv()->ExceptionCheck()) + { + CLog::Log(LOGERROR, "MediaDrm: provideProvisionResponse failed"); + xbmc_jnienv()->ExceptionClear(); + return false; + } + return true; +} diff --git a/xbmc/platform/android/media/drm/MediaDrmCryptoSession.h b/xbmc/platform/android/media/drm/MediaDrmCryptoSession.h new file mode 100644 index 0000000000..e321bc18bd --- /dev/null +++ b/xbmc/platform/android/media/drm/MediaDrmCryptoSession.h @@ -0,0 +1,76 @@ +/* + * 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 "media/drm/CryptoSession.h" + +class CJNIMediaDrm; +class CJNIMediaDrmCryptoSession; + +namespace DRM +{ + class CharVecBuffer : public XbmcCommons::Buffer + { + public: + inline CharVecBuffer(const XbmcCommons::Buffer& buf) : XbmcCommons::Buffer(buf) {}; + + inline CharVecBuffer(const std::vector& vec) + : XbmcCommons::Buffer(vec.size()) + { + memcpy(data(), vec.data(), vec.size()); + } + + inline operator std::vector() const + { + return std::vector(data(), data() + capacity()); + } + }; + + class CharVecBuffer; + + class CMediaDrmCryptoSession : public CCryptoSession + { + public: + static void Register(); + CMediaDrmCryptoSession(const std::string& UUID, const std::string& cipherAlgo, const std::string& macAlgo); + virtual ~CMediaDrmCryptoSession(); + + // Interface methods + XbmcCommons::Buffer GetKeyRequest(const XbmcCommons::Buffer& init, const std::string& mimeType, bool offlineKey, const std::map& parameters) override; + std::string GetPropertyString(const std::string& name) override; + std::string ProvideKeyResponse(const XbmcCommons::Buffer& response) override; + void RemoveKeys() override; + void RestoreKeys(const std::string& keySetId) override; + void SetPropertyString(const std::string& name, const std::string& value) override; + + // Crypto methods + XbmcCommons::Buffer Decrypt(const XbmcCommons::Buffer& cipherKeyId, const XbmcCommons::Buffer& input, const XbmcCommons::Buffer& iv) override; + XbmcCommons::Buffer Encrypt(const XbmcCommons::Buffer& cipherKeyId, const XbmcCommons::Buffer& input, const XbmcCommons::Buffer& iv) override; + XbmcCommons::Buffer Sign(const XbmcCommons::Buffer& macKeyId, const XbmcCommons::Buffer& message) override; + bool Verify(const XbmcCommons::Buffer& macKeyId, const XbmcCommons::Buffer& message, const XbmcCommons::Buffer& signature ) override; + + private: + static CCryptoSession* Create(const std::string& UUID, const std::string& cipherAlgo, const std::string& hmacAlgo); + bool OpenSession(); + void CloseSession(); + bool ProvisionRequest(); + + CJNIMediaDrm* m_mediaDrm; + CJNIMediaDrmCryptoSession* m_cryptoSession; + + std::string m_cipherAlgo; + std::string m_macAlgo; + std::string m_keySetId; + + bool m_hasKeys; + + CharVecBuffer* m_sessionId; + }; + +} //namespace diff --git a/xbmc/windowing/android/WinSystemAndroid.cpp b/xbmc/windowing/android/WinSystemAndroid.cpp index 9c7807321b..df4bf99161 100644 --- a/xbmc/windowing/android/WinSystemAndroid.cpp +++ b/xbmc/windowing/android/WinSystemAndroid.cpp @@ -11,19 +11,10 @@ #include #include -#include "WinEventsAndroid.h" -#include "OSScreenSaverAndroid.h" -#include "ServiceBroker.h" -#include "windowing/GraphicContext.h" -#include "windowing/Resolution.h" -#include "settings/DisplaySettings.h" -#include "settings/Settings.h" -#include "settings/SettingsComponent.h" -#include "guilib/DispResource.h" -#include "utils/log.h" -#include "threads/SingleLock.h" -#include "platform/android/activity/XBMCApp.h" +#include +#include +#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/Video/DVDVideoCodecAndroidMediaCodec.h" @@ -31,13 +22,21 @@ #include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererMediaCodec.h" #include "cores/VideoPlayer/Process/android/ProcessInfoAndroid.h" #include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererMediaCodecSurface.h" +#include "guilib/DispResource.h" +#include "OSScreenSaverAndroid.h" #include "platform/android/powermanagement/AndroidPowerSyscall.h" -#include "addons/interfaces/platform/android/System.h" -#include "platform/android/drm/MediaDrmCryptoSession.h" -#include - -#include -#include +#include "platform/android/media/drm/MediaDrmCryptoSession.h" +#include "platform/android/media/decoderfilter/MediaCodecDecoderFilterManager.h" +#include "platform/android/activity/XBMCApp.h" +#include "ServiceBroker.h" +#include "settings/DisplaySettings.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "threads/SingleLock.h" +#include "utils/log.h" +#include "windowing/GraphicContext.h" +#include "windowing/Resolution.h" +#include "WinEventsAndroid.h" using namespace KODI; @@ -72,6 +71,9 @@ bool CWinSystemAndroid::InitWindowSystem() m_android = new CAndroidUtils(); + m_decoderFilterManager = new(CMediaCodecDecoderFilterManager); + CServiceBroker::RegisterDecoderFilterManager(m_decoderFilterManager); + CDVDVideoCodecAndroidMediaCodec::Register(); CDVDAudioCodecAndroidMediaCodec::Register(); @@ -89,9 +91,14 @@ bool CWinSystemAndroid::InitWindowSystem() bool CWinSystemAndroid::DestroyWindowSystem() { CLog::Log(LOGNOTICE, "CWinSystemAndroid::%s", __FUNCTION__); + delete m_android; m_android = nullptr; + CServiceBroker::RegisterDecoderFilterManager(nullptr); + delete m_decoderFilterManager; + m_decoderFilterManager = nullptr; + return true; } @@ -182,17 +189,6 @@ void CWinSystemAndroid::UpdateResolutions() } res_index = (RESOLUTION)((int)res_index + 1); } - - unsigned int num_codecs = CJNIMediaCodecList::getCodecCount(); - for (int i = 0; i < num_codecs; i++) - { - CJNIMediaCodecInfo codec_info = CJNIMediaCodecList::getCodecInfoAt(i); - if (codec_info.isEncoder()) - continue; - - std::string codecname = codec_info.getName(); - CLog::Log(LOGNOTICE, "Mediacodec: %s", codecname.c_str()); - } } void CWinSystemAndroid::OnTimeout() diff --git a/xbmc/windowing/android/WinSystemAndroid.h b/xbmc/windowing/android/WinSystemAndroid.h index 04a8f4a099..e7f11dbff2 100644 --- a/xbmc/windowing/android/WinSystemAndroid.h +++ b/xbmc/windowing/android/WinSystemAndroid.h @@ -16,6 +16,7 @@ #include "threads/Timer.h" #include "EGL/egl.h" +class CDecoderFilterManager; class IDispResource; class CWinSystemAndroid : public CWinSystemBase, public ITimerCallback @@ -74,4 +75,5 @@ protected: CCriticalSection m_resourceSection; std::vector m_resources; + CDecoderFilterManager *m_decoderFilterManager; }; -- cgit v1.2.3