The PlayReady OCDM (Open Content Decryption Module) component implements the Microsoft PlayReady DRM backend for WPEFramework (Thunder). It enables protected media playback by performing license acquisition, key binding, and hardware-accelerated content decryption through a standardized CDMi interface.

The component manages the complete lifecycle of a DRM session: parsing PlayReady PSSH initialization data extracted from the content manifest, generating a license challenge for dispatch to a license server, processing the license response to bind decryption keys, and decrypting encrypted media samples. The component is delivered as a shared object (Playready.drm) installed into the WPEFramework OCDM discovery directory and loaded by the WPEFramework OCDM Plugin (OpenCDMi) at runtime.

From a stack perspective, the component resides within WPEFramework (Thunder) and exposes the CDMi IMediaKeys and IMediaKeysExt interfaces to the WPEFramework OCDM Plugin (OpenCDMi) above it. Below, it depends on the PlayReady SDK for all DRM operations and on a platform-specific Secure Video Path (SVP) library (gst-svp-ext) for routing decrypted video content through protected memory without exposing it to normally accessible memory.

At the device level, the component allows PlayReady-protected video-on-demand and live streaming content to be played back on the device. At the module level, it manages the DRM application context lifecycle, session-scoped key state machines, license store maintenance, Secure Stop session tracking, and output protection policy enforcement.

flowchart LR

%% Apps Layer
    subgraph Apps["Apps & Runtimes"]
        FireboltApps["Firebolt Apps"]
        WPERuntime["WPE Runtime"]
    end

%% Middleware
    subgraph RDKMW["RDK Core Middleware"]
        OCDM["WPEFramework OCDM\n(OpenCDMi Plugin)"]
        PR["PlayReady OCDM\n(OpenCDMi Backend)"]
        Thunder["WPEFramework (Thunder)"]
    end

%% Vendor Layer
    subgraph VL["Vendor Layer"]
        PlayReadySDK["PlayReady SDK\n(SoC DRM Libraries)"]
        SvpGeneric["gst-svp-ext\n(Generic Interface)"]
        SvpHAL["gst-svp-ext\n(Platform HAL)"]
        SvpGeneric --> SvpHAL
    end

    subgraph Cloud["Cloud Services"]
        LicenseServer["License Server"]
    end

    Apps -->|"EME / OCDM API"| Thunder
    Thunder --> OCDM
    OCDM -->|"CDMi IMediaKeys"| PR
    PR -->|"Drm_* APIs\n(SoC DRM libs)"| PlayReadySDK
    PR -->|"svp_* APIs"| SvpGeneric
    PR -.->|"License Challenge / Response"| LicenseServer

Key Features & Responsibilities:

  • License Acquisition: Generates a PlayReady license challenge from the DRM header present in the content and delivers it to the caller for dispatch to the license server. Processes the license server response and binds the resulting keys to per-key decrypt contexts.
  • Content Decryption: Decrypts AES-CTR, AES-CBC, and AES-CBCS encrypted media samples using PlayReady opaque decrypt APIs, with subsample mapping support for mixed clear-and-encrypted content.
  • Secure Video Path Integration: Routes decrypted video samples through protected memory regions using the SVP HAL, preventing video content from passing through normally accessible memory after decryption.
  • Secure Stop: Tracks active playback sessions using the PlayReady Secure Stop mechanism and provides challenge generation and response processing APIs to allow a server to verify that playback has ended.
  • Output Protection Enforcement: Evaluates license-specified output protection levels for compressed and uncompressed digital video, analog video, and digital audio outputs through a policy callback, and enforces maximum resolution decode constraints received from the license server.
  • License Store Management: Maintains a persistent DRM store, performs cleanup of expired and removal-date licenses on initialization, supports store deletion, and provides a SHA-256 hash of the store for integrity verification.

Design

The component is structured around two layers: a system-level context managed by the PlayReady class in MediaSystem.cpp, and a per-session context managed by MediaKeySession in MediaSession.cpp and MediaSessionExt.cpp. The system layer initializes the PlayReady platform and maintains the shared DRM_APP_CONTEXT that sessions within the same instance share. The session layer manages individual key state machines, license challenge-response cycles, and decrypt context binding. This separation allows multiple concurrent sessions — such as those needed for multi-period content or adaptive bitrate streams with multiple key IDs — to share a single application context while maintaining independent key states.

PlayReady SDK calls that use the shared DRM_APP_CONTEXT are serialized through a global CriticalSection (drmAppContextMutex_) to ensure thread safety. Platform initialization is guarded separately by prPlatformMutex_ using a reference counter so that concurrent callers do not double-initialize. Session construction is protected by prSessionMutex_.

