Skip to content

Commit

Permalink
Merge pull request #725 from CastagnaIT/licensed_manifest
Browse files Browse the repository at this point in the history
Add support to pre-initialize the DRM
  • Loading branch information
glennguy authored Jul 13, 2021
2 parents d9af37a + 806c6c1 commit 474f41b
Show file tree
Hide file tree
Showing 15 changed files with 241 additions and 42 deletions.
2 changes: 1 addition & 1 deletion inputstream.adaptive/addon.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
name="adaptive"
extension=""
tags="true"
listitemprops="license_type|license_key|license_data|license_flags|manifest_type|server_certificate|stream_headers|manifest_update_parameter|original_audio_language|max_bandwidth|play_timeshift_buffer"
listitemprops="license_type|license_key|license_data|license_flags|manifest_type|server_certificate|stream_headers|manifest_update_parameter|original_audio_language|max_bandwidth|play_timeshift_buffer|pre_init_data"
library_@PLATFORM@="@LIBRARY_FILENAME@"/>
<extension point="xbmc.addon.metadata">
<summary lang="en_GB">InputStream client for adaptive streams</summary>
Expand Down
5 changes: 3 additions & 2 deletions src/SSD_dll.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace SSD
{
PROPERTY_HEADER
};
static const uint32_t version = 12;
static const uint32_t version = 13;
#if defined(ANDROID)
virtual void* GetJNIEnv() = 0;
virtual int GetSDKVersion() = 0;
Expand Down Expand Up @@ -182,12 +182,13 @@ namespace SSD
// Return supported URN if type matches to capabilities, otherwise null
virtual const char *SelectKeySytem(const char* keySystem) = 0;
virtual bool OpenDRMSystem(const char *licenseURL, const AP4_DataBuffer &serverCertificate, const uint8_t config) = 0;
virtual AP4_CencSingleSampleDecrypter *CreateSingleSampleDecrypter(AP4_DataBuffer &pssh, const char *optionalKeyParameter, const uint8_t *defaultkeyid) = 0;
virtual AP4_CencSingleSampleDecrypter *CreateSingleSampleDecrypter(AP4_DataBuffer &pssh, const char *optionalKeyParameter, const uint8_t *defaultkeyid, bool skipSessionMessage) = 0;
virtual void DestroySingleSampleDecrypter(AP4_CencSingleSampleDecrypter* decrypter) = 0;

virtual void GetCapabilities(AP4_CencSingleSampleDecrypter* decrypter, const uint8_t *keyid, uint32_t media, SSD_DECRYPTER::SSD_CAPS &caps) = 0;
virtual bool HasLicenseKey(AP4_CencSingleSampleDecrypter* decrypter, const uint8_t *keyid) = 0;
virtual bool HasCdmSession() = 0;
virtual std::string GetChallengeB64Data(AP4_CencSingleSampleDecrypter* decrypter) = 0;

virtual bool OpenVideoDecoder(AP4_CencSingleSampleDecrypter* decrypter, const SSD_VIDEOINITDATA *initData) = 0;
virtual SSD_DECODE_RETVAL DecodeVideo(void* instance, SSD_SAMPLE *sample, SSD_PICTURE *picture) = 0;
Expand Down
3 changes: 2 additions & 1 deletion src/common/AdaptiveTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,8 @@ class ATTRIBUTE_HIDDEN AdaptiveTree
AdaptiveTree();
virtual ~AdaptiveTree();

