In this post we’re going to look at how to use Bento4 to generate HLS playlists from MP4 files. In previous posts I’ve used
ffmpeg to encode the videos and generate the playlist. However, sometimes it’s better to separate the encoding from the packaging. For example, you may want to support both DASH and HLS from the same encodings.
Bento4 also has support for MPEG DASH and many other formats, but in this post I’ll focus specifically on using the command line tools
mp42hls to create HLS playlists. You can download the latest version from the website. Once you’ve downloaded it, make sure the
bin directory is on your PATH. I used version 184.108.40.206 in the examples.
If you are familiar with Apple’s HLS tools,
mp42hls can be used as a replacement for
mediafilesegmenter respectively. Or put another way, if you want to create a master playlist for more than one video, use
mp4hls. If you only want to create a playlist for a single video, use
For the examples, I’m going to assume there are 3 videos available:
To segment a video and create a playlist for it, open up a terminal and run the following command:
$ mp42hls sample-1080p.mp4
If you look in the directory where you ran the command, you’ll see a number of segments (
segment-1.ts and so on) and the playlist (
stream.m3u8). The default segment duration is 6 seconds, which is what Apple recommends, but you can change this if you want to with the
If you want to choose the name of the playlist and the segments, you can do so like this:
$ mp42hls --index-filename prog_index.m3u8 \ --segment-filename-template "fileSequence%d.ts" sample-1080p.mp4
The segment filename template uses a printf-style format string with a specifier (
%d) for the segment number.
Depending on the duration of your videos, you could potentially end up with a large number of segments. Instead of having multiple segments, HLS supports creating a single file and referencing the segments in the file using a byte range. (You can think of it as taking all of the segments and concatenating them together to create one file.)
$ mp42hls --output-single-file sample-1080p.mp4
If you look in the output directory you should see the playlist (
stream.m3u8) like before but only one segment file (
stream.ts). The playlist now contains byte range tags that specify the length and the offset from the beginning of the file of each segment.
This feature requires HLS version 4 (or greater). The version number is set automatically by the tool when the single file option is specified. There is an option (
--hls-version) that can be used to set the version of HLS your playlist is compatible with.
You may have noticed after running the previous command that a third file was created:
iframes.m3u8. This playlist contains the locations of the I-frames in the video segments. The previous command created it automatically, but if you want to create an I-frame playlist (and you aren’t outputting a single file) you can do so with the
$ mp42hls --iframe-index-filename iframes.m3u8 sample-1080p.mp4
Just like outputting a single file, I-frame playlists are a version 4 feature because they also make use of the byte range tag.
To generate a master playlist from our imaginary videos, run this command:
$ mp4hls sample*
This command takes the list of videos, segments them, creates the playlist for each variant then outputs the master playlist, which references each individual playlist. In this example, instead of specifying the name of each video separately, we let the shell do it for us by using glob expansion. When the command finishes, there will be an
output directory. Inside that directory will be the master playlist (
master.m3u8) and 3 sub-directories:
media-3; there’s one sub-directory for each video. In each of those are the segments and the playlist that correspond to that particular variant.
It’s also possible to specify the name of the master playlist and media playlist. Here’s an example:
$ mp4hls --master-playlist-name=all.m3u8 \ --media-playlist-name=prog_index.m3u8 sample*
If you want to change the location where the files are output, you can do so by using the
-o and specifying the path of the directory to write to.
In addition to the video streams, it’s sometimes necessary to provide an audio-only stream (typically 64kbps). To do that, we can extract the audio from one of the videos and encode it appropriately using
$ ffmpeg -i sample-1080p.mp4 -vn -c:a aac -b:a 64k -ac 2 audio_64kbps.mp4
To create the master playlist with the audio-only stream, run this command:
$ mp4hls sample* [+audio_fallback=yes]audio_64kbps.mp4
output directory, in addition to the previous 3 sub-directories, there is now a
media-4 directory. Inside the master playlist are the following media playlist entries:
# Media Playlists #EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=2359034,BANDWIDTH=4135200,CODECS="avc1.640032,mp4a.40.2",RESOLUTION=1920x1080 media-1/stream.m3u8 #EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=748116,BANDWIDTH=1295505,CODECS="avc1.64001E,mp4a.40.2",RESOLUTION=854x480 media-2/stream.m3u8 #EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=1256065,BANDWIDTH=2203392,CODECS="avc1.64001F,mp4a.40.2",RESOLUTION=1280x720 media-3/stream.m3u8 #EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=133642,BANDWIDTH=136500,CODECS="mp4a.40.2" media-4/stream.m3u8
The last entry corresponds to the audio stream. You can tell because the CODECS attribute (highlighted) is set to AAC-LC audio. There is no video codec component.
In this section, we’ll see how to encrypt videos.
The first step is to generate an encryption key. HLS supports AES-128, which means that keys have to be 128 bits in length (16 bytes). We can use
openssl to create the key and write it out to a file:
$ openssl rand 16 > enc.key
This key will be used for both the encryption and decryption. Next, we run the command to create the playlist and encrypt the video segments:
$ mp42hls --encryption-key $(hexdump -e '"%x"' enc.key) \ --encryption-key-uri https://hlsbook.net/enc.key sample-1080p.mp4
We specify the encryption key with the
--encryption-key option but
mp42hls expects the key to be a hexadecimal string so we have to convert it first. Next, we specify the location of the key with the
--encryption-key-uri option. The client will attempt to fetch the key from this location in order to decrypt the video segments.
You can also encrypt each variant when creating a master playlist. To do this with the key we’ve just generated, run this command:
mp4hls --encryption-key=$(hexdump -e '"%x"' enc.key) \ --encryption-key-uri https://hlsbook.net/enc.key sample*
--signal-session-key option is present, a EXT-X-SESSION-KEY tag will be added to the master playlist. This is an optimisation that allows the client to preload the decryption key; otherwise, it would have to fetch the media playlist first to determine the location of the key.
If you want to learn more about HLS encryption, check out this post.
Bento4 is a useful tool that works across multi-platforms so if you don’t have access to Apple’s HLS tools, it’s a good alternative. It’s also simpler to use than
ffmpeg. If you don’t believe me, take a look at my post about how to create a master playlist with ffmpeg.
In addition to some of the features highlighted here, it also supports subtitles, multilingual audio, and DRM like Apple’s FairPlay Streaming.