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

[DRM][Playready] Initial implementation #1315

Draft
wants to merge 3 commits into
base: Omega
Choose a base branch
from
Draft
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
17 changes: 17 additions & 0 deletions lib/mfcdm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.10)
project(mfcdm_library)

add_library(mfcdm_library STATIC
mfcdm/MediaFoundationCdm.h
mfcdm/MediaFoundationCdmConfig.h
mfcdm/MediaFoundationCdmTypes.h
mfcdm/MediaFoundationCdm.cpp
mfcdm/MediaFoundationCdmFactory.cpp
mfcdm/MediaFoundationCdmSession.cpp
mfcdm/MediaFoundationSession.cpp
mfcdm/Log.cpp
)

target_include_directories(mfcdm_library PUBLIC ${PROJECT_SOURCE_DIR})
target_link_libraries(mfcdm_library PRIVATE cdm_library propsys mf mfplat mfplay mfreadwrite mfuuid wmcodecdspuuid)
set_target_properties(mfcdm_library PROPERTIES POSITION_INDEPENDENT_CODE True)
47 changes: 47 additions & 0 deletions lib/mfcdm/mfcdm/Log.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "Log.h"

#include <cstdarg>
#include <cstdio>

typedef struct
{
const char* name;
int cur_level;
void (*msg_callback)(int level, char* msg);
} debug_ctx_t;

static debug_ctx_t debug_ctx = {"MF", MFCDM::MFLOG_NONE, NULL};


static inline void __dbg(debug_ctx_t* ctx, int level, const char* fmt, va_list ap)
{
if (ctx != NULL && level <= ctx->cur_level)
{
char msg[4096];
int len = snprintf(msg, sizeof(msg), "[%s] ", ctx->name);
vsnprintf(msg + len, sizeof(msg) - len, fmt, ap);
if (ctx->msg_callback)
{
ctx->msg_callback(level, msg);
}
}
}

void MFCDM::LogAll()
{
debug_ctx.cur_level = MFLOG_ALL;
}

void MFCDM::Log(LogLevel level, const char* fmt, ...)
{
va_list ap;

va_start(ap, fmt);
__dbg(&debug_ctx, level, fmt, ap);
va_end(ap);
}

void MFCDM::SetMFMsgCallback(void (*msgcb)(int level, char*))
{
debug_ctx.msg_callback = msgcb;
}
17 changes: 17 additions & 0 deletions lib/mfcdm/mfcdm/Log.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace MFCDM
{
enum LogLevel
{
MFLOG_NONE = -1,
MFLOG_ERROR,
MFLOG_WARN,
MFLOG_INFO,
MFLOG_DEBUG,
MFLOG_ALL = 100
};

void LogAll();
void Log(LogLevel level, const char* fmt, ...);
void SetMFMsgCallback(void (*msgcb)(int level, char*));

} // namespace MFCDM
159 changes: 159 additions & 0 deletions lib/mfcdm/mfcdm/MediaFoundationCdm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
* 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.
*/

#include "MediaFoundationCdm.h"

#include "MediaFoundationCdmFactory.h"
#include "MediaFoundationCdmModule.h"
#include "MediaFoundationCdmSession.h"
#include "utils/PMPHostWrapper.h"
#include "Log.h"

#include <functional>

MediaFoundationCdm::MediaFoundationCdm() = default;
MediaFoundationCdm::~MediaFoundationCdm() = default;

bool MediaFoundationCdm::Initialize(const MediaFoundationCdmConfig& cdmConfig,
std::string_view keySystem,
std::string_view basePath)
{
bool ret = true;

m_session.Startup();

ret = m_session.HasMediaFoundation();
if (!ret)
{
Log(MFCDM::MFLOG_ERROR, "MF doesn't exist on current system");
return false;
}

const auto m_factory = std::make_unique<MediaFoundationCdmFactory>(keySystem);

ret = m_factory->Initialize();
if (!ret)
{
Log(MFCDM::MFLOG_ERROR, "MFFactory failed to initialize.");
return false;
}

ret = m_factory->CreateMfCdm(cdmConfig, basePath, m_module);
if (!ret)
{
Log(MFCDM::MFLOG_ERROR, "MFFactory failed to create MF CDM.");
return false;
}

Log(MFCDM::MFLOG_DEBUG, "MF CDM created.");

SetupPMPServer();
return true;
}

/*!
* \brief Setup PMPHostApp
* IMFContentDecryptionModule->SetPMPHostApp
* needs to be set if not under UWP or else GenerateChallenge will fail
* \link https://github.com/microsoft/media-foundation/issues/37#issuecomment-1194534228
*/
void MediaFoundationCdm::SetupPMPServer() const
{
if (!m_module)
return;

const winrt::com_ptr<IMFGetService> spIMFGetService = m_module->As<IMFGetService>();
winrt::com_ptr<IMFPMPHost> pmpHostApp;

if(FAILED(spIMFGetService->GetService(
MF_CONTENTDECRYPTIONMODULE_SERVICE, IID_PPV_ARGS(pmpHostApp.put()))))
{
Log(MFCDM::MFLOG_ERROR, "Failed to get MF CDM service.");
return;
}

winrt::com_ptr<PMPHostWrapper> spIMFPMPHostApp = winrt::make_self<PMPHostWrapper>(pmpHostApp);
m_module->SetPMPHostApp(spIMFPMPHostApp.get());
}

