Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[widevine] Reintroduce CDM aarch64 support #1379

Merged
merged 3 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ if(WIN32)
list(APPEND DEPLIBS ${dlfcn-win32_LIBRARIES})
include_directories(${dlfcn-win32_INCLUDE_DIRS})
else()
list(APPEND DEPLIBS ${CMAKE_DL_LIBS})
# Required on some old linux platforms to use macro like PRIu64
add_definitions(-D__STDC_FORMAT_MACROS)
endif()
Expand Down Expand Up @@ -68,6 +69,16 @@ endforeach()
get_property(DEPS_NAMES_LIST GLOBAL PROPERTY GlobalDepsNamesList)
list(APPEND DEPLIBS ${DEPS_NAMES_LIST})

# Add additional shared dependencies
get_property(DEPS_FOLDERS_LIST GLOBAL PROPERTY GlobalSharedDepsFoldersList)
foreach(DEP_FOLDER ${DEPS_FOLDERS_LIST})
add_subdirectory(${DEP_FOLDER})
endforeach()
get_property(DEPS_NAMES_LIST GLOBAL PROPERTY GlobalSharedDepsNamesList)
foreach(DEP_NAME ${DEPS_NAMES_LIST})
list(APPEND ADP_ADDITIONAL_BINARY $<TARGET_FILE:${DEP_NAME}>)
endforeach()

build_addon(inputstream.adaptive ADP DEPLIBS)

if(NOT CMAKE_CROSSCOMPILING AND BUILD_TESTING)
Expand Down
6 changes: 6 additions & 0 deletions Helpers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,9 @@ function(add_dependency project_name folder)
set_property(GLOBAL APPEND PROPERTY GlobalDepsNamesList "${project_name}")
set_property(GLOBAL APPEND PROPERTY GlobalDepsFoldersList "${folder}")
endfunction(add_dependency)

# Function to add an additional shared dependency to global properties GlobalSharedDepsNamesList/GlobalSharedDepsFoldersList
function(add_shared_dependency project_name folder)
set_property(GLOBAL APPEND PROPERTY GlobalSharedDepsNamesList "${project_name}")
set_property(GLOBAL APPEND PROPERTY GlobalSharedDepsFoldersList "${folder}")
endfunction(add_shared_dependency)
8 changes: 8 additions & 0 deletions lib/cdm_aarch64/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.10)
project(cdm_aarch64_loader)

add_library(cdm_aarch64_loader SHARED
cdm_loader.cpp
)

set_target_properties(cdm_aarch64_loader PROPERTIES POSITION_INDEPENDENT_CODE True)
31 changes: 31 additions & 0 deletions lib/cdm_aarch64/cdm_loader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2023 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 <cstdint>

extern "C"
{
// Linux arm64 version of libwidevinecdm.so depends on two dynamic symbols.
// See https://github.com/xbmc/inputstream.adaptive/issues/1128
#if defined(__linux__) && (defined(__aarch64__) || defined(__arm64__))

__attribute__((target("no-outline-atomics"))) int32_t __aarch64_ldadd4_acq_rel(int32_t value,
int32_t* ptr)
{
return __atomic_fetch_add(ptr, value, __ATOMIC_ACQ_REL);
}

__attribute__((target("no-outline-atomics"))) int32_t __aarch64_swp4_acq_rel(int32_t value,
int32_t* ptr)
{
return __atomic_exchange_n(ptr, value, __ATOMIC_ACQ_REL);
}
#endif
}
7 changes: 7 additions & 0 deletions src/decrypters/widevine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,10 @@ add_dir_sources(SOURCES HEADERS)

# Native CDM library
add_dependency(cdm_library lib/cdm)

# Patch to load CDM library on aarch64 platforms
if(${CMAKE_SYSTEM_NAME} STREQUAL Linux)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "^arm64")
add_shared_dependency(cdm_aarch64_loader lib/cdm_aarch64)
endif()
endif()
50 changes: 50 additions & 0 deletions src/decrypters/widevine/WVDecrypter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,16 @@
#include "WVCdmAdapter.h"
#include "WVCencSingleSampleDecrypter.h"
#include "utils/Base64Utils.h"
#include "utils/FileUtils.h"
#include "utils/StringUtils.h"
#include "utils/log.h"