The component's northbound interface is the CDMi IMediaKeys and IMediaKeysExt API consumed by the WPEFramework OCDM Plugin (OpenCDMi) — the Thunder plugin responsible for discovering and loading OpenCDMi backend shared libraries and routing EME-layer requests to them. Its southbound interface covers two paths: PlayReady SDK calls for all DRM operations (directed to SoC-provided DRM libraries), and gst-svp-ext calls for SVP secure memory management — gst-svp-ext provides a generic interface where GStreamer SVP-specific platform handling is passed through to the underlying platform HAL. Configuration is delivered as a JSON string at Initialize() time, from which the DRM data directory, store path, and HOME environment variable are extracted.

The DRM store is persisted on the filesystem at the path specified by the store-location configuration parameter and is managed by the PlayReady SDK. The component performs a cleanup pass at startup to remove expired licenses. In-memory licenses are removed when the session closes. Temporary persistent licenses acquired during a session are tracked and deleted on session close to prevent unbounded accumulation.

graph LR

    OCDM["WPEFramework\nOCDM Plugin (OpenCDMi)"]

    subgraph Component["PlayReady OCDM (Playready.drm)"]
        subgraph SysL["System Layer"]
            SysCtx["DRM_APP_CONTEXT"]
            SecStop["Secure Stop"]
            StoreOps["Store Ops"]
        end
        Mutex["drmAppContextMutex_"]
        subgraph SessL["Session Layer"]
            KeySM["Key State Machine"]
            LicAcq["License Acq"]
            DecCtx["Decrypt Contexts"]
        end
    end

    PlayReadySDK["PlayReady SDK\n(SoC DRM Libraries)"]
    SvpGeneric["gst-svp-ext\n(Generic Interface)"]
    SvpHAL["gst-svp-ext\n(Platform HAL)"]
    SvpGeneric --> SvpHAL

    OCDM -->|"System APIs"| SysL
    OCDM -->|"Session APIs"| SessL
    SysL --> Mutex
    SessL --> Mutex
    Mutex --> PlayReadySDK
    SessL -->|"svp_* calls"| SvpGeneric

Threading Model

  • Threading Architecture: Multi-threaded
  • Main Thread: Handles IMediaKeys calls from the WPEFramework OCDM Plugin (OpenCDMi) — initialization, session creation, Secure Stop operations, and configuration.
  • Worker Threads:
  • Decrypt caller thread: Invokes Decrypt() on MediaKeySession; acquires drmAppContextMutex_ for the duration of each decrypt operation.
  • Synchronization:
  • drmAppContextMutex_ — global CriticalSection serializing PlayReady SDK calls that use the shared DRM_APP_CONTEXT from both system and session layers.
  • prPlatformMutex_CriticalSection with reference counting protecting Drm_Platform_Initialize and Drm_Platform_Uninitialize in CPRDrmPlatform.
  • prSessionMutex_CriticalSection protecting PlayreadySession::InitializeDRM during session-local context setup.
  • SafeCriticalSection — RAII wrapper for WPEFramework::Core::CriticalSection providing scoped lock with explicit unlock() and relock() methods, used throughout in preference to manual lock/unlock pairs.
  • Session Count Guard: m_sessionCount (in the PlayReady system class) tracks the number of live MediaKeySession instances. Both InitializeAppCtx() and UninitializeAppCtx() check this counter before resetting m_poAppContext, preventing a use-after-free where active sessions hold a raw alias of the app context pointer. m_isAppCtxInitialized is a complementary flag that prevents double-initialization.
  • Async / Event Dispatch: Key status updates and key messages are delivered synchronously to the registered IMediaKeySessionCallback during Update(), Run(), and error paths. All callbacks are invoked inline on the calling thread.

Platform and Integration Requirements

  • Build Dependencies: wpeframework, wpeframework-clientlibraries, wpeframework-tools-native, entservices-apis, gst-svp-ext, gstreamer1.0, OpenSSL. Platform-specific PlayReady library resolved via platform-playready-depends and platform-playready-flags Yocto variables.
  • Component Dependencies: The WPEFramework OCDM Plugin (OpenCDMi) must be active; this OpenCDMi backend is loaded by the OCDM Plugin (OpenCDMi) at startup.
  • SVP Integration: gst-svp-ext is used for secure memory allocation and token management during decryption. The gst-svp-ext generic interface delegates platform-specific SVP operations to the underlying platform HAL.
  • Systemd Services: When run under systemd, the component's stdout/stderr logging can be captured by journald (depending on the unit configuration).
  • Configuration Files: JSON configuration string delivered by the WPEFramework configuration system at component initialization, containing read-dir, store-location, and home-path fields.
  • Startup Order: The component is loaded by the WPEFramework OCDM Plugin (OpenCDMi). svpPlatformInitializePlayready() is called at the start of Initialize() before any DRM context setup.

Component State Flow

Initialization to Active State

