Skip to content

Commit

Permalink
Merge pull request #1547 from CastagnaIT/encryptstate_fix
Browse files Browse the repository at this point in the history
[HLSTree] Fix and cleanup to encryption types/states
  • Loading branch information
CastagnaIT authored May 9, 2024
2 parents 0e0820d + ba99820 commit cb4d857
Show file tree
Hide file tree
Showing 14 changed files with 257 additions and 95 deletions.
9 changes: 4 additions & 5 deletions src/Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,7 @@ bool CSession::InitializeDRM(bool addDefaultKID /* = false */)
m_cdmSessions.resize(m_adaptiveTree->m_currentPeriod->GetPSSHSets().size());

// Try to initialize an SingleSampleDecryptor
if (m_adaptiveTree->m_currentPeriod->GetEncryptionState() !=
EncryptionState::UNENCRYPTED)
if (m_adaptiveTree->m_currentPeriod->GetEncryptionState() == EncryptionState::ENCRYPTED_DRM)
{
std::string_view licenseKey = CSrvBroker::GetKodiProps().GetLicenseKey();

Expand Down Expand Up @@ -581,14 +580,14 @@ bool CSession::InitializePeriod(bool isSessionOpened /* = false */)
isPsshChanged =
!(m_adaptiveTree->m_currentPeriod->GetPSSHSets() == m_adaptiveTree->m_nextPeriod->GetPSSHSets());
isReusePssh = !isPsshChanged && m_adaptiveTree->m_nextPeriod->GetEncryptionState() ==
EncryptionState::ENCRYPTED_SUPPORTED;
EncryptionState::ENCRYPTED_DRM;
m_adaptiveTree->m_currentPeriod = m_adaptiveTree->m_nextPeriod;
m_adaptiveTree->m_nextPeriod = nullptr;
}

m_chapterStartTime = GetChapterStartTime();

if (m_adaptiveTree->m_currentPeriod->GetEncryptionState() == EncryptionState::ENCRYPTED)
if (m_adaptiveTree->m_currentPeriod->GetEncryptionState() == EncryptionState::NOT_SUPPORTED)
{
LOG::LogF(LOGERROR, "Unhandled encrypted stream.");
return false;
Expand Down Expand Up @@ -949,7 +948,7 @@ void CSession::PrepareStream(CStream* stream)
}

if (startEvent != EVENT_TYPE::REP_CHANGE &&
stream->m_adStream.getPeriod()->GetEncryptionState() == EncryptionState::ENCRYPTED_SUPPORTED)
stream->m_adStream.getPeriod()->GetEncryptionState() == EncryptionState::ENCRYPTED_DRM)
{
InitializeDRM();
}
Expand Down
1 change: 0 additions & 1 deletion src/common/AdaptationSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ void PLAYLIST::CAdaptationSet::CopyHLSData(const CAdaptationSet* other)

m_baseUrl = other->m_baseUrl;
m_streamType = other->m_streamType;
m_startNumber = other->m_startNumber;
m_isImpaired = other->m_isImpaired;
m_isOriginal = other->m_isOriginal;
m_isDefault = other->m_isDefault;
Expand Down
6 changes: 3 additions & 3 deletions src/common/AdaptiveUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ constexpr uint64_t KODI_VP_BUFFER_SECS = 8;
enum class EncryptionState
{
UNENCRYPTED,
ENCRYPTED, // Unhandled/unsupported encrypted stream
ENCRYPTED_SUPPORTED, // Supported encrypted stream
ENCRYPTED_DRM, // DRM encrypted
ENCRYPTED_CK, // ClearKey encrypted (e.g. AES-128)
NOT_SUPPORTED, // Unsupported encryption
};

enum class EncryptionType
Expand All @@ -58,7 +59,6 @@ enum class EncryptionType
CLEAR,
AES128,
WIDEVINE,
UNKNOWN,
};

enum class ContainerType
Expand Down
2 changes: 0 additions & 2 deletions src/common/Period.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ void PLAYLIST::CPeriod::CopyHLSData(const CPeriod* other)
m_baseUrl = other->m_baseUrl;
m_id = other->m_id;
m_timescale = other->m_timescale;
m_encryptionState = other->m_encryptionState;
m_includedStreamType = other->m_includedStreamType;
m_isSecureDecoderNeeded = other->m_isSecureDecoderNeeded;
}

