diff --git a/middleware/drm/DrmCallbacks.h b/middleware/drm/DrmCallbacks.h index 79bf5fa0d1..5495aa7d39 100644 --- a/middleware/drm/DrmCallbacks.h +++ b/middleware/drm/DrmCallbacks.h @@ -26,6 +26,7 @@ */ #include +#include "DrmSession.h" /** * @class DrmCallbacks @@ -36,6 +37,7 @@ class DrmCallbacks public: virtual void Individualization(const std::string& payload) = 0; virtual void LicenseRenewal(DrmHelperPtr drmHelper, void* userData) = 0; + virtual void NotifyKeyStatus(PlayerKeyStatus keyStatus) = 0; virtual ~DrmCallbacks() {}; }; diff --git a/middleware/drm/DrmSession.h b/middleware/drm/DrmSession.h index c7d7ff1065..f6864fdcec 100755 --- a/middleware/drm/DrmSession.h +++ b/middleware/drm/DrmSession.h @@ -32,6 +32,22 @@ #include "DrmUtils.h" #include "ContentSecurityManagerSession.h" +/** + * @enum PlayerKeyStatus + * @brief DRM key status values, independent of OCDM. + */ +typedef enum { + PLAYER_KEY_USABLE = 0, + PLAYER_KEY_EXPIRED, + PLAYER_KEY_RELEASED, + PLAYER_KEY_OUTPUT_RESTRICTED, + PLAYER_KEY_OUTPUT_RESTRICTED_HDCP22, + PLAYER_KEY_OUTPUT_DOWNSCALED, + PLAYER_KEY_STATUS_PENDING, + PLAYER_KEY_INTERNAL_ERROR, + PLAYER_KEY_HW_ERROR +} PlayerKeyStatus; + using namespace std; #define PLAYREADY_KEY_SYSTEM_STRING "com.microsoft.playready" diff --git a/middleware/drm/ocdm/opencdmsessionadapter.cpp b/middleware/drm/ocdm/opencdmsessionadapter.cpp index e45ca1197f..4163766e66 100644 --- a/middleware/drm/ocdm/opencdmsessionadapter.cpp +++ b/middleware/drm/ocdm/opencdmsessionadapter.cpp @@ -22,7 +22,6 @@ * @brief Handles operation with OCDM session to handle DRM License data */ #include "opencdmsessionadapter.h" - #include "DrmHelper.h" #include "PlayerUtils.h" @@ -44,6 +43,44 @@ #include #define LICENSE_RENEWAL_MESSAGE_TYPE "1" +/** + * @fn toPlayerKeyStatus + * @brief Convert OCDM KeyStatus to PlayerKeyStatus. + * @param ocdmStatus The KeyStatus from OCDM. + * @return The corresponding PlayerKeyStatus. + */ +static PlayerKeyStatus toPlayerKeyStatus(KeyStatus ocdmStatus) { + switch (ocdmStatus) { + case Usable: return PLAYER_KEY_USABLE; + case Expired: return PLAYER_KEY_EXPIRED; + case Released: return PLAYER_KEY_RELEASED; + case OutputRestricted: return PLAYER_KEY_OUTPUT_RESTRICTED; + case OutputRestrictedHDCP22: return PLAYER_KEY_OUTPUT_RESTRICTED_HDCP22; + case OutputDownscaled: return PLAYER_KEY_OUTPUT_DOWNSCALED; + case StatusPending: return PLAYER_KEY_STATUS_PENDING; + case HWError: return PLAYER_KEY_HW_ERROR; + case InternalError: default: return PLAYER_KEY_INTERNAL_ERROR; + } +} + +/** + * @fn ShouldNotifyKeyStatus + * @brief Determine if a given KeyStatus should trigger a key status notification to the player. + * @param status The KeyStatus to evaluate. + * @return true if the status should trigger a notification, false otherwise. + */ +bool ShouldNotifyKeyStatus(KeyStatus status) +{ + switch (status) + { + case OutputRestricted: + case Usable: + return true; + default: + return false; + } +} + /** * @fn OCDMSessionAdapter * @brief OCDMSessionAdapter constructor @@ -61,6 +98,7 @@ OCDMSessionAdapter::OCDMSessionAdapter(DrmHelperPtr drmHelper, DrmCallbacks *cal m_challengeReady(), m_challengeSize(0), m_keyStatus(InternalError), + m_sessionKeyStatus(Usable), m_keyStateIndeterminate(false), m_keyStatusReady(), m_OCDMSessionCallbacks(), @@ -224,7 +262,12 @@ void OCDMSessionAdapter::keyUpdateOCDM(const uint8_t key[], const uint8_t keySiz if (m_pOpenCDMSession) { m_keyStatus = opencdm_session_status(m_pOpenCDMSession, key, keySize); + MW_LOG_INFO("Key status from OCDM: %d", m_keyStatus); m_keyStateIndeterminate = false; + if (m_keyStatus != Usable) + { + m_sessionKeyStatus = m_keyStatus; + } } else { @@ -248,6 +291,17 @@ void OCDMSessionAdapter::keyUpdateOCDM(const uint8_t key[], const uint8_t keySiz void OCDMSessionAdapter::keysUpdatedOCDM() { MW_LOG_INFO("at %p, with %p, %p", this , m_pOpenCDMSystem, m_pOpenCDMSession); m_keyStatusReady.signal(); + const KeyStatus notifyStatus = m_sessionKeyStatus; + m_sessionKeyStatus = Usable; // reset for next burst + if (m_drmCallbacks && (ShouldNotifyKeyStatus(notifyStatus) == true)) + { + MW_LOG_INFO("keysUpdatedOCDM notifying key status %d", notifyStatus); + m_drmCallbacks->NotifyKeyStatus(toPlayerKeyStatus(notifyStatus)); + } + else + { + MW_LOG_INFO("Key status %d does not trigger a notification to the player", notifyStatus); + } } @@ -423,6 +477,7 @@ void OCDMSessionAdapter:: clearDecryptContext() std::lock_guard keyLock(m_usableKeysMutex); m_usableKeys.clear(); } + m_sessionKeyStatus = Usable; m_eKeyState = KEY_INIT; } diff --git a/middleware/drm/ocdm/opencdmsessionadapter.h b/middleware/drm/ocdm/opencdmsessionadapter.h index a1554deb1c..7714cfb1c9 100644 --- a/middleware/drm/ocdm/opencdmsessionadapter.h +++ b/middleware/drm/ocdm/opencdmsessionadapter.h @@ -103,6 +103,7 @@ class OCDMSessionAdapter : public DrmSession std::string m_destUrl; KeyStatus m_keyStatus; + KeyStatus m_sessionKeyStatus; // tracks any non-Usable key status received in a callback burst bool m_keyStateIndeterminate; std::vector m_keyStored; std::vector> m_usableKeys; // Store usable key IDs from ocdm_update_callback diff --git a/priv_aamp.cpp b/priv_aamp.cpp index 031c73f392..ebacb60250 100644 --- a/priv_aamp.cpp +++ b/priv_aamp.cpp @@ -11462,6 +11462,14 @@ void PrivateInstanceAAMP::Individualization(const std::string& payload) SendEvent(event,AAMP_EVENT_ASYNC_MODE); } +/** + * @brief DRM key status notification callback + */ +void PrivateInstanceAAMP::NotifyKeyStatus(PlayerKeyStatus keyStatus) +{ + AAMPLOG_WARN("NotifyKeyStatus: keyStatus=%d", static_cast(keyStatus)); +} + /** * @brief Get current initial buffer duration in seconds */ diff --git a/priv_aamp.h b/priv_aamp.h index f0393970bd..240e36e749 100644 --- a/priv_aamp.h +++ b/priv_aamp.h @@ -1336,6 +1336,13 @@ class PrivateInstanceAAMP : public DrmCallbacks, public std::enable_shared_from_ * @return void */ void LicenseRenewal(DrmHelperPtr drmHelper,void* userData) override; + /** + * @fn NotifyKeyStatus + * + * @param[in] keyStatus - Key status received from OCDM + * @return void + */ + void NotifyKeyStatus(PlayerKeyStatus keyStatus) override; /** * @fn CurlTerm * diff --git a/test/utests/drm/mocks/aampMocks.cpp b/test/utests/drm/mocks/aampMocks.cpp index 777a2c78fb..d0a31096cd 100644 --- a/test/utests/drm/mocks/aampMocks.cpp +++ b/test/utests/drm/mocks/aampMocks.cpp @@ -545,6 +545,11 @@ void PrivateInstanceAAMP::UnlockGetPositionMilliseconds() { } +void PrivateInstanceAAMP::NotifyKeyStatus(PlayerKeyStatus keyStatus) +{ + (void)keyStatus; +} + void PrivateInstanceAAMP::SetPreferredLanguages(const char *, const char *, const char *, const char *, const char *, const Accessibility *, const char *) diff --git a/test/utests/fakes/FakePrivateInstanceAAMP.cpp b/test/utests/fakes/FakePrivateInstanceAAMP.cpp index 1d60204833..b812c8496e 100644 --- a/test/utests/fakes/FakePrivateInstanceAAMP.cpp +++ b/test/utests/fakes/FakePrivateInstanceAAMP.cpp @@ -688,6 +688,11 @@ void PrivateInstanceAAMP::UnlockGetPositionMilliseconds() { } +void PrivateInstanceAAMP::NotifyKeyStatus(PlayerKeyStatus keyStatus) +{ + (void) keyStatus; +} + void PrivateInstanceAAMP::SetPreferredLanguages(char const*, char const*, char const*, char const*, char const*, const Accessibility*, char const*) { } @@ -1913,4 +1918,4 @@ bool PrivateInstanceAAMP::CheckForChunkEarlyAbort(CurlCallbackContext *context) void PrivateInstanceAAMP::EnableLatencyMonitor(bool enabled) { -} \ No newline at end of file +}