The component is initialized when the WPEFramework OCDM Plugin (OpenCDMi) calls Initialize() on the system object. Platform-level PlayReady initialization is performed first (svpPlatformInitializePlayready), followed by JSON configuration parsing to extract the DRM data directory and store paths. The DRM path globals are set, directories are created, and the revocation buffer is allocated in CreateSystemExt(). The DRM application context is then initialized via Drm_Initialize(). If the store is found to be corrupt, it is deleted and initialization is retried automatically. After successful context setup, the revocation buffer is registered, the secure or anti-rollback clock is validated, and the revocation list is loaded. Finally, expired and removal-date licenses are removed from the store.

The component transitions through the following states during its lifecycle: Initializing (platform init, config parse) → SystemExtCreated (DRM path and revocation buffer allocated) → AppCtxInitialized (Drm_Initialize succeeded, revocation buffer registered, clock validated, revocation list loaded) → Active (serving CDMi calls and session creation) → Shutdown (store cleanup, Drm_Uninitialize, platform uninit).

sequenceDiagram
    participant OCDM as WPEFramework OCDM Plugin (OpenCDMi)
    participant PR as PlayReady OCDM (OpenCDMi Backend)
    participant SVP as gst-svp-ext (Generic)
    participant PRSDK as PlayReady SDK (SoC DRM)

    OCDM->>PR: Initialize(shell, configline)
    PR->>SVP: svpPlatformInitializePlayready()
    SVP-->>PR: Platform ready

    PR->>PR: OnSystemConfigurationAvailable(configline)
    PR->>PR: Parse JSON config (read-dir, store-location, home-path)
    PR->>SVP: svpGetDrmStoragePath()
    SVP-->>PR: Store path resolved

    PR->>PR: CreateSystemExt() — set DRM path globals, alloc revocation buffer

    PR->>PRSDK: Drm_Platform_Initialize(platformInitData)
    PRSDK-->>PR: Platform initialized

    PR->>SVP: svpGetDrmOEMContext()
    SVP-->>PR: OEM context

    PR->>PRSDK: Drm_Initialize(AppCtx, OemCtx, opaqueBuf, storeNameStr)
    PRSDK-->>PR: DRM_SUCCESS (or store corrupt → delete & retry)

    PR->>PRSDK: Drm_Revocation_SetBuffer(revocationBuf, size)
    PRSDK-->>PR: OK

    PR->>SVP: svpIsSecureClockInitNeed()
    SVP-->>PR: bool

    PR->>PRSDK: Drm_SecureTime_GetValue() / Drm_AntiRollBackClock_Init()
    PRSDK-->>PR: Clock validated

    PR->>SVP: svpLoadRevocationList()
    SVP-->>PR: Revocation list loaded

    PR->>PRSDK: Drm_StoreMgmt_CleanupStore(DELETE_EXPIRED | DELETE_REMOVAL_DATE)
    PRSDK-->>PR: Store cleaned

    PR-->>OCDM: Initialization complete — Component Active

    loop Runtime
        OCDM->>PR: CDMi API calls (sessions, decrypt, secure stop)
    end

    OCDM->>PR: Deinitialize()
    PR->>PRSDK: Drm_StoreMgmt_CleanupStore()
    PR->>PRSDK: Drm_Uninitialize()
    PR->>PRSDK: Drm_Platform_Uninitialize()
    PR->>SVP: svpPlatformUninitializePlayready()
    PR-->>OCDM: Deinitialized

Runtime State Changes

State Change Triggers:

  • A new MediaKeySession is created per content stream. Each session transitions independently through KEY_INITKEY_PENDINGKEY_READYKEY_CLOSED. Update() guards its entry with a state check (KEY_PENDING required).
  • If DRM_E_SECURESTORE_CORRUPT, DRM_E_SECURESTOP_STORE_CORRUPT, or DRM_E_DST_CORRUPTED are returned from Drm_Initialize, the store file is deleted and initialization is automatically retried once.
  • On Close(), in-memory licenses (tracked by batch ID) and any temporary persistent licenses acquired during the session are deleted from the store.

Context Switching Scenarios:

  • When the license server response contains persistent licenses, they are tracked per session and removed when the session closes, preventing accumulation in the store across playback sessions.
  • When Drm_Reader_Bind returns DRM_E_BUFFERTOOSMALL, the opaque buffer is doubled (up to 64× its initial size) and the bind operation is retried, allowing the session to adapt to license complexity without failing.

Call Flows

Initialization Call Flow

