diff --git a/AampConfig.cpp b/AampConfig.cpp index a91b7d88f0..0422f8276a 100644 --- a/AampConfig.cpp +++ b/AampConfig.cpp @@ -276,7 +276,7 @@ static const ConfigLookupEntryBool mConfigLookupTableBool[AAMPCONFIG_BOOL_COUNT] {false,"appSrcForProgressivePlayback",eAAMPConfig_UseAppSrcForProgressivePlayback,false}, {false,"descriptiveAudioTrack",eAAMPConfig_DescriptiveAudioTrack,false}, {true,"reportBufferEvent",eAAMPConfig_ReportBufferEvent,false}, - {false,"info",eAAMPConfig_InfoLogging,true}, + {true,"info",eAAMPConfig_InfoLogging,true}, {false,"debug",eAAMPConfig_DebugLogging,false}, {false,"trace",eAAMPConfig_TraceLogging,false}, {true,"warn",eAAMPConfig_WarnLogging,false}, diff --git a/aampgstplayer.cpp b/aampgstplayer.cpp index 9512db260b..c0bcc30530 100644 --- a/aampgstplayer.cpp +++ b/aampgstplayer.cpp @@ -558,12 +558,19 @@ static void HandleBusMessage(const BusEventData busEvent, AAMPGstPlayer * _this) case MESSAGE_ERROR: { std::string errorDesc = "GstPipeline Error:" + busEvent.msg; + if (_this->aamp->HasHDCPProtectionError() && + busEvent.msg.find("Rialto dropped a frame that failed to decrypt") != std::string::npos) + { + // Drop this error message as its due to HDCP output protection ie, HDMI un-plug. + return; + } if (busEvent.msg.find("video decode error") != std::string::npos) { _this->aamp->SendErrorEvent(AAMP_TUNE_GST_PIPELINE_ERROR, errorDesc.c_str(), false); } else if (busEvent.msg.find("HDCP Compliance Check Failure") != std::string::npos) { + // TODO: Replace with a common way for Rialto and non-Rialto pipelines. _this->aamp->SendErrorEvent(AAMP_TUNE_HDCP_COMPLIANCE_ERROR, errorDesc.c_str(), false); } else if ((busEvent.msg.find("Internal data stream error") != std::string::npos) && _this->aamp->mConfig->IsConfigSet(eAAMPConfig_RetuneForGSTError)) @@ -639,6 +646,7 @@ static void HandleBusMessage(const BusEventData busEvent, AAMPGstPlayer * _this) break; case MESSAGE_APPLICATION: + // Should be deprecated if (busEvent.msg.find("HDCPProtectionFailure") != std::string::npos) { AAMPLOG_ERR("Received HDCPProtectionFailure event.Schedule Retune "); 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..88b15813b4 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,46 @@ #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 PlayerKeyStatus::PLAYER_KEY_USABLE; + case Expired: return PlayerKeyStatus::PLAYER_KEY_EXPIRED; + case Released: return PlayerKeyStatus::PLAYER_KEY_RELEASED; + case OutputRestricted: return PlayerKeyStatus::PLAYER_KEY_OUTPUT_RESTRICTED; + case OutputRestrictedHDCP22: return PlayerKeyStatus::PLAYER_KEY_OUTPUT_RESTRICTED_HDCP22; + case OutputDownscaled: return PlayerKeyStatus::PLAYER_KEY_OUTPUT_DOWNSCALED; + case StatusPending: return PlayerKeyStatus::PLAYER_KEY_STATUS_PENDING; + case HWError: return PlayerKeyStatus::PLAYER_KEY_HW_ERROR; + case InternalError: default: return PlayerKeyStatus::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: + case OutputRestrictedHDCP22: + case OutputDownscaled: + return true; + default: + return false; + } +} + /** * @fn OCDMSessionAdapter * @brief OCDMSessionAdapter constructor @@ -61,6 +100,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(), @@ -215,6 +255,7 @@ void OCDMSessionAdapter::processOCDMChallenge(const char destUrl[], const uint8_ void OCDMSessionAdapter::keyUpdateOCDM(const uint8_t key[], const uint8_t keySize) { MW_LOG_INFO("at %p, with %p, %p", this , m_pOpenCDMSystem, m_pOpenCDMSession); + printf("keyUpdateOCDM-> Key: "); for (uint8_t i = 0; i < keySize; i++) printf("%02X", key[i]); printf("\n"); // Validate input parameters if (key != nullptr && keySize > 0) { @@ -224,7 +265,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_WARN("Key status from OCDM: %d", m_keyStatus); m_keyStateIndeterminate = false; + if (m_keyStatus != Usable) + { + m_sessionKeyStatus = m_keyStatus; + } } else { @@ -248,6 +294,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 +480,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..f7f4b27a26 100644 --- a/priv_aamp.cpp +++ b/priv_aamp.cpp @@ -1830,6 +1830,7 @@ PrivateInstanceAAMP::PrivateInstanceAAMP(AampConfig *config) : mReportProgressPo , mThumbnailLastProgramDateTime(0) , mLastSleThumbnailInfo() , mLatencyMonitor(std::make_unique(this)) + , mDRMKeyStatus(PlayerKeyStatus::PLAYER_KEY_STATUS_PENDING) { AAMPLOG_MIL("Create Private Player %d", mPlayerId); mAampCacheHandler = new AampCacheHandler(mPlayerId); @@ -11462,6 +11463,29 @@ 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)); + // Check if we are coming out of a HDCP protection error state, means HDMI plugged in. + // Do a retune to recover the playback internally. + // Check before saving the new keyStatus so retune can be scheduled. + bool hdcpError = HasHDCPProtectionError(); + + // Update DRM key status. This will be checked when we receive a GStreamer playback error for HDCP errors. + SetDRMKeyStatus(keyStatus); + // Note - mDRMKeyStatus is persisted across sessions as the same URL could be retried again + // but the callback only fires once for the DRM session. For a new playback, a new session + // will be created, which will trigger the callback with KeyUsable status so the mDRMKeyStatus is updated properly. + if (hdcpError && (keyStatus == PlayerKeyStatus::PLAYER_KEY_USABLE)) + { + // Retune is asynchronously scheduled so should not cause any deadlocks. + ScheduleRetune(eGST_ERROR_OUTPUT_PROTECTION_ERROR, eMEDIATYPE_VIDEO); + } +} + /** * @brief Get current initial buffer duration in seconds */ diff --git a/priv_aamp.h b/priv_aamp.h index f0393970bd..20920ac845 100644 --- a/priv_aamp.h +++ b/priv_aamp.h @@ -73,6 +73,7 @@ #include "TextTrackInfo.h" #include "AAMPAnomalyMessageType.h" #include "AampDemuxDataTypes.h" +#include "DrmSession.h" // For PlayerKeyStatus #define FAKE_TUNE_URL "file:///etc/manifest.mpd" /**< Fake tune URL for testing purposes */ @@ -1336,6 +1337,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 * @@ -4057,6 +4065,22 @@ class PrivateInstanceAAMP : public DrmCallbacks, public std::enable_shared_from_ */ bool IsAdPlaying(); + /** + * @brief Has encountered HDCP Output Protection Error + */ + bool HasHDCPProtectionError() { return mDRMKeyStatus.load() == PlayerKeyStatus::PLAYER_KEY_OUTPUT_RESTRICTED; } + + /** + * @brief Has encountered HDCP Compliance Error + */ + bool HasHDCPComplianceError() { return mDRMKeyStatus.load() == PlayerKeyStatus::PLAYER_KEY_OUTPUT_RESTRICTED_HDCP22; } + + /** + * @brief Set the DRM key status + * @param status - New DRM key status + */ + void SetDRMKeyStatus(PlayerKeyStatus status) { mDRMKeyStatus.store(status); } + protected: /** @@ -4353,5 +4377,7 @@ class PrivateInstanceAAMP : public DrmCallbacks, public std::enable_shared_from_ * video_muted and subtitle_muted. */ void SetCCStatusInternal(void); + + std::atomic mDRMKeyStatus {PlayerKeyStatus::PLAYER_KEY_STATUS_PENDING}; /**< Key status for the current session */ }; #endif // PRIVAAMP_H 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 +}