Foxws
Laravel Streamer

Aes Encryption

Encryption

withAESEncryption() generates a random 128-bit key, tells Shaka Streamer to encrypt every stream with it, and writes the key to a file that's uploaded with the segments.

use Foxws\Streamer\Facades\Streamer;
use Foxws\Streamer\Support\ProtectionScheme;

$streamer = Streamer::fromDisk('media')
    ->open('videos/clip.mp4')
    ->addVideoStream('videos/clip.mp4', 'video.mp4')
    ->addAudioStream('videos/clip.mp4', 'audio.mp4')
    ->withMpdOutput('index.mpd')
    ->withHlsMasterPlaylist('master.m3u8');

$key = $streamer->withAESEncryption('key', ProtectionScheme::Cbcs);

$streamer->export()->toDisk('s3')->toPath('streams/clip/')->save();

$video->update([
    'encryption_key' => $key->key,       // 32 hex characters
    'encryption_key_id' => $key->keyId,  // 32 hex characters
]);

withAESEncryption() returns the key, not the streamer, so it can't sit in the middle of a chain. Call it on its own line.

Arguments

withAESEncryption(string $keyFilename = 'key', ProtectionScheme|string|null $protectionScheme = null, ?string $label = null): EncryptionKey
  • $keyFilename is the name of the key file that's uploaded with the segments, and the URI written into HLS playlists (#EXT-X-KEY).
  • $protectionScheme is cenc or cbcs, as a string or a ProtectionScheme case. Leave it null to use Shaka Streamer's default, cenc. cbc1 and cens throw an InvalidStreamConfigurationException, because Shaka Streamer doesn't support them.
  • $label is the key label. You only need it when streams use different keys.

The returned EncryptionKey has key and keyId (both hex) and filePath, the local path of the key file.

The first seconds of a video are encrypted too: the package sets clear_lead to 0, where Shaka Streamer's default is 10.

Choosing a protection scheme

Scheme Plays on
cbcs Safari and Apple devices, and recent Chrome, Firefox and Edge. The best choice when you serve both HLS and DASH.
cenc Chrome, Firefox, Edge and Android. Not Safari's native HLS player.

Where the key goes

The key file is written to cache_files_root (by default /dev/shm, a RAM disk), not next to the segments. save() uploads it to the same folder as the segments, then deletes the local copy.

The key file is as sensitive as the video. Keep the bucket private, and only hand out key URLs to users who may watch:

  • HLS: sign the key URL with setKeyUrlResolver() on the dynamic playlist, or point hls_key_uri at your own route that checks access and returns the key.
  • DASH: there's no key URL. Give the player the key yourself. In Shaka Player that's a ClearKey setting:
player.configure({
    drm: {
        clearKeys: { [keyId]: key },
    },
});

Store $key->key and $key->keyId so you can serve the key later without reading the file.

Key rotation

Shaka Streamer has no key rotation setting, so withKeyRotationDuration() throws an InvalidStreamConfigurationException. If you need key rotation, package with Laravel Shaka instead.

Full control

To use your own key, or Widevine, skip withAESEncryption() and pass Shaka Streamer's encryption config directly:

->withEncryption([
    'enable' => true,
    'encryption_mode' => 'raw',
    'protection_scheme' => 'cbcs',
    'clear_lead' => 0,
    'hls_key_uri' => 'https://example.com/keys/clip',
    'keys' => [
        ['key_id' => $keyId, 'key' => $key],
    ],
])

In that case you write and store the key yourself.