sequenceDiagram
    participant OCDM as OCDM (OpenCDMi)
    participant PR as PlayReady OCDM (OpenCDMi Backend)
    participant SVP as gst-svp-ext (Generic)
    participant PRSDK as PlayReady SDK (SoC DRM)

    OCDM->>PR: Initialize(shell, configJSON)
    PR->>SVP: svpPlatformInitializePlayready()
    PR->>PR: Parse config (read-dir, store-location, home-path)
    PR->>SVP: svpGetDrmStoragePath(readDir, storePath, storeLocation)
    PR->>PR: CreateSystemExt() — set g_dstrDrmPath, alloc revocation buffer
    PR->>PRSDK: Drm_Platform_Initialize(platformInitData)
    PR->>SVP: svpGetDrmOEMContext()
    PR->>PRSDK: Drm_Initialize(AppCtx, OemCtx, opaqueBuf, storeNameStr)
    PR->>PRSDK: Drm_Revocation_SetBuffer(revocationBuf, REVOCATION_BUFFER_SIZE)
    PR->>PRSDK: Drm_SecureTime_GetValue() / Drm_AntiRollBackClock_Init()
    PR->>SVP: svpLoadRevocationList()
    PR->>PRSDK: Drm_StoreMgmt_CleanupStore(DELETE_EXPIRED | DELETE_REMOVAL_DATE)
    PR-->>OCDM: Ready

Session License Acquisition Call Flow

sequenceDiagram
    participant App as Application / WPE Runtime
    participant OCDM as OCDM (OpenCDMi)
    participant PR as PlayReady OCDM (OpenCDMi Backend)
    participant PRSDK as PlayReady SDK (SoC DRM)
    participant LS as License Server

    App->>OCDM: createMediaKeySession(initData)
    OCDM->>PR: CreateMediaKeySession(keySystem, initData, cdmData, ...)
    PR->>PR: parsePlayreadyInitializationData() — extract DRM header from PSSH
    PR->>PRSDK: Drm_Content_SetProperty(DRM_CSP_AUTODETECT_HEADER, drmHeader)
    PR->>PRSDK: DRM_HDR_GetAttribute() — extract Key IDs and header version
    PR-->>OCDM: MediaKeySession created (KEY_INIT)

    OCDM->>PR: Run(callback)
    PR->>PRSDK: Drm_LicenseAcq_GenerateChallenge(rights, customData, ..., &challenge, &batchID)
    PR-->>OCDM: callback.OnKeyMessage(challenge, silentURL)
    OCDM-->>App: keyMessage event (KEY_PENDING)

    App->>LS: POST challenge to license server
    LS-->>App: License response

    App->>OCDM: update(licenseResponse)
    OCDM->>PR: Update(licenseResponse)
    PR->>PRSDK: Drm_LicenseAcq_ProcessResponse(response, &licenseResponse)
    loop Per key acknowledgement in response
        PR->>PRSDK: Drm_Content_SetProperty(DRM_CSP_DECRYPTION_OUTPUT_MODE, HANDLE)
        PR->>PRSDK: Drm_Reader_Bind(rights, _PolicyCallback, &decryptContext)
        PR->>PRSDK: Drm_Reader_Commit(_PolicyCallback)
    end
    PR-->>OCDM: callback.OnKeyStatusUpdate("KeyUsable", keyId)
    PR-->>OCDM: callback.OnKeyStatusesUpdated()
    OCDM-->>App: keystatuseschange event (KEY_READY)

Decrypt Call Flow

sequenceDiagram
    participant OCDM as OCDM (OpenCDMi)
    participant PR as PlayReady OCDM (OpenCDMi Backend)
    participant SVP as gst-svp-ext (Generic)
    participant PRSDK as PlayReady SDK (SoC DRM)

    OCDM->>PR: Decrypt(inData, sampleInfo, properties)
    PR->>PR: Resolve current decrypt context by Key ID from sampleInfo
    PR->>SVP: svp_allocate_secure_buffers(pSVPContext, secBufInfo, encData, encDataLen)
    PR->>SVP: svp_buffer_alloc_token() / svp_buffer_to_token()
    PR->>PRSDK: Drm_Reader_DecryptMultipleOpaque(decryptContext, ivVector, regionMapping, encData)
    PRSDK-->>PR: decryptedLength, pDecryptedContent (secure handle)
    PR->>SVP: Write secure token to output buffer header
    PR-->>OCDM: CDMi_SUCCESS — output buffer contains SVP token

Internal Modules