#include <kodi/Filesystem.h>

#if defined(__linux__) && (defined(__aarch64__) || defined(__arm64__))
#include <dlfcn.h>
#endif

using namespace DRM;
using namespace UTILS;
using namespace kodi::tools;
Expand All @@ -24,6 +30,50 @@ CWVDecrypter::~CWVDecrypter()
{
delete m_WVCdmAdapter;
m_WVCdmAdapter = nullptr;
#if defined(__linux__) && (defined(__aarch64__) || defined(__arm64__))
if (m_hdlLibLoader)
dlclose(m_hdlLibLoader);
#endif
}

bool CWVDecrypter::Initialize()
{
#if defined(__linux__) && (defined(__aarch64__) || defined(__arm64__))
// On linux arm64, libwidevinecdm.so depends on two dynamic symbols:
// __aarch64_ldadd4_acq_rel
// __aarch64_swp4_acq_rel
// These are defined from a separate library cdm_aarch64_loader,
// but to make them available in the main binary's PLT, we need RTLD_GLOBAL.
// Kodi kodi::tools::CDllHelper LoadDll() cannot be used because use RTLD_LOCAL,
// and we need the RTLD_GLOBAL flag.
std::string binaryPath;
std::vector<kodi::vfs::CDirEntry> items;
if (kodi::vfs::GetDirectory(FILESYS::GetAddonPath(), "", items))
{
for (auto item : items)
{
if (!STRING::Contains(item.Label(), "cdm_aarch64_loader"))
continue;

binaryPath = item.Path();
break;
}
}
if (binaryPath.empty())
{
LOG::Log(LOGERROR, "Cannot find the cdm_aarch64_loader file");
return false;
}

m_hdlLibLoader = dlopen(binaryPath.c_str(), RTLD_GLOBAL | RTLD_LAZY);
if (!m_hdlLibLoader)
{
LOG::LogF(LOGERROR, "Failed to load CDM aarch64 loader from path \"%s\", error: %s",
binaryPath.c_str(), dlerror());
return false;
}
#endif
return true;
}

const char* CWVDecrypter::SelectKeySytem(const char* keySystem)
Expand Down
3 changes: 3 additions & 0 deletions src/decrypters/widevine/WVDecrypter.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class ATTR_DLL_LOCAL CWVDecrypter : public IDecrypter
CWVDecrypter() : m_WVCdmAdapter(nullptr), m_decodingDecrypter(nullptr){};
virtual ~CWVDecrypter() override;

virtual bool Initialize() override;

virtual const char* SelectKeySytem(const char* keySystem) override;
virtual bool OpenDRMSystem(const char* licenseURL,
const AP4_DataBuffer& serverCertificate,
Expand Down Expand Up @@ -69,4 +71,5 @@ class ATTR_DLL_LOCAL CWVDecrypter : public IDecrypter
std::string m_strProfilePath;
std::string m_strLibraryPath;
bool m_isDebugSaveLicense;
void* m_hdlLibLoader{nullptr}; // Aarch64 loader library handle
};
87 changes: 87 additions & 0 deletions src/test/KodiStubs.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <cstdint>
#include <string>
#include <vector>
#include <map>