virtual bool open(const std::string &url, const std::string &manifestUpdateParam) = 0;
virtual bool open(const std::string& url, const std::string& manifestUpdateParam) = 0;
virtual bool open(const std::string& url, const std::string& manifestUpdateParam, std::map<std::string, std::string> additionalHeaders) = 0;
virtual PREPARE_RESULT prepareRepresentation(Period* period,
AdaptationSet* adp,
Representation* rep,
Expand Down
137 changes: 128 additions & 9 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1982,7 +1982,8 @@ Session::Session(MANIFEST_TYPE manifestType,
uint16_t display_height,
const std::string& ov_audio,
bool play_timeshift_buffer,
bool force_secure_decoder)
bool force_secure_decoder,
const std::string& drmPreInitData)
: manifest_type_(manifestType),
manifestURL_(strURL),
manifestUpdateParam_(strUpdateParam),
Expand All @@ -2005,7 +2006,8 @@ Session::Session(MANIFEST_TYPE manifestType,
chapter_start_time_(0),
chapter_seek_time_(0.0),
play_timeshift_buffer_(play_timeshift_buffer),
force_secure_decoder_(force_secure_decoder)
force_secure_decoder_(force_secure_decoder),
drmPreInitData_(drmPreInitData)
{
switch (manifest_type_)
{
Expand Down Expand Up @@ -2222,10 +2224,31 @@ bool Session::Initialize(const std::uint8_t config, uint32_t max_user_bandwidth)
kodi::Log(ADDON_LOG_DEBUG, "Supported URN: %s", adaptiveTree_->supportedKeySystem_.c_str());
}

// Preinitialize the DRM, if pre-initialisation data are provided
std::map<std::string, std::string> additionalHeaders = std::map<std::string, std::string>();

if (!drmPreInitData_.empty())
{
std::string challengeB64;
std::string sessionId;
// Pre-initialize the DRM allow to generate the challenge and session ID data
// used to make licensed manifest requests (via proxy callback)
if (PreInitializeDRM(challengeB64, sessionId))
{
additionalHeaders["challengeB64"] = challengeB64;
additionalHeaders["sessionId"] = sessionId;
}
else
{
kodi::Log(ADDON_LOG_ERROR, "%s - DRM pre-initialization failed", __FUNCTION__);
return false;
}
}

// Open manifest file with location redirect support bool mpdSuccess;
std::string manifestUrl =
adaptiveTree_->location_.empty() ? manifestURL_.c_str() : adaptiveTree_->location_;
if (!adaptiveTree_->open(manifestUrl.c_str(), manifestUpdateParam_.c_str()) || adaptiveTree_->empty())
if (!adaptiveTree_->open(manifestUrl.c_str(), manifestUpdateParam_.c_str(), additionalHeaders) || adaptiveTree_->empty())
{
kodi::Log(ADDON_LOG_ERROR, "Could not open / parse manifest (%s)", manifestUrl.c_str());
return false;
Expand All @@ -2242,6 +2265,91 @@ bool Session::Initialize(const std::uint8_t config, uint32_t max_user_bandwidth)
return InitializePeriod();
}

bool Session::PreInitializeDRM(std::string& challengeB64, std::string& sessionId)
{
std::string psshData;
std::string kidData;
// Parse the PSSH/KID data
std::string::size_type posSplitter(drmPreInitData_.find("|"));
if (posSplitter != std::string::npos)
{
psshData = drmPreInitData_.substr(0, posSplitter);
kidData = drmPreInitData_.substr(posSplitter + 1);
}

if (psshData.empty() || kidData.empty())
{
kodi::Log(ADDON_LOG_ERROR, "%s - Invalid DRM pre-init data, must be as: {PSSH as base64}|{KID as base64}", __FUNCTION__);
return false;
}

cdm_sessions_.resize(2);
memset(&cdm_sessions_.front(), 0, sizeof(CDMSESSION));
// Try to initialize an SingleSampleDecryptor
kodi::Log(ADDON_LOG_DEBUG, "%s - Entering encryption section", __FUNCTION__);

if (license_key_.empty())
{
kodi::Log(ADDON_LOG_ERROR, "%s - Invalid license_key", __FUNCTION__);
return false;
}

if (!decrypter_)
{
kodi::Log(ADDON_LOG_ERROR, "%s - No decrypter found for encrypted stream", __FUNCTION__);
return false;
}

if (!decrypter_->HasCdmSession())
{
if (!decrypter_->OpenDRMSystem(license_key_.c_str(), server_certificate_, drmConfig_))
{
kodi::Log(ADDON_LOG_ERROR, "%s - OpenDRMSystem failed", __FUNCTION__);
return false;
}
}

AP4_DataBuffer init_data;
const char* optionalKeyParameter(nullptr);

// Set the provided PSSH
init_data.SetBufferSize(1024);
unsigned int init_data_size(1024);

b64_decode(psshData.c_str(), psshData.size(), init_data.UseData(), init_data_size);
init_data.SetDataSize(init_data_size);

// Decode the provided KID
uint8_t buffer[32];
unsigned int buffer_size(32);
b64_decode(kidData.c_str(), kidData.size(), buffer, buffer_size);
const char* decodedKid = reinterpret_cast<const char*>(buffer);

CDMSESSION& session(cdm_sessions_[1]);

char hexkid[36];
AP4_FormatHex(reinterpret_cast<const AP4_UI08*>(decodedKid), 16, hexkid), hexkid[32] = 0;
kodi::Log(ADDON_LOG_DEBUG, "%s - Initializing session with KID: %s", __FUNCTION__, hexkid);

if (decrypter_ && init_data.GetDataSize() >= 4 &&
(session.single_sample_decryptor_ = decrypter_->CreateSingleSampleDecrypter(
init_data, optionalKeyParameter, (const uint8_t*)decodedKid, true)) != 0)
{
session.cdm_session_str_ = session.single_sample_decryptor_->GetSessionId();
sessionId = session.cdm_session_str_;
challengeB64 = decrypter_->GetChallengeB64Data(session.single_sample_decryptor_);
}
else
{
kodi::Log(ADDON_LOG_ERROR, "%s - Initialize failed (SingleSampleDecrypter)", __FUNCTION__);
cdm_sessions_[1].single_sample_decryptor_ = nullptr;
return false;
}

DisposeSampleDecrypter();
return true;
}

bool Session::InitializeDRM()
{
cdm_sessions_.resize(adaptiveTree_->current_period_->psshSets_.size());
Expand Down Expand Up @@ -2459,7 +2567,7 @@ bool Session::InitializeDRM()
if (decrypter_ && init_data.GetDataSize() >= 4 &&
(session.single_sample_decryptor_ ||
(session.single_sample_decryptor_ = decrypter_->CreateSingleSampleDecrypter(
init_data, optionalKeyParameter, (const uint8_t*)defkid)) != 0))
init_data, optionalKeyParameter, (const uint8_t*)defkid, false)) != 0))
{

decrypter_->GetCapabilities(session.single_sample_decryptor_, (const uint8_t*)defkid,
Expand Down Expand Up @@ -3325,7 +3433,7 @@ bool CInputStreamAdaptive::Open(const kodi::addon::InputstreamProperty& props)
{
kodi::Log(ADDON_LOG_DEBUG, "Open()");

std::string lt, lk, ld, lsc, mfup, ov_audio;
std::string lt, lk, ld, lsc, mfup, ov_audio, drmPreInitData;
std::map<std::string, std::string> manh, medh;
std::string url = props.GetURL();
MANIFEST_TYPE manifest(MANIFEST_TYPE_UNKNOWN);
Expand Down Expand Up @@ -3401,7 +3509,19 @@ bool CInputStreamAdaptive::Open(const kodi::addon::InputstreamProperty& props)
max_user_bandwidth);
}
else if (prop.first == "inputstream.adaptive.play_timeshift_buffer")
{
m_playTimeshiftBuffer = stricmp(prop.second.c_str(), "true") == 0;
}
else if (prop.first == "inputstream.adaptive.pre_init_data")
{
// This property allow to "pre-initialize" the DRM with a PSSH/KID,
// the property value must be as "{PSSH as base64}|{KID as base64}".
// The challenge/session ID data generated by the initialisation of the DRM
// will be attached to the manifest request callback
// as HTTP headers with the names of "challengeB64" and "sessionId".
kodi::Log(ADDON_LOG_DEBUG, "found inputstream.adaptive.pre_init_data: [not shown]");
drmPreInitData = prop.second;
}
}