Module / ClassDescriptionKey Files
PlayReadyImplements IMediaKeys and IMediaKeysExt. Manages the system-level DRM application context (DRM_APP_CONTEXT), configuration parsing, platform initialization, Secure Stop session enumeration and challenge/response, license store cleanup and deletion, and store integrity hashing. Maintains m_sessionCount and m_isAppCtxInitialized to guard against use-after-free when resetting the app context while sessions are active. Receives JSON configuration from the WPEFramework host at startup.MediaSystem.cpp
MediaKeySessionImplements IMediaKeySession and IMediaKeySessionExt. Manages per-session key state, license challenge generation, license response processing, dual decrypt context binding (SVP video + optional non-SVP audio), sample decryption, output protection policy evaluation, and session teardown including license cleanup. Exposes printGuid() and printUuid() diagnostic helpers for key ID logging.MediaSession.cpp, MediaSessionExt.cpp, MediaSession.h
PlayreadySessionBase class for MediaKeySession. Owns a session-local DRM_APP_CONTEXT and manages reference-counted DrmPlatformInitialize / Drm_Initialize for sessions that do not share the system-level context.MediaSession.cpp, MediaSession.h
CPRDrmPlatformReference-counted wrapper for Drm_Platform_Initialize and Drm_Platform_Uninitialize. Ensures the PlayReady platform is initialized exactly once across multiple concurrent callers using prPlatformMutex_. Retries on DRM_E_DEPRECATED_DEVCERT_READ_ERROR up to DEVCERT_RETRY_MAX times with DEVCERT_WAIT_SECS delay between attempts.MediaSession.cpp
SafeCriticalSectionRAII wrapper for WPEFramework::Core::CriticalSection. Acquires the lock on construction and releases it on destruction. Provides explicit unlock() and relock() methods for cases where the lock must be temporarily dropped within a scope.MediaSession.h
KeyIdUtility class encapsulating a 16-byte DRM key identifier. Supports GUID little-endian and UUID big-endian byte orderings and toggling between them. Provides base64 and hex string representations for logging and protocol use.MediaSession.h, MediaSession.cpp
__DECRYPT_CONTEXTPOD struct holding a KeyId, a primary DRM_DECRYPT_CONTEXT (oDrmDecryptContext) for SVP-protected video decryption, and a secondary DRM_DECRYPT_CONTEXT (oDrmDecryptAudioContext) for non-SVP audio decryption when svpIsAudioNeedNonSVPContext() returns true. Both contexts are zero-initialised on construction.MediaSession.h

Component Interactions

The component's interactions are with the WPEFramework OCDM Plugin — OpenCDMi (northbound, in-process), the PlayReady SDK / SoC DRM libraries (southbound, in-process), and gst-svp-ext (generic interface delegating to the platform SVP HAL) for secure memory management.

Interaction Matrix

Target Component / LayerInteraction PurposeKey APIs / Topics
WPEFramework OCDM Plugin (OpenCDMi)

OCDM Plugin (OpenCDMi)CDMi interface entry points — discovers and loads this OpenCDMi backend, routes EME requests to itIMediaKeys::CreateMediaKeySession, IMediaKeysExt::InitSystemExt, IMediaKeysExt::TeardownSystemExt, IMediaKeysExt::GetSecureStop, IMediaKeysExt::CommitSecureStop
IMediaKeySessionCallbackSession event delivery to the OCDM callerOnKeyMessage, OnKeyStatusUpdate, OnKeyStatusesUpdated, OnError
PlayReady SDK


DRM platform and application context lifecycleDrm_Platform_Initialize, Drm_Platform_Uninitialize, Drm_Initialize, Drm_Uninitialize, Drm_Reinitialize

Content header parsing and key selectionDrm_Content_SetProperty with DRM_CSP_AUTODETECT_HEADER, DRM_CSP_SELECT_KID, DRM_CSP_DECRYPTION_OUTPUT_MODE

License acquisitionDrm_LicenseAcq_GenerateChallenge, Drm_LicenseAcq_ProcessResponse

Decrypt context binding and content decryptionDrm_Reader_Bind, Drm_Reader_Commit, Drm_Reader_Close, Drm_Reader_DecryptMultipleOpaque

Revocation data managementDrm_Revocation_SetBuffer

Secure time and anti-rollback clockDrm_SecureTime_GetValue, Drm_AntiRollBackClock_Init

Secure Stop session managementDrm_SecureStop_EnumerateSessions, Drm_SecureStop_GenerateChallenge, Drm_SecureStop_ProcessResponse

License store maintenanceDrm_StoreMgmt_CleanupStore, Drm_StoreMgmt_DeleteLicenses, Drm_StoreMgmt_DeleteInMemoryLicenses
gst-svp-ext (Generic Interface → Platform HAL)


Platform PlayReady initialization and teardownsvpPlatformInitializePlayready, svpPlatformUninitializePlayready

DRM and platform context provisioningsvpGetDrmOEMContext, svpGetDrmPlatformInitData

DRM storage path resolutionsvpGetDrmStoragePath

Revocation list loading and clock initialization flagsvpLoadRevocationList, svpIsSecureClockInitNeed

Secure buffer lifecycle for decrypted videosvp_allocate_secure_buffers, svp_release_secure_buffers, svp_buffer_alloc_token, svp_buffer_to_token, svp_buffer_free_token, svp_token_size