uint16_t PLAYLIST::CPeriod::InsertPSSHSet(const PSSHSet& psshSet)
Expand Down
2 changes: 0 additions & 2 deletions src/common/Representation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ void PLAYLIST::CRepresentation::CopyHLSData(const CRepresentation* other)

m_isIncludedStream = other->m_isIncludedStream;
m_isEnabled = other->m_isEnabled;
m_isWaitForSegment = other->m_isWaitForSegment;
m_initSegment = other->m_initSegment;
}

void PLAYLIST::CRepresentation::SetScaling()
Expand Down
6 changes: 3 additions & 3 deletions src/parser/DASHTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ void adaptive::CDashTree::ParseTagAdaptationSet(pugi::xml_node nodeAdp, PLAYLIST
// Parse <ContentProtection> child tags
if (nodeAdp.child("ContentProtection"))
{
period->SetEncryptionState(EncryptionState::ENCRYPTED);
period->SetEncryptionState(EncryptionState::NOT_SUPPORTED);
ParseTagContentProtection(nodeAdp, adpSet->ProtectionSchemes());
period->SetSecureDecodeNeeded(ParseTagContentProtectionSecDec(nodeAdp));
}
Expand Down Expand Up @@ -947,7 +947,7 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr,
// Parse <ContentProtection> child tags
if (nodeRepr.child("ContentProtection"))
{
period->SetEncryptionState(EncryptionState::ENCRYPTED);
period->SetEncryptionState(EncryptionState::NOT_SUPPORTED);
ParseTagContentProtection(nodeRepr, repr->ProtectionSchemes());
}

Expand All @@ -961,7 +961,7 @@ void adaptive::CDashTree::ParseTagRepresentation(pugi::xml_node nodeRepr,
if (m_isCustomInitPssh ||
GetProtectionData(adpSet->ProtectionSchemes(), repr->ProtectionSchemes(), pssh, kid))
{
period->SetEncryptionState(EncryptionState::ENCRYPTED_SUPPORTED);
period->SetEncryptionState(EncryptionState::ENCRYPTED_DRM);

uint16_t psshSetPos = InsertPsshSet(adpSet->GetStreamType(), period, adpSet, pssh, kid);

Expand Down
148 changes: 78 additions & 70 deletions src/parser/HLSTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,34 +491,42 @@ void adaptive::CHLSTree::FixDiscSequence(std::stringstream& streamData, uint32_t
if (tagName == "#EXT-X-KEY" && !isSkipUntilDiscont)
{
auto attribs = ParseTagAttributes(tagValue);

// NOTE: Multiple EXT-X-KEYs can be parsed sequentially
switch (ProcessEncryption(rep->GetBaseUrl(), attribs))
{
case EncryptionType::CLEAR:
currentEncryptionType = EncryptionType::CLEAR;
period->SetEncryptionState(EncryptionState::UNENCRYPTED);
psshSetPos = PSSHSET_POS_DEFAULT;
break;
case EncryptionType::UNKNOWN:
currentEncryptionType = EncryptionType::UNKNOWN;
period->SetEncryptionState(EncryptionState::ENCRYPTED);
break;
case EncryptionType::NOT_SUPPORTED:
currentEncryptionType = EncryptionType::NOT_SUPPORTED;
period->SetEncryptionState(EncryptionState::ENCRYPTED);
break;
case EncryptionType::AES128:
currentEncryptionType = EncryptionType::AES128;
period->SetEncryptionState(EncryptionState::UNENCRYPTED);
psshSetPos = PSSHSET_POS_DEFAULT;
if (period->GetEncryptionState() != EncryptionState::ENCRYPTED_DRM)
{
currentEncryptionType = EncryptionType::AES128;
period->SetEncryptionState(EncryptionState::ENCRYPTED_CK);
psshSetPos = PSSHSET_POS_DEFAULT;
}
break;
case EncryptionType::WIDEVINE:
currentEncryptionType = EncryptionType::WIDEVINE;
period->SetEncryptionState(EncryptionState::ENCRYPTED_SUPPORTED);

rep->m_psshSetPos = InsertPsshSet(adp->GetStreamType(), period, adp, m_currentPssh,
m_currentDefaultKID, m_currentKidUrl, m_currentIV);
if (period->GetEncryptionState() != EncryptionState::ENCRYPTED_CK)
{
currentEncryptionType = EncryptionType::WIDEVINE;
period->SetEncryptionState(EncryptionState::ENCRYPTED_DRM);
rep->m_psshSetPos = InsertPsshSet(adp->GetStreamType(), period, adp, m_currentPssh,
m_currentDefaultKID, m_currentKidUrl, m_currentIV);
}
break;
case EncryptionType::NOT_SUPPORTED:
// Set only if a supported encryption has not previously been parsed
if (period->GetEncryptionState() != EncryptionState::ENCRYPTED_DRM &&
period->GetEncryptionState() != EncryptionState::ENCRYPTED_CK)
{
currentEncryptionType = EncryptionType::NOT_SUPPORTED;
period->SetEncryptionState(EncryptionState::NOT_SUPPORTED);
}
break;
default:
LOG::LogF(LOGFATAL, "Unhandled EncryptionType");
break;
}
}
Expand Down Expand Up @@ -775,44 +783,51 @@ void adaptive::CHLSTree::FixDiscSequence(std::stringstream& streamData, uint32_t
isSkipUntilDiscont = false;
++discontCount;

// Create a new period or update an existing one
mediaSequenceNbr += rep->SegmentTimeline().GetSize();
currentSegNumber = mediaSequenceNbr;

CPeriod* foundPeriod = FindDiscontinuityPeriod(m_discontSeq + discontCount);
if (foundPeriod) // Update existing period
CPeriod* newPeriod = FindDiscontinuityPeriod(m_discontSeq + discontCount);

if (!newPeriod) // Create new period
{
period = foundPeriod;
auto newPeriodPtr = CPeriod::MakeUniquePtr();

// Clone same data structure from previous period (no segment will be copied)
newPeriodPtr->CopyHLSData(period);
newPeriod = newPeriodPtr.get();
m_periods.push_back(std::move(newPeriodPtr));
}
else // Create new period
{
auto newPeriod = CPeriod::MakeUniquePtr();
// CopyHLSData will copy also the init segment in the representations
// that must persist to next period until overrided by new EXT-X-MAP tag
newPeriod->CopyHLSData(m_currentPeriod);

period = newPeriod.get();
period->SetStart(0);
newPeriod->SetStart(0);

m_periods.push_back(std::move(newPeriod));
}
CAdaptationSet* newAdpSet = newPeriod->GetAdaptationSets()[adpSetPos].get();
CRepresentation* newRep = newAdpSet->GetRepresentations()[reprPos].get();

mediaSequenceNbr += rep->SegmentTimeline().GetSize();
currentSegNumber = mediaSequenceNbr;
// Copy the base url from previous period/representation
newRep->SetBaseUrl(rep->GetBaseUrl());

adp = period->GetAdaptationSets()[adpSetPos].get();
// When we switch to a repr of another period we need to set current base url
CRepresentation* switchRep = adp->GetRepresentations()[reprPos].get();
switchRep->SetBaseUrl(rep->GetBaseUrl());
rep = switchRep;
// Copy init segment from previous period/representation
// it must persist until overrided by a new EXT-X-MAP tag
if (rep->GetInitSegment().has_value())
{
newRep->SetInitSegment(*rep->GetInitSegment());
newRep->SetContainerType(rep->GetContainerType());
}

// Copy encryption data from previous period/representation
// it must persist until overrided by a new EXT-X-KEY tag
newPeriod->SetEncryptionState(period->GetEncryptionState());
if (currentEncryptionType == EncryptionType::WIDEVINE)
{
rep->m_psshSetPos = InsertPsshSet(adp->GetStreamType(), period, adp, m_currentPssh,
m_currentDefaultKID, m_currentKidUrl, m_currentIV);
period->SetEncryptionState(EncryptionState::ENCRYPTED_SUPPORTED);
newRep->m_psshSetPos =
InsertPsshSet(newAdpSet->GetStreamType(), newPeriod, newAdpSet, m_currentPssh,
m_currentDefaultKID, m_currentKidUrl, m_currentIV);
}

if (rep->HasInitSegment())
rep->SetContainerType(ContainerType::MP4);
// Set the new period as current
period = newPeriod;
adp = newAdpSet;
rep = newRep;
}
else if (tagName == "#EXT-X-ENDLIST")
{
Expand Down Expand Up @@ -939,7 +954,7 @@ void adaptive::CHLSTree::OnDataArrived(uint64_t segNum,
size_t segBufferSize,
bool isLastChunk)
{
if (psshSet && m_currentPeriod->GetEncryptionState() != EncryptionState::ENCRYPTED_SUPPORTED)
if (psshSet && m_currentPeriod->GetEncryptionState() == EncryptionState::ENCRYPTED_CK)
{
std::lock_guard<TreeUpdateThread> lckUpdTree(GetTreeUpdMutex());

Expand Down Expand Up @@ -1222,7 +1237,9 @@ PLAYLIST::EncryptionType adaptive::CHLSTree::ProcessEncryption(
}

// WIDEVINE
if (STRING::CompareNoCase(attribs["KEYFORMAT"], "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"))
if (STRING::CompareNoCase(attribs["KEYFORMAT"],
"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed") &&
STRING::CompareNoCase(attribs["KEYFORMAT"], m_supportedKeySystem))
{
m_currentPssh = uriData;

Expand Down Expand Up @@ -1256,14 +1273,9 @@ PLAYLIST::EncryptionType adaptive::CHLSTree::ProcessEncryption(
return EncryptionType::WIDEVINE;
}

// KNOWN UNSUPPORTED
if (STRING::CompareNoCase(attribs["KEYFORMAT"], "com.apple.streamingkeydelivery"))
{
LOG::LogF(LOGDEBUG, "Keyformat %s not supported", attribs["KEYFORMAT"].c_str());
return EncryptionType::NOT_SUPPORTED;
}

return EncryptionType::UNKNOWN;
// Unsupported encryption
LOG::Log(LOGDEBUG, "Unsupported EXT-X-KEY keyformat \"%s\"", attribs["KEYFORMAT"].c_str());
return EncryptionType::NOT_SUPPORTED;
}

bool adaptive::CHLSTree::GetUriByteData(std::string_view uri, std::vector<uint8_t>& data)
Expand Down Expand Up @@ -1354,6 +1366,7 @@ bool adaptive::CHLSTree::ParseMultivariantPlaylist(const std::string& data)
{
std::stringstream streamData{data};
MultivariantPlaylist pl;
std::vector<EncryptionType> encryptionTypes;

// Parse text data

Expand Down Expand Up @@ -1462,26 +1475,21 @@ bool adaptive::CHLSTree::ParseMultivariantPlaylist(const std::string& data)
else if (tagName == "#EXT-X-SESSION-KEY")
{
auto attribs = ParseTagAttributes(tagValue);

switch (ProcessEncryption(base_url_, attribs))
{
case EncryptionType::NOT_SUPPORTED:
return false;
case EncryptionType::AES128:
case EncryptionType::WIDEVINE:
// #EXT-X-SESSION-KEY is meant for preparing DRM without
// loading sub-playlist. As long our workflow is serial, we
// don't profite and therefore do not any action.
break;
case EncryptionType::UNKNOWN:
LOG::LogF(LOGWARNING, "Unknown encryption type");
break;
default:
break;
}
encryptionTypes.emplace_back(ProcessEncryption(base_url_, attribs));
}
}

if (!encryptionTypes.empty())
{
// Check if there is at least one EXT-X-SESSION-KEY supported
bool isNotSupported =
std::all_of(encryptionTypes.begin(), encryptionTypes.end(),
[](EncryptionType type) { return type == EncryptionType::NOT_SUPPORTED; });

if (isNotSupported)
return false;
}

// Create Period / Adaptation sets / Representations

std::unique_ptr<CPeriod> period = CPeriod::MakeUniquePtr();
Expand Down
4 changes: 2 additions & 2 deletions src/parser/SmoothTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ bool adaptive::CSmoothTree::ParseManifest(const std::string& data)
xml_node nodeProt = nodeSSM.child("Protection");
if (nodeProt)
{
period->SetEncryptionState(EncryptionState::ENCRYPTED);
period->SetEncryptionState(EncryptionState::NOT_SUPPORTED);
period->SetSecureDecodeNeeded(true);

pugi::xml_node nodeProtHead = nodeProt.child("ProtectionHeader");
Expand All @@ -105,7 +105,7 @@ bool adaptive::CSmoothTree::ParseManifest(const std::string& data)
{
if (protParser.ParseHeader(nodeProtHead.child_value()))
{
period->SetEncryptionState(EncryptionState::ENCRYPTED_SUPPORTED);
period->SetEncryptionState(EncryptionState::ENCRYPTED_DRM);
m_licenseUrl = protParser.GetLicenseURL();
}
}
Expand Down
Loading

0 comments on commit cb4d857

Please sign in to comment.