DRM Downloading
Download protected HLS/DASH streams for offline playback. At a minimum you need: encrypted media, a license server that issues offline (persistent) licenses, and a short‑lived token from your backend.
Prerequisites
Your DRM provider must support persistent (offline) licenses. Without persistent/offline license support, offline downloads will not work.
- DRM vendor account or your own Widevine/FairPlay license server
- Packaged encrypted HLS/DASH e.g., with Shaka Packager
- Backend entitlement/token endpoint (recommended)
- SDK installed and registered
You can quickly verify your client setup using our Free DRM Token Generator for Video before wiring your own entitlement backend.
High-level Flow
- Package and encrypt your content (HLS/DASH, CMAF/CENC).
- Obtain license server URLs (Widevine/FairPlay) and the FairPlay certificate.
- Implement an entitlement endpoint that returns a short-lived token.
- On the client, fetch token and call
downloadStream
withdrm
config.
Entitlement endpoint (overview)
For production, your app should request a short‑lived token from your backend and include it in DRM license requests. You can set up a simple endpoint (for example, with an Express server) that validates user entitlement and returns a signed token required by your DRM provider. Keep vendor secrets on the server and issue tokens scoped to user and asset with short expirations.
Client Usage (React Native)
- Test immediately with our free DRM token generator: Free DRM Token Generator for Video
import React from "react";
import { Platform } from "react-native";
import {
downloadStream,
registerPlugin,
} from "@TheWidlarzGroup/react-native-video-stream-downloader";
const API_KEY = "YOUR_SDK_API_KEY";
async function getDrmToken(assetId: string) {
const resp = await fetch("https://your-backend.example.com/drm/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId: "CURRENT_USER_ID", assetId }),
});
if (!resp.ok) throw new Error("Failed to get DRM token");
const json = await resp.json();
return json.token as string;
}
export async function downloadDrmExample() {
await registerPlugin(API_KEY);
const platform = Platform.OS as "ios" | "android";
const licenseServer =
platform === "ios"
? "https://your-fairplay-license-server.com/license"
: "https://your-widevine-license-server.com/license";
const drmConfig: {
licenseServer?: string;
certificateUrl?: string;
headers?: Record<string, string>;
getLicense?: (
spcString: string,
contentId: string,
licenseUrl: string,
loadedLicenseUrl: string
) => Promise<string> | string;
} = {
licenseServer,
certificateUrl:
platform === "ios"
? "https://your-fairplay-certificate.com/certificate"
: undefined,
headers: { "x-drm-usertoken": await getDrmToken("asset-1234") },
};
await downloadStream("https://example.com/drm-video.m3u8", {
drm: drmConfig,
});
}
For a complete, runnable DRM example, see the Examples page.
Custom License Acquisition (iOS Only)
Instead of using licenseServer
, you can manually acquire the license using the getLicense
function. This gives you full control over the license request process:
const drmConfig = {
certificateUrl: "https://your-fairplay-certificate.com/certificate",
getLicense: (spcString, contentId, licenseUrl, loadedLicenseUrl) => {
const base64spc = Base64.encode(spcString);
const formData = new FormData();
formData.append("spc", base64spc);
return fetch(`https://license.pallycon.com/ri/licenseManager.do`, {
method: "POST",
headers: {
"pallycon-customdata-v2": "your-custom-header",
"Content-Type": "application/x-www-form-urlencoded",
},
body: formData,
})
.then((response) => response.text())
.then((response) => response)
.catch((error) => {
console.error("License acquisition error:", error);
throw error;
});
},
};
getLicense Parameters
spcString
: The SPC (Server Playback Context) used for DRM validationcontentId
: The content ID from the DRM object orloadingRequest.request.url?.host
licenseUrl
: The URL passed in the DRM objectloadedLicenseUrl
: The URL retrieved fromloadingRequest.request.URL.absoluteString
, starting withskd://
orclearkey://
You should return a Base64-encoded CKC response, either directly or as a Promise
.
Testing
Prefer real devices over emulators/simulators for DRM and offline license flows. Virtual devices often lack proper DRM components and may not behave reliably.
FairPlay Notes (iOS)
DRM configuration in this SDK mirrors the approach used by react-native-video
(1:1 style props for license server, certificate, headers). Platform specifics are handled internally; use real devices for reliable testing.
License Expiration & Renewal
- Use short-lived entitlement tokens; include asset and user context.
- Track
expiresAt
in your app; prompt renewal when near expiry.