SVP context lifecyclegst_svp_ext_get_context, gst_svp_ext_free_context

SVP buffer header inspection and updategst_svp_has_header, gst_svp_header_get_start_of_data, gst_svp_header_get_field, gst_svp_header_set_field

Per-stream decrypt path capability queriessvpIsAudioNeedNonSVPContext, svpIsVideoResCheckNeed, svpIsDynamicSVPEncEnabled, svpIsMultipleOpaqueSupportCTR
External Systems

License ServerLicense challenge dispatch and response retrievalHTTP POST (the caller handles transport; the component generates the challenge binary and processes the response binary)

Events Published

Event NameCallback / APITrigger ConditionSubscriber Components
Key messageIMediaKeySessionCallback::OnKeyMessageLicense challenge successfully generated in playreadyGenerateKeyRequest()OCDM Plugin (OpenCDMi) → EME layer → application
Key status updateIMediaKeySessionCallback::OnKeyStatusUpdateLicense bound successfully (KeyUsable), output restriction (KeyOutputRestricted, KeyOutputRestrictedHDCP, KeyOutputRestrictedHDCP22), license expired (LicenseExpired), license not found (LicenseNotFound), or internal error (KeyInternalError)OCDM Plugin (OpenCDMi) → application
Key statuses updatedIMediaKeySessionCallback::OnKeyStatusesUpdatedCompletion of all key status updates within an Update() cycle or persistent license pre-checkOCDM Plugin (OpenCDMi)
ErrorIMediaKeySessionCallback::OnErrorDecrypt failure or license challenge generation failureOCDM Plugin (OpenCDMi) → application

IPC Flow Patterns

Primary Request / Response Flow:

The WPEFramework OCDM Plugin (OpenCDMi) dispatches CDMi API calls directly in-process to the component's C++ interface. The component then invokes PlayReady SDK APIs synchronously under the protection of drmAppContextMutex_.

sequenceDiagram
    participant App as Application
    participant OCDM as OCDM (OpenCDMi)
    participant PR as PlayReady OCDM (OpenCDMi Backend)
    participant PRSDK as PlayReady SDK (SoC DRM)

    App->>OCDM: EME API call
    OCDM->>PR: CDMi method call (in-process)
    PR->>PRSDK: Drm_* API call (under drmAppContextMutex_)
    PRSDK-->>PR: DRM_RESULT
    PR-->>OCDM: CDMi_RESULT
    OCDM-->>App: EME result / event

Event Notification Flow:

Key status events are posted synchronously from within the Update() and playreadyGenerateKeyRequest() call paths by invoking the registered IMediaKeySessionCallback directly on the calling thread.

sequenceDiagram
    participant PRSDK as PlayReady SDK (SoC DRM)
    participant PR as PlayReady OCDM (OpenCDMi Backend)
    participant CB as IMediaKeySessionCallback
    participant App as Application

    PRSDK-->>PR: Drm_LicenseAcq_ProcessResponse() result
    PR->>CB: OnKeyStatusUpdate("KeyUsable", keyId)
    PR->>CB: OnKeyStatusesUpdated()
    CB-->>App: keystatuseschange event

Implementation Details

SDK and Component API Reference

PlayReady SDK APIs

Called by playready-rdk directly on SoC-provided PlayReady DRM libraries (path: playready-rdk → SoC DRM libraries).

PlayReady SDK APIPurposeImplementation File
Drm_Platform_InitializeInitialize the PlayReady platform layer using platform-specific init dataMediaSession.cpp
Drm_Platform_UninitializeUninitialize the PlayReady platform layerMediaSession.cpp
Drm_InitializeInitialize the DRM application context with an opaque buffer and store pathMediaSystem.cpp, MediaSession.cpp
Drm_UninitializeRelease the DRM application contextMediaSystem.cpp, MediaSession.cpp
Drm_ReinitializeRe-initialize an existing DRM application context on session reuseMediaSession.cpp
Drm_Content_SetPropertySet content properties: auto-detect header, select KID, set decryption output modeMediaSystem.cpp, MediaSession.cpp, MediaSessionExt.cpp
Drm_LicenseAcq_GenerateChallengeGenerate a license acquisition challenge from the DRM headerMediaSession.cpp, MediaSessionExt.cpp
Drm_LicenseAcq_ProcessResponseProcess a license server response and store acquired licensesMediaSession.cpp
Drm_Reader_BindBind a decrypt context to a license for the specified key IDMediaSession.cpp, MediaSessionExt.cpp
Drm_Reader_CommitCommit the bound reader context and apply output protection policyMediaSession.cpp, MediaSessionExt.cpp
Drm_Reader_CloseRelease a decrypt contextMediaSession.cpp
Drm_Reader_DecryptMultipleOpaqueDecrypt a multi-region encrypted buffer supporting multiple IV valuesMediaSession.cpp
Drm_Revocation_SetBufferRegister the revocation data buffer with the application contextMediaSystem.cpp, MediaSession.cpp
Drm_SecureTime_GetValueRead the secure clock value and type from the application contextMediaSystem.cpp
Drm_AntiRollBackClock_InitInitialize the anti-rollback clock when the secure clock is unavailableMediaSystem.cpp
Drm_SecureStop_EnumerateSessionsList active Secure Stop session IDs from the storeMediaSystem.cpp
Drm_SecureStop_GenerateChallengeGenerate a Secure Stop challenge for a given session IDMediaSystem.cpp
Drm_SecureStop_ProcessResponseProcess a Secure Stop server responseMediaSystem.cpp
Drm_StoreMgmt_CleanupStoreRemove expired and removal-date licenses from the DRM storeMediaSystem.cpp
Drm_StoreMgmt_DeleteLicensesDelete a specific license identified by KID and LIDMediaSession.cpp
Drm_StoreMgmt_DeleteInMemoryLicensesDelete all in-memory licenses associated with a batch IDMediaSession.cpp