if (manifest == MANIFEST_TYPE_UNKNOWN)
Expand All @@ -3423,10 +3543,9 @@ bool CInputStreamAdaptive::Open(const kodi::addon::InputstreamProperty& props)

kodihost->SetProfilePath(props.GetProfileFolder());

m_session = std::shared_ptr<Session>(new Session(manifest, url.c_str(), mfup, lt, lk, ld, lsc,
manh, medh, props.GetProfileFolder(),
m_width, m_height, ov_audio,
m_playTimeshiftBuffer, force_secure_decoder));
m_session = std::shared_ptr<Session>(new Session(
manifest, url.c_str(), mfup, lt, lk, ld, lsc, manh, medh, props.GetProfileFolder(), m_width,
m_height, ov_audio, m_playTimeshiftBuffer, force_secure_decoder, drmPreInitData));
m_session->SetVideoResolution(m_width, m_height);

if (!m_session->Initialize(config, max_user_bandwidth))
Expand Down
5 changes: 4 additions & 1 deletion src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,11 @@ class ATTRIBUTE_HIDDEN Session : public adaptive::AdaptiveStreamObserver
uint16_t display_height,
const std::string& ov_audio,
bool play_timeshift_buffer,
bool force_secure_decoder);
bool force_secure_decoder,
const std::string& drm_preinit_data);
virtual ~Session();
bool Initialize(const std::uint8_t config, uint32_t max_user_bandwidth);
bool PreInitializeDRM(std::string& challengeB64, std::string& sessionId);
bool InitializeDRM();
bool InitializePeriod();
SampleReader *GetNextSample();
Expand Down Expand Up @@ -177,6 +179,7 @@ class ATTRIBUTE_HIDDEN Session : public adaptive::AdaptiveStreamObserver
MANIFEST_TYPE manifest_type_;
std::string manifestURL_, manifestUpdateParam_;
std::string license_key_, license_type_, license_data_;
std::string drmPreInitData_;
std::map<std::string, std::string> media_headers_;
AP4_DataBuffer server_certificate_;
std::string profile_path_;
Expand Down
8 changes: 7 additions & 1 deletion src/parser/DASHTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1557,6 +1557,11 @@ static void XMLCALL end(void* data, const char* el)
| DASHTree
+---------------------------------------------------------------------*/
bool DASHTree::open(const std::string& url, const std::string& manifestUpdateParam)
{
return open(url, manifestUpdateParam, std::map<std::string, std::string>());
}

