Skip to content

Set up a DRM player

This guide shows how your player gets a license from the MEGOGO license server for each DRM system: Widevine, FairPlay and PlayReady. Read DRM first: it explains which DRM system your key uses.

Before you start

Make sure that:

  • MEGOGO set the DRM system of your key for each platform. See DRM.
  • You can get a stream: you did Play a video.

How the player gets a license

  1. Your application sends /stream and gets src, drm_type, license_server and, for FairPlay, license_certificate.
  2. The player loads the manifest from src and finds that the video is encrypted.
  3. The player sends a license request to license_server.
  4. The license server sends the license. The player decrypts and plays the video.

Use license_server exactly as you get it. Do not change it and do not build it yourself. It contains the signed path of the stream in the ref parameter: this is the only authorization. You do not need headers or tokens.

WARNING

license_server is for this user and this playback only, as src. Request a new /stream each time the user starts the playback.

Widevine (drm_type: modular)

Platforms: web (Chrome, Firefox, Edge), Android, Android TV, some Smart TV models. Stream: DASH.

Request Body Response
POST to license_server The license challenge of the player (binary, as the player makes it) The license (binary, application/octet-stream)

Most players send a service-certificate request to the same URL first. The license server answers it. You do not need to get a certificate yourself.

Web: Shaka Player

const player = new shaka.Player();
await player.attach(document.querySelector("video"));
player.configure({
  drm: { servers: { "com.widevine.alpha": stream.license_server } },
});
await player.load(stream.src);

stream is the data object of the /stream response.

Android: Media3 ExoPlayer

val mediaItem = MediaItem.Builder()
    .setUri(stream.src)
    .setDrmConfiguration(
        MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
            .setLicenseUri(stream.licenseServer)
            .build()
    )
    .build()
player.setMediaItem(mediaItem)
player.prepare()

FairPlay (drm_type: fairplay)

Platforms: iOS, iPadOS, tvOS, Safari on macOS. Stream: HLS.

Step What to do
Certificate Get the application certificate from license_certificate (binary, DER)
Content ID Take the text after skd:// in the key URI of the playlist (#EXT-X-KEY or #EXT-X-SESSION-KEY). It is a 32-character hexadecimal key ID
Request POST the SPC (binary) to license_server with Content-Type: application/octet-stream
Response The CKC (binary)

The playlist can have more than one key, for example for audio, SD and Full HD. The player sends one request for each key.

iOS and tvOS: AVContentKeySession

func contentKeySession(_ session: AVContentKeySession,
                       didProvide keyRequest: AVContentKeyRequest) {
    // keyRequest.identifier is the key URI: "skd://<key ID>"
    guard let uri = keyRequest.identifier as? String,
          let contentId = uri.replacingOccurrences(of: "skd://", with: "").data(using: .utf8)
    else { return }
    // certificate: the bytes from license_certificate
    keyRequest.makeStreamingContentKeyRequestData(forApp: certificate,
                                                  contentIdentifier: contentId,
                                                  options: nil) { spc, error in
        guard let spc = spc else { keyRequest.processContentKeyResponseError(error!); return }
        var request = URLRequest(url: licenseServer)  // license_server
        request.httpMethod = "POST"
        request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
        request.httpBody = spc
        URLSession.shared.dataTask(with: request) { ckc, _, error in
            guard let ckc = ckc else { keyRequest.processContentKeyResponseError(error!); return }
            keyRequest.processContentKeyResponse(
                AVContentKeyResponse(fairPlayStreamingKeyResponseData: ckc))
        }.resume()
    }
}

Safari

Use the native HLS playback of Safari (the video element) with the FairPlay support of the browser. FairPlay through the hls.js library does not work now.

Errors

If the license server cannot read the request, it gives HTTP 400 with an empty body. The X-Error header tells the cause:

X-Error Cause
Invalid SPC data size The SPC is empty or too short
Invalid SPC certificate The SPC was made with another certificate. Use license_certificate
Invalid asset ID The content ID in the SPC is not a key of this video. Use the key ID from skd://

PlayReady (drm_type: playready)

Platforms: Smart TV (Samsung Tizen, LG webOS and others), Xbox, PlayStation, Microsoft Edge. Stream: DASH or MSS, as set on your key.

Request Body Response
POST to license_server The standard PlayReady license challenge (SOAP) of the player The PlayReady license response

Set license_server as the license URL of your player. Do not add custom data: the ref in the URL authorizes the request.

NOTE

On Samsung Tizen, the Full HD and 4K versions with PlayReady SL3000 play in the HTML5 video element. Use it, not the native Tizen player.

Device security for each quality

The quality that a device can play depends on its DRM security level and on HDCP of the screen connection:

Quality Widevine FairPlay PlayReady
SD, HD (up to 720) L3 (software) is enough No extra rule No extra rule
Full HD (1080) L1 (hardware) and HDCP 2.x No extra rule SL3000 and HDCP 2.2
4K (UHD) L1 and HDCP 2.2 HDCP 2.2 SL3000 and HDCP 2.2

If the device does not meet the rules, the player gets no keys for the higher qualities. The rights holders can set stricter rules for some videos and regions.

License server errors

HTTP status Cause Action
403 The ref is not correct or not valid any more Send /stream again and use the new license_server
400 (FairPlay) The SPC is not correct. See X-Error See FairPlay errors
500 (Widevine) The body is not a Widevine license challenge Send the challenge of the player without changes

For all error codes, see the Error catalog.

Next steps


Guide version 1.5, 2026-10-31 · Report a problem on this page