gst-svp-ext Component APIs

Called by playready-rdk on the gst-svp-ext generic interface. GStreamer SVP-specific platform handling is passed through by gst-svp-ext to the underlying platform HAL layer (path: playready-rdk → gst-svp-ext generic → gst-svp-ext platform HAL).

gst-svp-ext APIPurposeImplementation File
svpPlatformInitializePlayreadyPerform SVP-layer PlayReady platform initializationMediaSystem.cpp
svpPlatformUninitializePlayreadyPerform SVP-layer PlayReady platform teardownMediaSystem.cpp
svpGetDrmOEMContextRetrieve the OEM DRM context pointer for Drm_InitializeMediaSystem.cpp, MediaSession.cpp
svpGetDrmPlatformInitDataRetrieve platform-specific initialization data for Drm_Platform_InitializeMediaSession.cpp
svp_allocate_secure_buffersAllocate protected memory regions for decrypted video contentMediaSession.cpp
svp_release_secure_buffersRelease protected memory regions after useMediaSession.cpp
svp_buffer_alloc_token / svp_buffer_to_tokenConvert a secure buffer handle to an opaque token for downstream pipeline consumptionMediaSession.cpp

Key Implementation Logic

  • State / Lifecycle Management: MediaKeySession maintains a KeyState enum with values KEY_INIT, KEY_PENDING, KEY_READY, KEY_ERROR, and KEY_CLOSED. State transitions are driven by Run() (→ KEY_PENDING), Update() (→ KEY_READY or KEY_ERROR), and Close() (→ KEY_CLOSED). The entry guard ChkBOOL(m_eKeyState == KEY_PENDING) at the start of Update() prevents out-of-order license response processing.
  • Core implementation: MediaSession.cpp
  • State transition handlers: MediaSession.cpp (Run, Update, Close, playreadyGenerateKeyRequest)

  • Dual Decrypt Context Binding: Each __DECRYPT_CONTEXT holds two DRM_DECRYPT_CONTEXT fields. During Update(), BindKeyNow(), and SelectKeyId(), if svpIsAudioNeedNonSVPContext() returns true, a second ReaderBind is performed with OEM_TEE_DECRYPTION_MODE_NOT_SECURE to populate oDrmDecryptAudioContext. During Decrypt(), the audio context is selected when useSVP is false.

  • CBCS Pattern Handling: In the decrypt path, the encryptedRegionSkip vector (passed to Drm_Reader_DecryptMultipleOpaque as skip/pattern data) is populated conditionally: for AesCbc_Cbcs scheme the pattern is always pushed even when values are 0:0 (required for audio CBCS), whereas for all other schemes the pattern is only pushed when encrypted_blocks != 0.

  • Event Processing: Events are dispatched synchronously to IMediaKeySessionCallback from within Update(), playreadyGenerateKeyRequest(), and error paths. Key status strings ("KeyUsable", "KeyOutputRestricted", "KeyOutputRestrictedHDCP", "KeyOutputRestrictedHDCP22", "LicenseExpired", "LicenseNotFound", "KeyInternalError") are mapped from DRM_RESULT values through MapDrToKeyMessage() in MediaSession.cpp.

  • Error Handling Strategy: DRM_RESULT error codes are checked after every PlayReady SDK call. Non-fatal errors trigger retry logic:

  • DRM_E_BUFFERTOOSMALL in ReaderBind causes the opaque buffer to be doubled (up to 64× its initial size) before retrying.
  • DRM_E_BUFFERTOOSMALL in playreadyGenerateKeyRequest triggers a two-pass challenge generation to size the challenge buffer correctly.
  • DRM_E_NO_URL from Drm_LicenseAcq_GenerateChallenge causes a silent retry of the same call with the URL output parameters set to nullptr, allowing challenge generation to proceed for content that carries no embedded license URL.
  • DRM_E_LICACQ_TOO_MANY_LICENSES from Drm_LicenseAcq_ProcessResponse causes the acks array to be reallocated to the required size and the call to be retried once.
  • Fatal errors set m_eKeyState = KEY_ERROR and invoke OnError() and OnKeyStatusUpdate() on the callback. Decrypt failures are handled in DRM_DecryptFailure(), which reports a hex-encoded error code string.
  • Store corruption at Drm_Initialize (DRM_E_SECURESTORE_CORRUPT, DRM_E_SECURESTOP_STORE_CORRUPT, DRM_E_DST_CORRUPTED) triggers automatic store deletion and one retry.

  • Logging & Diagnostics: All diagnostics are emitted through the PR_LOG(level, fmt, ...) macro introduced in this codebase. The macro emits to stderr with the format [PlayReady][LEVEL][function:line] message. Five levels are defined: PR_LOG_ERROR (0), PR_LOG_WARN (1), PR_LOG_INFO (2), PR_LOG_DEBUG (3), PR_LOG_TRACE (4). The active log level is held in the global g_logLevel and initialized at startup by InitializeLogLevel(), which reads the PLAYREADY_RDK_LOG_LEVEL environment variable. If the variable is absent or out of range the level defaults to PR_LOG_DEBUG. When DRM_ERROR_NAME_SUPPORT is enabled at build time, human-readable error name strings are appended to every DRM result log via DRM_ERR_GetErrorNameFromCode() through the DRM_ERR_NAME(dr) macro. DRM header bytes are logged at PR_LOG_TRACE level as a base64 string via a local convertToBase64() helper in MediaSessionExt.cpp.


