Digital Rights Management (DRM) protects content by enforcing access controls. Google Widevine secures video and audio content. Widevine operates using a modular architecture consisting of a license server, client-side components, and encryption protocols.

Widevine supports 3 security levels: L1, L2, and L3. L1 has the highest security with hardware-backed key storage, while L3 relies on software-only protection. The Content Decryption Module (CDM) handles decryption on the client side to integrate with media players to enforce license policies.

Widevine Encryption and License Flow

Widevine encrypts content using AES-128 or AES-256 with the Common Encryption Scheme (CENC). The encryption process generates a key ID and content ID, which the license server uses to issue decryption keys.

  1. The client requests a license from the Widevine license server.
  2. The server validates the device’s security level and returns a signed license.
  3. The CDM decrypts the content using the key from the license.

code
const licenseRequest = {
payload: widevinePayload,
drm: "widevine",
sessionId: "abcd1234",
contentType: "application/octet-stream"
};

player.requestLicense(licenseRequest, (license) => {
player.setLicense(license);
});

Explanation:

  • widevinePayload: Contains the KID and other metadata for the license request.
  • drm: "widevine": Specifies the DRM system being used.
  • sessionId: Unique identifier for the playback session.
  • contentType: Defines the MIME type for the license response.

Widevine in Mobile Video Development

Mobile developers integrate Widevine for secure video playback on Android and iOS. The process involves configuring the media player, handling DRM sessions, and managing offline playback.

Android MediaDrm Integration

Android’s MediaDrm API provides access to Widevine DRM capabilities. Developers initialize MediaDrm, create a DRM session, acquire a license, process the license response, and integrate with the MediaCrypto object for decryption.

Step 1: Initialize MediaDrm

code
// Widevine UUID
val WIDEVINE_UUID = UUID("EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED")

// Create MediaDrm instance
val mediaDrm = MediaDrm(WIDEVINE_UUID).apply {
// Set security level preference (optional)
setPropertyString("securityLevel", "L1")
}

Explanation:

  • WIDEVINE_UUID: The UUID used to identify the Widevine DRM scheme.
  • mediaDrm: The MediaDrm instance created to handle DRM operations with Widevine.
  • setPropertyString("securityLevel", "L1"): Sets the preferred security level for DRM content playback to Level 1 (hardware-backed security).

Step 2: Create DRM Session

code
// Open session
val sessionId = mediaDrm.openSession()

// Generate license request
val keyRequest = mediaDrm.getKeyRequest(
sessionId,
initData, // From PSSH box in media
"video/mp4",
MediaDrm.KEY_TYPE_STREAMING,
optionalParameters // Can be null
)

Explanation:

  • sessionId: The identifier for the opened DRM session used to manage keys.
  • mediaDrm.getKeyRequest(...): Creates a license request for the given session, media type, and parameters.

Step 3: Fetch License

code
// Convert keyRequest data to byte array
val licenseRequest = keyRequest.data

// Send to license server (using OkHttp)
val client = OkHttpClient()
val request = Request.Builder()
.url("https://your-license-server.com")
.post(licenseRequest.toRequestBody())
.build()

val response = client.newCall(request).execute()
val license = response.body?.bytes() ?: throw Exception("License fetch failed")

Step 4: Process License Response

code
// Provide license to MediaDrm
val keyResponse = mediaDrm.provideKeyResponse(sessionId, license)

// Create MediaCrypto for decryption
val mediaCrypto = MediaCrypto(WIDEVINE_UUID, sessionId)

Explanation:

  • mediaCrypto: The MediaCrypto instance is created to handle the decryption of protected media.
  • MediaCrypto(WIDEVINE_UUID, sessionId): Initializes MediaCrypto with the Widevine UUID and the active DRM session ID.

Step 5: Integrate with ExoPlayer

code
val drmSessionManager = DefaultDrmSessionManager.Builder()
.setUuidAndExoMediaDrmProvider(WIDEVINE_UUID, ExoMediaDrm.AppManagedProvider(mediaDrm))
.build()

val player = ExoPlayer.Builder(context)
.setDrmSessionManager(drmSessionManager)
.build()

Explanation:

  • setUuidAndExoMediaDrmProvider(WIDEVINE_UUID, ExoMediaDrm.AppManagedProvider(mediaDrm)): Sets the Widevine UUID and specifies the MediaDrm provider for DRM handling.
  • ExoPlayer.Builder(context): Creates a new ExoPlayer builder with the given Android context.

iOS and FairPlay Integration

While iOS uses FairPlay for DRM, cross-platform solutions support Widevine on hybrid apps. Developers rely on Encrypted Media Extensions (EME) for web-based playback:

Step 1: Enroll in the Apple Developer Program to get the FairPlay Streaming certificate and set up a license server for user authentication and key delivery.

Step 2: Encrypt your video using the FairPlay SDK or other providers by encoding it into HLS chunks. FairPlay uses it to protect content at the segment level.

Step 3: In your iOS app, use AVPlayer to play FairPlay-protected HLS streams; the app requests a license from your server to get the decryption keys.

Example Code (iOS - AVPlayer with FairPlay):

code
import AVFoundation

let playerItem = AVPlayerItem(url: videoURL)
let asset = AVAsset(url: videoURL)

asset.resourceLoader.setDelegate(self, queue: DispatchQueue.main)
let player = AVPlayer(playerItem: playerItem)

player.play()

Explanation:

  • playerItem: An AVPlayerItem initialized with the video URL, representing the media to be played.
  • asset: An AVAsset created from the video URL, representing the underlying media resource.
  • asset.resourceLoader.setDelegate(self, queue: DispatchQueue.main): Sets the resource loader delegate to handle custom loading requests on the queue.

Step 4: When the app receives the FairPlay content, it will request your FairPlay Streaming License Server. The license server will respond with a decryption key and license restrictions.

License Request Flow:

  • The iOS app requests a license from your server.
  • The server authenticates the user and returns a FairPlay license.
  • The app uses the decryption keys to decrypt the content for playback.

Offline Playback and Persistent Licenses

Widevine supports offline playback using licenses stored on the device. The license specifies an expiration time and playback restrictions. Android’s OfflineLicenseHelper simplifies this process:

code
val helper = OfflineLicenseHelper(
MediaDrm(UUID("EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED")),
DefaultHttpDataSourceFactory("user-agent")
)

val license = helper.downloadLicense(initData)

Explanation:

  • DefaultHttpDataSourceFactory: Handles HTTP communication with the license server.
  • downloadLicense: Fetches and stores the license for offline use.

Widevine Security Levels and Device Compatibility

Widevine has 3 security levels that govern how DRM-protected content is decrypted and rendered on a device. They affect the playback quality and the content protection.

Security LevelKey StoragePerformanceUse Case
L1HardwareHighPremium 4K/HDR
L2MixedModerateHD Streaming
L3 SoftwareLowerSD Content