Streaming Video with HTTP Live Streaming (HLS)

16 Mar 2023

This post is about how to stream video from a webserver that isn’t technically a “video streaming server”. As it turns out, for “modern” video formats and browsers, if you just want to display a short video in your web page, you might be able to just link to the .mp4 and that will be good enough for most people. However, there are still cases where a stream or an adaptive stream is a better solution.

Using a Video Tag

For adding video to any web page, regardless of the source, HTML provides a video tag. It is important to give the web page a width and height for the video so that the rest of the page continues to render if any issues happen with the video.

<video src="<path_to_your_video>" height="640" width="360" controls type="application/x-mpegURL">
</video>

If you are using Jekyll/Markdown, don’t forget you can add in HTML directly into your blog post with a little bit of liquid. So the HTML snippet above might start out as:

<video src="{{"<path_to_your_video>" | absolute_url}}" height ="640" width="360" controls type="application/x-mpegURL">
</video>

Omitting the controls key will display the video without any pause/play or other controls. The user will have to know to right-click or option-click on the placeholder image to show them. You can also add an autoplay option that will start the video as soon as the page loads.

Online tutorials might say you need to use a javascript library, or fancy player, but try with just a simple video tag first, to see if that will meet your needs. Why add completixty and dependencies if you don’t need them?

Rendering a Stream

The fox video below is rendering from an .m3u8 source. It began as a .mov file, but then I chunked it into a series of ten second .ts files and use the .m3u8 playlist file to tell the browser how to stitch them back together. An .m3u8 file is actually a text/xml file listing each of the chunks and when they should appear in the stream. This results in video playback that appears continuous to the user. The ten second chunks are each about 14MB in size. Tutorials on TS streaming recommend six second chunks, but the Apple tool defaults to ten. With short videos, like in my examples, it’s hard to tell the difference.

Apple has released a series of tools to assist with chopping up a large file or stream into small snippets. If you don’t have access to the Apple tool, there are online tools and the cross-platform ffmpeg CLI tool that will do the same. Apple provides the HLS tools on its developer download pages. You need a developer account and Apple ID to access them.

The tools help you to provide live streams, but we can also use them to chop up video files. The key tool to look at for serving basic videos (like in your blog) is mediafilesegmenter. The basic usage of the tool is:

mediafilesegmenter <your_video>

This will produce the .m3u8 playlist file as well as the segmented .ts files and a single, really tiny .mp4 file. Copy all of the files to someplace on your webserver (probably best to put them in their own directory), point to the .m3u8 file with a <video> tag and you might be done.

The key is transcoding your video into a format that the segmenter will process, and then using the mediafilesegmenter to slice it into small files that a browser can download and cache for playback. The According to the man page entry, the segmenter will process “source media files containing H.264/H.265 video or AAC/HE-AAC, AC-3, EC-3, MP3, xHE-AAC, FLAC or ALAC audio”.

The command above is the default for segmenting a single video into a series of smaller chunks. In addition to .ts files containing the video, a “playlist” file gets created. This will work with your HTML5 compatible browser to download, cache and stitch together the .ts files to play the video.

Unfortunately, the default export for videos in the Photos.app is video with the MPEG-4 AAC, HEVC codecs. So, the segmenter will fail. Thankfully, adding the -r option to the command allows the segmenter to process the file. So the command below, accepts a default export movie from Photos and renders the set of files.

mediafilesegmenter -r <your_mov_file>

Other Streams

For other video formats, you can use the VLC app to convert into formats that are easier to stream. However, in testing this, I began to wonder how well the browser can handle different formats.

But what about if you just link to an a .ts file directly?

The video above started out as an old format that no amount of Save As... or Export was able to get into a format that mediafilesegmenter would process. But VLC could turn it into .ts which Safari and a <video> tag handled fine.

Final Notes

For smallish (less than 100MB) videos and modren, fast networks, there seems to be no real performance hit between doing all of this streaming and segmenting and just linking directly to a .mov file as you can see below.

However, those with good vision can notice on the timeline controller (hover your pointer over the video to make the controls stay visible) that the browser is pre-buffering the video. The buffered video part of the timeline is a slightly darker gray line. If the network were to slow, the video would freeze when it reaches the end of the buffered portion.

control with buffering hilighted

The above image shows the buffered video with a red line.