bool MediaFoundationCdm::SetServerCertificate(const uint8_t* serverCertificateData,
uint32_t serverCertificateDataSize) const
{
m_module->SetServerCertificate(serverCertificateData, serverCertificateDataSize);
return true;
}

bool MediaFoundationCdm::CreateSessionAndGenerateRequest(SessionType sessionType,
InitDataType initDataType,
const std::vector<uint8_t>& initData,
SessionClient* client)
{
auto session = std::make_shared<MediaFoundationCdmSession>(client);

if (!session->Initialize(m_module.get(), sessionType))
{
return false;
}

// when session id is identified, callback is ran.
// this meant to be able to access UpdateSession()
// inside MF callback because then session id is known.
int sessionToken = m_nextSessionToken++;
m_pendingSessions.emplace(sessionToken, session);

if (!session->GenerateRequest(initDataType, initData,
std::bind(&MediaFoundationCdm::OnNewSessionId, this, sessionToken, std::placeholders::_1)))
{
return false;
}
return true;
}

void MediaFoundationCdm::LoadSession(SessionType sessionType, const std::string& sessionId)
{

}

bool MediaFoundationCdm::UpdateSession(const std::string& sessionId,
const std::vector<uint8_t>& response)
{
if (!m_module)
return false;

auto* session = GetSession(sessionId);
if (!session)
{
Log(MFCDM::MFLOG_ERROR, "Couldn't find session in created sessions.");
return false;
}

return session->Update(response);
}

void MediaFoundationCdm::OnNewSessionId(int sessionToken, std::string_view sessionId)
{
auto itr = m_pendingSessions.find(sessionToken);
assert(itr != m_pendingSessions.end());

auto session = std::move(itr->second);
assert(session);

m_pendingSessions.erase(itr);

m_sessions.emplace(sessionId, std::move(session));
}

MediaFoundationCdmSession* MediaFoundationCdm::GetSession(const std::string& sessionId) const
{
auto itr = m_sessions.find(sessionId);
if (itr == m_sessions.end())
return nullptr;

return itr->second.get();
}

56 changes: 56 additions & 0 deletions lib/mfcdm/mfcdm/MediaFoundationCdm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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 "MediaFoundationSession.h"
#include "MediaFoundationCdmConfig.h"
#include "MediaFoundationCdmTypes.h"

#include <string>
#include <map>
#include <memory>
#include <vector>

class MediaFoundationCdmSession;
class MediaFoundationCdmModule;

class MediaFoundationCdm {
public:
MediaFoundationCdm();
~MediaFoundationCdm();

bool IsInitialized() const { return m_module != nullptr; }

bool Initialize(const MediaFoundationCdmConfig& cdmConfig,
std::string_view keySystem,
std::string_view basePath);

bool SetServerCertificate(const uint8_t* serverCertificateData,
uint32_t serverCertificateDataSize) const;

bool CreateSessionAndGenerateRequest(SessionType sessionType,
InitDataType initDataType,
const std::vector<uint8_t>& initData,
SessionClient* client);

void LoadSession(SessionType session_type, const std::string& session_id);
bool UpdateSession(const std::string& session_id, const std::vector<uint8_t>& response);

private:
void SetupPMPServer() const;
MediaFoundationCdmSession* GetSession(const std::string& sessionId) const;
void OnNewSessionId(int sessionToken, std::string_view sessionId);

MediaFoundationSession m_session;
std::unique_ptr<MediaFoundationCdmModule> m_module;

int m_nextSessionToken = 0;
std::map<int, std::shared_ptr<MediaFoundationCdmSession>> m_pendingSessions;
std::map<std::string, std::shared_ptr<MediaFoundationCdmSession>> m_sessions;
};
32 changes: 32 additions & 0 deletions lib/mfcdm/mfcdm/MediaFoundationCdmConfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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

/*!
* \brief The runtime configuration for the CDM instance
*/
struct MediaFoundationCdmConfig
{
MediaFoundationCdmConfig(bool distinctive_identifier = false, bool persistent_state = false)
: allow_distinctive_identifier(distinctive_identifier),
allow_persistent_state(persistent_state),
use_hw_secure_codecs(false)
{

}

// Allow access to a distinctive identifier.
bool allow_distinctive_identifier;

// Allow access to persistent state.
bool allow_persistent_state;

// Use hardware-secure codecs.
bool use_hw_secure_codecs;
};
Loading