Fooling around with ABS, HLS, and FFmpeg
I’ve been messing around with ABS a bit and thought I’d throw up this little project in case anyone else wanted to mess around with it too. I learned a lot more about the FFmpeg command line tool, ABS, HMS, and other random bits throughout the process.
Prerequsites
First, capture or snip a little video that you can quickly generate HLS segements and playlists for different resolutions. For example, I used a 8 sec video of my little lady swinging in Tiburon, but not to plaster her adorable face across the internet, I’ve opted to use the Blue Angles from SF’s Fleet Week 2023.
Also, if you’re on a Mac and are using a video that is from your Photos library, my snippets below assume you’re using a .mp4
and not a .MOV
. You can use FFmpeg to perform the conversion pretty easily.
ffmpeg -i input.mov -c:v libx264 -c:a aac -movflags +faststart input.mp4
Steps
Transcode it
Use the following FFmpeg command to generate different resolutions of segments:
ffmpeg -i input.mp4 -filter_complex \
"[0:v]scale=trunc(oh*a/2)*2:720[v720]; \
[0:v]scale=trunc(oh*a/2)*2:480[v480]; \
[0:v]scale=trunc(oh*a/2)*2:360[v360]" \
-map "[v720]" -map a -c:v:0 h264 -b:v:0 2000k -maxrate:v:0 2000k -bufsize:v:0 4000k -hls_time 4 -hls_playlist_type vod -hls_segment_filename "720p_%03d.ts" 720p.m3u8 \
-map "[v480]" -map a -c:v:1 h264 -b:v:1 1000k -maxrate:v:1 1000k -bufsize:v:1 2000k -hls_time 4 -hls_playlist_type vod -hls_segment_filename "480p_%03d.ts" 480p.m3u8 \
-map "[v360]" -map a -c:v:2 h264 -b:v:2 500k -maxrate:v:2 500k -bufsize:v:2 1000k -hls_time 4 -hls_playlist_type vod -hls_segment_filename "360p_%03d.ts" 360p.m3u8
Note: I used oh*a/
inside FFmpeg’s scale filter to calculate the width to maintain the aspect ratio while also keeping it even. Video codecs typically require width (and sometimes height) to be even. From what I understand, it’s because of how video data is stored + processed in blocks of pixels.
You now have, very simply, different resolutions of the same video. We’ve converted it into .m3u8
which stands for “MPEG version 3.0 URL” (with the ‘8’ indicating UTF-8 encoding.) .m3u8
is a playlist file used in HLS to organize and list the sequence of media segments (usually .ts
files) for streaming. Which leads us to these .ts
files that we generated.
The .ts
files are MPEG-2 Transport Stream files used in HTTP Live Streaming (HLS) to segment video data. they contain chunks of video and audio data that are sequentially streamed to a client, allowing for adaptive bitrate streaming and more efficient video playback over the internet.
Ok, so here is the same video in 720p, 480p, and 360p, respectively:
er.. Platlist it
Now, ABR allows the video player to switch between different quality levels (bitrate and resolution) based on the viewer’s network conditions. This is the basis for how Netflix and Disney+ ensure a smooth viewing experience with the least amount of buffering and drops in video quality when streaming on device.
What we want to do now is create a playlist that contains all three resolutions into a “master” playlist.
echo "#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=2000000,RESOLUTION=1280x720
720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=854x480
480p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=500000,RESOLUTION=640x360
360p.m3u8" > master.m3u8
There’s a ton more to look at in FFmpeg’s docs here.
Serve it
We’ll want to play the video on a webpage someplace. We can use Video.js’s player (popular video player library) to handle the HLS playback. It’s a fantastic library that I’ve used in the past, and required seemingly little config.
You can then fire up a server to see it in action. Python’s SimpleServer did the trick:
python -m http.server 8000
So what happens?
- When the video player starts, it loads
master.m3u8
. - It then determines the viewer’s (your) current bandwidth and device capabilities.
- Based on the conditions, the player selects the best quality stream (e.g., 720p, 480p, or 360p).
- If network conditions change, the player can switch to a different variant stream seamlessly. Try playing around with the Network dev tools to see how the player dynamically switches to a different stream vairant.
That’s it–enjoy!