bool DASHTree::open(const std::string& url, const std::string& manifestUpdateParam, std::map<std::string, std::string> additionalHeaders)
{
parser_ = XML_ParserCreate(NULL);
if (!parser_)
Expand All @@ -1569,7 +1574,8 @@ bool DASHTree::open(const std::string& url, const std::string& manifestUpdatePar
strXMLText_.clear();

PrepareManifestUrl(url, manifestUpdateParam);
bool ret = download(manifest_url_.c_str(), manifest_headers_) && !periods_.empty();
additionalHeaders.insert(manifest_headers_.begin(), manifest_headers_.end());
bool ret = download(manifest_url_.c_str(), additionalHeaders) && !periods_.empty();

XML_ParserFree(parser_);
parser_ = 0;
Expand Down
1 change: 1 addition & 0 deletions src/parser/DASHTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class ATTRIBUTE_HIDDEN DASHTree : public AdaptiveTree
public:
DASHTree();
virtual bool open(const std::string& url, const std::string& manifestUpdateParam) override;
virtual bool open(const std::string& url, const std::string& manifestUpdateParam, std::map<std::string, std::string> additionalHeaders) override;
virtual bool write_data(void* buffer, size_t buffer_size, void* opaque) override;
virtual void RefreshSegments(Period* period,
AdaptationSet* adp,
Expand Down
8 changes: 7 additions & 1 deletion src/parser/HLSTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,15 @@ int HLSTree::processEncryption(std::string baseUrl, std::map<std::string, std::s
}

bool HLSTree::open(const std::string& url, const std::string& manifestUpdateParam)
{
return open(url, manifestUpdateParam, std::map<std::string, std::string>());
}

bool HLSTree::open(const std::string& url, const std::string& manifestUpdateParam, std::map<std::string, std::string> additionalHeaders)
{
PrepareManifestUrl(url, manifestUpdateParam);
if (download(manifest_url_.c_str(), manifest_headers_, &manifest_stream))
additionalHeaders.insert(manifest_headers_.begin(), manifest_headers_.end());
if (download(manifest_url_.c_str(), additionalHeaders, &manifest_stream))
return processManifest(manifest_stream);
return false;
}
Expand Down
1 change: 1 addition & 0 deletions src/parser/HLSTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class ATTRIBUTE_HIDDEN HLSTree : public AdaptiveTree
virtual ~HLSTree();

virtual bool open(const std::string& url, const std::string& manifestUpdateParam) override;
virtual bool open(const std::string& url, const std::string& manifestUpdateParam, std::map<std::string, std::string> additionalHeaders) override;
virtual PREPARE_RESULT prepareRepresentation(Period* period,
AdaptationSet* adp,
Representation* rep,
Expand Down
8 changes: 7 additions & 1 deletion src/parser/SmoothTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,11 @@ static void XMLCALL end(void* data, const char* el)
+---------------------------------------------------------------------*/

bool SmoothTree::open(const std::string& url, const std::string& manifestUpdateParam)
{
return open(url, manifestUpdateParam, std::map<std::string, std::string>());
}

bool SmoothTree::open(const std::string& url, const std::string& manifestUpdateParam, std::map<std::string, std::string> additionalHeaders)
{
parser_ = XML_ParserCreate(NULL);
if (!parser_)
Expand All @@ -359,7 +364,8 @@ bool SmoothTree::open(const std::string& url, const std::string& manifestUpdateP
strXMLText_.clear();

PrepareManifestUrl(url, manifestUpdateParam);
bool ret = download(manifest_url_.c_str(), manifest_headers_);
additionalHeaders.insert(manifest_headers_.begin(), manifest_headers_.end());
bool ret = download(manifest_url_.c_str(), additionalHeaders);

XML_ParserFree(parser_);
parser_ = 0;
Expand Down
1 change: 1 addition & 0 deletions src/parser/SmoothTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class ATTRIBUTE_HIDDEN SmoothTree : public AdaptiveTree
public:
SmoothTree();
virtual bool open(const std::string& url, const std::string& manifestUpdateParam) override;
virtual bool open(const std::string& url, const std::string& manifestUpdateParam, std::map<std::string, std::string> additionalHeaders) override;
virtual bool write_data(void* buffer, size_t buffer_size, void* opaque) override;

enum
Expand Down
9 changes: 7 additions & 2 deletions wvdecrypter/cdm/media/cdm/cdm_adapter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,14 @@ void CdmAdapter::UpdateSession(uint32_t promise_id,
response, response_size);
}

void CdmAdapter::SetSessionActive()
void CdmAdapter::SetSessionActive(bool isActive)
{
session_active_ = true;
session_active_ = isActive;
}

bool CdmAdapter::IsSessionActive()
{
return session_active_;
}

void CdmAdapter::CloseSession(uint32_t promise_id,
Expand Down
4 changes: 3 additions & 1 deletion wvdecrypter/cdm/media/cdm/cdm_adapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ class CdmAdapter : public std::enable_shared_from_this<CdmAdapter>
const uint8_t* response,
uint32_t response_size);

void SetSessionActive();
void SetSessionActive(bool isActive);

bool IsSessionActive();

void CloseSession(uint32_t promise_id,
const char* session_id,
Expand Down
Loading

0 comments on commit 474f41b

Please sign in to comment.