Abe Estrada

Video.js + FairPlay

No soy experto en DRM, es por eso que me parece interesante el problema con el que me topé mientras intentaba ver un video protegido con el cual contaba permiso, pero no tenía idea de como obtener la licencia del servidor para que el reproductor desencriptara el video y poderlo reproducir en el navegador.

Para mis pruebas, utilicé Video.js con el plugin videojs-contrib-eme para poder interactuar con servidores DRM.

En un mundo ideal, el código necesario sería el siguiente:

const keySystems = {
  "com.apple.fps.1_0": {
    certificateUri: "<CERTIFICATE_URL>",
    licenseUri: "<LICENSE_URL>",
  },
};

Pero en este caso, el servidor necesita que el Server Playback Context (SPC) sea enviado como un parámetro y codificado en base64. Por lo cual es necesario utilizar la opción getLicense que permite una función para personalizar la forma en que se conecta con el servidor y los parámetros que son enviados. En este ejemplo también se añade el encabezado Authorization.

En el siguiente código, dejo algunos comentarios de las partes clave de esta transacción del reproductor con el servidor:

player.eme();

const keySystems = {
  "com.widevine.alpha": {
    url: "https://<DRM_SERVER>/WideVine/",
    licenseHeaders: {
      Authorization: `Bearer ${token}`,
    },
  },
  "com.microsoft.playready": {
    url: "https://<DRM_SERVER>/PlayReady/",
    licenseHeaders: {
      Authorization: `Bearer ${token}`,
    },
  },
  "com.apple.fps.1_0": {
    certificateUri: "https://<CERTIFICATE_SERVER>/fairplay.der",
    getLicense: (emeOptions, contentId, keyMessage, callback) => {
      // Convert payload to base64
      const spc = encodeURI(btoa(String.fromCharCode(...keyMessage)));
      fetch("skd://<DRM_SERVER>".replace("skd://", "https://"), {
        method: "POST",
        headers: {
          Authorization: `Bearer ${token}`,
          "Content-Type": "application/x-www-form-urlencoded",
        },
        body: `spc=${spc}`,
      })
        .then((res) => res.text())
        .then((body) => {
          // Remove the <ckc> and </ckc> tags
          const b64key = body.trim().slice(5, -6);
          // Decode the base64 encoded data
          const key = new Uint8Array(
            atob(b64key)
              .split("")
              .map((c) => c.charCodeAt(0))
          );
          // Send the key to the player
          callback(null, key);
        })
        .catch((err) => callback(err));
    },
  },
};

player.src([
  { src: "https://<SERVER>/dash.url", type: "application/dash+xml", keySystems },
  { src: "https://<SERVER>/hls.m3u8", type: "application/x-mpegUrl", keySystems },
]);

Fuentes:

Relacionados: