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 mp4hls
and 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 1.5.1.0 in the examples.
If you are familiar with Apple’s HLS tools, mp4hls
and mp42hls
can be used as a replacement for variantplaylistcreator
and 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 mp42hls
instead.
For the examples, I’m going to assume there are 3 videos available: sample-1080p.mp4
, sample-720p.mp4
and sample-360p.mp4
.
Let’s begin.
Media Playlists
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-0.ts
, 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 --segment-duration
option.
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 --iframe-index-filename
option:
$ 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.
Master Playlists
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-1
, media-2
, and 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
:
$ 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
Inside the 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.
Encryption
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*
If the --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.
Conclusion
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.
thanks for this post. very helpful.
Do you know if Bento supports rotating keys for HLS like ffmpeg? I cannot seem to locate any info on this and Bento does seem to be much simpler.
I don’t think it does. I had a look at all the available command options and I couldn’t see anything.
Hi, I’m sorry to bother you, I’ve searched the entire network, how to compile bento4 in ubuntu, and how to use it, but I haven’t found any information
Hi Simon, first of all thank you for very detailed blog posts, for sharing your knowledge, and dedicating you time for it. It’s a huge help and great source of exclusive information.
I wonder if you provide paid consulting or development services? If yes – please let me know.
And one more question: Do you have any recommendations for inserting other videos into a HLS stream and have it played nicely, without any interruptions or freeze frames?
For example i need to insert newly encoded video segments into an existing stream, so i will be able to dynamically change titles between chapters in my videos, insert ads, replace parts of the long videos without need to re-encode the whole video. Something like:
#EXTINF:10.600000,
ORIGINAL_segment-0.ts
#EXTINF:10.233333,
NEW_segment-1.ts
#EXTINF:8.333333,
ORIGINAL_segment-2.ts
All the best and Happy New Year!
You can use the EXT-X-DISCONTINUITY tag to insert ads into a video stream. As far as I’m aware, Bento4 doesn’t support something like this so you’ll have to roll your own solution. There is some example PHP code in my book (section 6.2) that selects an “ad” at random and appends it to a video. It shouldn’t be too much effort to change the code to insert a clip at a specific point in an existing stream.
Hi Simon,
Thanks for the post which very helpful.
i have 4k video and need to create create 1080,720,480,360 profiles out of it.
as a input have video_4k.mp4.using bento4 how do i create above mentioned profiles.
Thanks
You can’t. You’ll need to use something like
ffmpeg
to encode the videos first. Take a look at this excerpt from the book.While Generating master playlist, some sub directories are created like media-1, media-2, media-3 etc, is there any option to change the name of the subdirectories ?
I did a quick search in the source code and it looks like it’s not possible.