Configuration

Key Configuration Files

Configuration FilePurposeOverride Mechanism
JSON configuration string (WPEFramework host)Specifies the DRM data directory path, DRM store file path, and HOME path for the component processDelivered by the WPEFramework configuration system at Initialize() time

Key Configuration Parameters

ParameterTypeDefaultDescription
read-dirstringFilesystem path to the directory containing PlayReady data files (device certificate and related assets).
store-locationstringFilesystem path to the PlayReady DRM store file where licenses are persisted.
home-pathstringValue set as the HOME environment variable for the component process; required for Secure Stop functionality to operate correctly.

Runtime Configuration Parameters

ParameterTypeDefaultDescription
PLAYREADY_RDK_LOG_LEVELinteger3 (DEBUG)Environment variable read at Initialize() time by InitializeLogLevel(). Accepted range 0–4 maps to ERROR, WARN, INFO, DEBUG, TRACE. Values outside the range are silently ignored and the default is applied.

Build-Time Configuration Parameters

Build-Time Option / DefineDefaultDescription
USE_SVPOn (unconditional)Enables Secure Video Path integration via gst-svp-ext. Applied unconditionally across all build configurations.
DRM_ERROR_NAME_SUPPORTOffWhen enabled, appends human-readable DRM error name strings to all PR_LOG output via DRM_ERR_NAME(dr)DRM_ERR_GetErrorNameFromCode().
DRM_ANTI_ROLLBACK_CLOCK_SUPPORTOffWhen enabled, allows falling back to the anti-rollback clock when the secure clock is unavailable.
PLAYREADY_VERSION_4_6OffWhen enabled, uses the PlayReady 4.6 SDK version string global instead of the legacy global.
NO_PERSISTENT_LICENSE_CHECKOffWhen enabled, PersistentLicenseCheck() unconditionally returns failure, preventing key reuse from a prior session and always forcing a fresh license request.
TEE_CONFIG_NEEDOffWhen enabled, includes the TEE configuration header and calls OEM_OPTEE_SetHandle() in the decrypt path.
CLEAN_ON_INITOn (hardcoded)Guards the CleanLicenseStore() call inside InitSystemExt() via #ifdef, but is hardcoded to #define CLEAN_ON_INIT 1 in the same file and not exposed via CMake, so that path always executes. CleanLicenseStore() is also called unconditionally, with no macro guard at all, from TeardownSystemExt() and DeleteSecureStore().
ENABLE_AMBIGUOUS_FIXOffWhen enabled, suppresses the extern DRM_CONST_STRING g_dstrDrmPath declaration in MediaSystem.cpp, resolving build errors on platforms where the symbol is already visible in scope via a platform SDK header or prior definition.

Configuration Persistence

The DRM store file at the path specified by store-location persists license data across reboots and is managed by the PlayReady SDK. In-memory licenses and temporary persistent licenses acquired during a session are deleted from the store when that session is closed.

  • No labels