hometools

Create an HLS Encrypted Video with ffmpeg

April 2026

I'm playing around with videos for some project ideas. I'm looking into encrypting the videos to try to keep them from being slurped up by bots. One thing I looked at is using HLS videos with encryption.

I don't think it's going to do what I want. The process involves setting up a playlist that's encrypted and then pointing to a key to decrypt them. The problem is that path to the key is in the playlist. I expect bots will just pick it right up.

Regardless, I do like the HLS approach (minus the encryption). It lets you split a video into multiple files and then the browser stitches them together. That provides a better experience when skipping around a video because later chunks can be loaded if they get scrubbed to prior to the full video being downloaded.

Here's the ffmpeg process I'm using to make prep the videos (including the encryption):

#!/bin/bash

INPUT_DIR="input"
KEY_NAME="hls-sample.key"
KEY_PATH="$INPUT_DIR/$KEY_NAME"
KEY_INFO="$INPUT_DIR/hls-sample.txt"
OUTPUT_DIR="output"
VIDEO_NAME="bird.mp4"

[ ! -e "$KEY_PATH" ] && openssl rand -out "$KEY_PATH" 16

echo "https://al9000.com/ffmpeg/create-an-hls-encrypted-video/$KEY_NAME" > "$KEY_INFO"
echo "$KEY_NAME" >> "$KEY_INFO"
openssl rand -hex 16 >> "$KEY_INFO"

ffmpeg -y \
  -i "$INPUT_DIR/$VIDEO_NAME" \
  -c:v libx264 \
  -c:a aac \
  -hls_segment_type mpegts \
  -hls_list_size 0 \
  "$OUTPUT_DIR/bird.m3u8"

Some browsers support playing HLS files natively. Firefox does not. I'm using the hls.js library to handle that. It uses the HLS files in browsers that support it and transmuxes a version for browsers that don't.

The code looks like this:

<script src="/scripts/hlsjs-1.6.15.off/hls.min.js"></script>

<video id="birdVideo" controls></video>

<script>
  var video = document.getElementById('birdVideo');
  var videoSrc = "/ffmpeg/create-an-hls-encrypted-video/output/bird.m3u8";
  if (video.canPlayType('application/vnd.apple.mpegurl')) {
    video.src = videoSrc;
  } else if (Hls.isSupported()) {
    var hls = new Hls();
    hls.loadSource(videoSrc);
    hls.attachMedia(video);
  }
</script>
And, here's the output:

I was investigating VideoJS. It looks awesome. It doesn't quite work at the moment, though. They're in the middle of a big rewrite and I couldn't get HLS working. I expect that'll be addresses as the continue working.

I've done some looking at using addSourceBuffer() for the encryption stuff. I can make that work, but the method doesn't have full browser support. Notably, it's missing iPhone support. I think I can work without it, it just won't be as smooth.

-a

References