#ifdef _WIN32 // windows
#if !defined(_SSIZE_T_DEFINED) && !defined(HAVE_SSIZE_T)
Expand Down Expand Up @@ -86,6 +87,11 @@ namespace addon
{
class InputstreamInfo;

inline std::string GetAddonInfo(const std::string& id)
{
return "";
}

inline std::string GetLocalizedString(uint32_t labelId, const std::string& defaultStr = "")
{
return defaultStr;
Expand Down Expand Up @@ -114,8 +120,72 @@ inline std::string GetUserPath(const std::string& append = "")

} // namespace addon

struct VFSProperty
{
char* name;
char* val;
};

struct VFSDirEntry
{
char* label; //!< item label
char* title; //!< item title
char* path; //!< item path
unsigned int num_props; //!< Number of properties attached to item
struct VFSProperty* properties; //!< Properties
time_t date_time; //!< file creation date & time
bool folder; //!< Item is a folder
uint64_t size; //!< Size of file represented by item
};

namespace vfs
{
class ATTR_DLL_LOCAL CDirEntry
{
public:
CDirEntry(const std::string& label = "",
const std::string& path = "",
bool folder = false,
int64_t size = -1,
time_t dateTime = 0)
: m_label(label), m_path(path), m_folder(folder), m_size(size), m_dateTime(dateTime)
{
}

explicit CDirEntry(const VFSDirEntry& dirEntry)
: m_label(dirEntry.label ? dirEntry.label : ""),
m_path(dirEntry.path ? dirEntry.path : ""),
m_folder(dirEntry.folder),
m_size(dirEntry.size),
m_dateTime(dirEntry.date_time)
{
}

const std::string& Label(void) const { return m_label; }
const std::string& Title(void) const { return m_title; }
const std::string& Path(void) const { return m_path; }
bool IsFolder(void) const { return m_folder; }
int64_t Size(void) const { return m_size; }
time_t DateTime() { return m_dateTime; }
void SetLabel(const std::string& label) { m_label = label; }
void SetTitle(const std::string& title) { m_title = title; }
void SetPath(const std::string& path) { m_path = path; }
void SetFolder(bool folder) { m_folder = folder; }
void SetSize(int64_t size) { m_size = size; }
void SetDateTime(time_t dateTime) { m_dateTime = dateTime; }
void AddProperty(const std::string& id, const std::string& value) { m_properties[id] = value; }
void ClearProperties() { m_properties.clear(); }
const std::map<std::string, std::string>& GetProperties() const { return m_properties; }
private:
std::string m_label;
std::string m_title;
std::string m_path;
std::map<std::string, std::string> m_properties;
bool m_folder;
int64_t m_size;
time_t m_dateTime;
};

class CFile
{
public:
Expand Down Expand Up @@ -183,11 +253,28 @@ inline bool FileExists(const std::string& filename, bool usecache = false)
return false;
}

inline bool DirectoryExists(const std::string& path)
{
return false;
}

inline bool RemoveDirectory(const std::string& path, bool recursive = false)
{
return true;
}

inline std::string TranslateSpecialProtocol(const std::string& source)
{
return "";
}

inline bool GetDirectory(const std::string& path,
const std::string& mask,
std::vector<kodi::vfs::CDirEntry>& items)
{
return false;
}

} // namespace vfs

namespace gui
Expand Down
23 changes: 23 additions & 0 deletions src/utils/FileUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,29 @@ std::string UTILS::FILESYS::GetAddonUserPath()
return kodi::addon::GetUserPath();
}

std::string UTILS::FILESYS::GetAddonPath()
{
std::array<std::string, 3> searchPaths = {
kodi::vfs::TranslateSpecialProtocol("special://xbmcbinaddons/inputstream.adaptive/"),
kodi::vfs::TranslateSpecialProtocol("special://xbmcaltbinaddons/inputstream.adaptive/"),
kodi::addon::GetAddonInfo("path"),
};

for (auto searchPath : searchPaths)
{
std::vector<kodi::vfs::CDirEntry> items;
if (!kodi::vfs::DirectoryExists(searchPath) || !kodi::vfs::GetDirectory(searchPath, "", items))
continue;

for (auto item : items)
{
if (!item.IsFolder() && item.Label().find("inputstream.adaptive") != std::string::npos)
return searchPath;
}
}
return {};
}

bool UTILS::FILESYS::CheckDuplicateFilePath(std::string& filePath, uint32_t filesLimit /* = 0 */)
{
const size_t extensionPos = filePath.rfind('.');
Expand Down
6 changes: 6 additions & 0 deletions src/utils/FileUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ std::string PathCombine(std::string path, std::string filePath);
*/
std::string GetAddonUserPath();

/*!
* \brief Get the data folder of the addon.
* \return The path.
*/
std::string GetAddonPath();

/*!
* \brief Check for duplicates for the file path, and change the filename
* based on the number of duplicate files.
Expand Down
Loading