Forensic Watermarking DoveRunner
Introduction
Qencode offers built-in integration with multiple DRM providers including BuyDRM, EZDRM, ExpressPlay, and DoveRunner. Through this integration, Qencode can encrypt video streams and deliver them with DRM protections that DoveRunner will license at playback time.
Prerequisites
- Qencode
- Project API key - used to obtain a session token (/v1/access_token) and run jobs.
- DoveRunner (formerly PallyCon)
- Site ID, Site Key, Access Key
- Content ID (CID / content_id) (the identifier you’ll use when generating license tokens)
1Get encryption material from DoveRunner (CPIX)
DoveRunner supports integration with third-party encoders/transcoders/packagers using CPIX-based key exchange (exact request/response details depend on their CPIX guide and your account setup).
What you need to end up with (for Widevine via CENC):
- key_id (KID)
- key (content key)
- pssh (Widevine PSSH)
2Encrypt during transcoding in Qencode (cenc_drm)
In your Qencode job JSON, you’ll:
- choose a packaging output such as DASH (commonly advanced_dash)
- add a cenc_drm block inside the same format item
- provide key_id, key, pssh
Example Qencode job (DASH + Widevine CENC)
{
"query": {
"source": "https://example.com/source.mp4",
"format": [
{
"output": "advanced_dash",
"destination": {
"url": "s3://YOUR_BUCKET/path/to/output/",
"key": "YOUR_STORAGE_KEY",
"secret": "YOUR_STORAGE_SECRET"
},
"stream": [
{
"video_codec": "libx264",
"audio_codec": "aac",
"width": 1280,
"height": 720,
"bitrate": 3000,
"audio_bitrate": 128,
"keyframe": 48
}
],
"cenc_drm": {
"key_id": "YOUR_KID_HEX_NO_DASHES",
"key": "YOUR_CONTENT_KEY_HEX",
"pssh": "YOUR_WIDEVINE_PSSH_BASE64"
}
}
]
}
}
Common Qencode-side gotchas
- key_id: often must be hex without dashes (if you have UUID form, normalize it).
- key: must be hex and match the KID used to build the PSSH.
- pssh: must be Widevine PSSH for Widevine playback tests (not PlayReady/FairPlay).
3Generate DoveRunner license token (server-side)
This part is not Qencode API - it’s your application backend that prepares playback authorization for DoveRunner.
Use your Python snippet to generate a token. Treat it as a backend-only action.
Python example (your provided code)
from pallycon import PallyConClient
# Initialise the client
client = PallyConClient(
site_id="",
site_key="",
access_key="",
drm_type=PallyConClient.DrmType.WIDEVINE.value,
user_id="",
content_id="your-content-id",
license_rule={
"policy_version": 2,
"playback_policy": {"persistent": False}
},
)
# Get the license token
license_token = client.license_token
print(license_token)
Recommended backend pattern
Create an endpoint like:
- GET /api/drm/token?content_id=...
It should:
- Authenticate the viewer (session/JWT).
- Check entitlement (is this user allowed this content?).
- Generate and return
{ "licenseToken": "..." }.
Never generate tokens in the browser/app - keep site_key and access_key private.
4Configure playback to request the license from DoveRunner
At playback time, your player needs:
- Manifest URL (your Qencode output .mpd)
- DoveRunner Widevine license server URL
- License token from your backend
How you attach the token depends on the player:
- some players send it as a custom header
- others expect it as request body or query param
- DoveRunner SDK guides typically define the exact method
Generic player flow
- Load MPD (encrypted segments).
- Player detects DRM init data (PSSH).
- Player issues a license request to DoveRunner license server.
- Request includes your license token.
- License returned → playback starts.