streaming video on safari: working around range request requirements

2021-03-15

 | 

~2 min read

 | 

367 words

When it comes to playing video on the web, we now have a <video> tag. This is really handy, however, browser implementations vary and Safari in particular expects the data to be streamed. Specifically, in order to use the <video> tag with Safari, the server needs to send a 206 response with a range request (indicating which bytes are being sent) and will reject a 200.

Our server was not configured to differentiate between requests and responded with a 200. From its perspective, everything was OK!

The suggested way to resolve this is reconfigure your server to support partial / range requests. LogRocket has a great blog post on the topic.

In our case, modifying the server to support playing video was more work than it was worth. (We landed on using the <video> tag in the first place because iPad’s don’t mute playing video like they do playing audio when the iPad is on “silent” mode. Discovering that “feature” is a story for another day.) So, we had to get creative.

The solution was to create a new URL to serve as the source for the video tag instead of pointing to it directly. So, we fetch the video initially as a file, then construct a URL. From the browser’s perspective, at this point, the video is just data and the browser won’t care what it is. The URL we create for it copies the data into memory and we can then use that URL as the media source.

<body>
    <video id='audio-only' style='display: none;'>
</body>
const audioElement = document.getElementById("audio-only")

fetch("./public/chime.mp4")
  .then((res) => res.blob())
  .then((blob) => (audioElement.src = URL.createObjectURL(blob)))

const playButton = document.getElementById("play-button")
playButton.addEventListener("click", play)

async function play() {
  await audioElement.play()
}

Here’s a Code Sandbox demonstrating the concept:

Now, it’s worth pointing out that there are advantages to actually following Safari’s recommended approach - specifically, you download fewer bits that might be unused at a time (though it does make buffering ahead harder). So, range requests are more efficient (generally), but if your server’s not set up to handle them, and you need a work around, try this approach of downloading the data completely, and transforming the blob on your own.


Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!