In this tutorial, we shall learn about HLS packaging using FFmeg. The great part about using FFmpeg is that you can ingest a video, resize it, transcode, package, and stream it without leaving the command line!
We shall first take a look at all the steps to create a HLS packaging for VOD and then take a look at packaging for HLS Live Streaming.
If you want to learn more about HLS playlists, check out our collection of HLS m3u8 files to see examples from different vendors with different use-cases. And, if you are new to HLS (HTTP Live Streaming), please read our basic tutorial to HLS Streaming and why you should use ABR streaming.
Without further ado, let’s get started.
Table of Contents
Basic Steps to HLS Packaging using FFmpeg
Okay, let’s see what the fundamental steps to packaging a VOD file using HLS are, shall we?
- read an input video from disk.
- scale/resize the video to the multiple resolutions required.
- transcode each of the scaled videos to the required bitrates
- transcode the audio to the required bitrates.
- combine the video and audio, package each combination, and create the individual TS segments and the playlists.
- create a master playlist that points to each of the variants.
Now, let’s tackle this step by step, shall we?
Resize a Video to Multiple Resolutions using FFmpeg
Okay, Step 1 and 2 involve reading a video from disk and scaling it to multiple resolutions. This can be done in a single command as follows (but this is incomplete as I have not specified the output files – its only a partial step in the process 🙂 )
ffmpeg -i brooklynsfinest_clip_1080p.mp4 -filter_complex "[0:v]split=3[v1][v2][v3]; [v1]copy[v1out]; [v2]scale=w=1280:h=720[v2out];[v3]scale=w=640:h=360[v3out]"
[0:v] refers to the input file’s first video stream. In our case, there is only one video stream and this is split into 3 outputs
[v1], [v2], [v3]. Each of these are taken as inputs to a scaling function in FFmpeg that accepts a height and width number.
Here, we are scaling the input video to 1080p, 720p, and 360p.
[v3out] are variables that contain the output of the scaling process. Note, here we are assuming that the scaling process is going to retain the aspect ratio. Else, you can force it and apply letterboxing if necessary. For more on this topic, please check our tutorial on resizing and scaling videos using FFmpeg.
Transcode a Video to Multiple Bitrates for HLS Packaging using FFmpeg
Next, we move on to Steps 3 & 4 – we have to transcode the video to multiple bitrates as is typically done for ABR Video Streaming.
Remember, that we have already scaled the video at the required resolutions and stored the output in
[v3out]. Let’s use those directly as inputs for the transcoding step.
-map "[v1out]" -c:v:0 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:0 5M -maxrate:v:0 5M -minrate:v:0 5M -bufsize:v:0 10M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map "[v2out]" -c:v:1 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:1 3M -maxrate:v:1 3M -minrate:v:1 3M -bufsize:v:1 3M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map "[v3out]" -c:v:2 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:2 1M -maxrate:v:2 1M -minrate:v:2 1M -bufsize:v:2 1M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map a:0 -c:a:0 aac -b:a:0 96k -ac 2 \ -map a:0 -c:a:1 aac -b:a:1 96k -ac 2 \ -map a:0 -c:a:2 aac -b:a:2 48k -ac 2 \
Can you see what’s been done here? We have taken the three variables
[v3out] as our inputs and transcoded each of the inputs using
slow preset, and at the desired bitrates.
Important: I have only one audio track in my video, hence, I have used
-map a:0 in all my commands.
Note: You can also choose your own encoding parameters and modify it to your liking and requirements. I’ve used some simple parameters to mimic a CBR encode in this example. There are probably a zillion ways to transcode your videos using FFmpeg. You can choose between a combination of presets, crf values, CBR settings, etc.
Importantly, we have set the
-keyint_min value to
48 which should force a keyframe periodically as this is very important in ABR streaming.
Now, we move on to the next step which is to create an HLS
m3u8 playlist file for each of the renditions/variants.
Creating HLS Playlists (m3u8) using FFmpeg
Now that we have the commands to transcode a video into multiple bitrate variants, let’s start creating HLS VOD Playlists FFmpeg.
Some of the important settings that are needed for HLS packaging are –
hls_playlist_type=vod: By setting this value, FFmpeg creates a VOD playlist, inserts #EXT-X-PLAYLIST-TYPE:VOD into the m3u8 header and forces hls_list_size to 0.
hls_time seconds: We need to use this to set the target segment length in seconds.
- The default value is 2 seconds and the segment will be cut on the next key frame after this time has passed.
- That is why it is important for us to make sure that there is a Key frame at the end of every N seconds for each of the bitstream variants so that they align with each other.
hls_segment_type: this takes on two values – mpegts or fmp4 and creates either TS segments or fmp4 (CMAF) segments which is useful for creating a single set of streams for both HLS and DASH.
-hls_flags independent_segments: Add the
#EXT-X-INDEPENDENT-SEGMENTSto playlists when all the segments of that playlist are guaranteed to start with a Key frame.
hls_segment_filename filename: this is used to name the segments that are created during the packaging process.
Here’s an example of creating a playlist for a single video file
-f hls \ -hls_time 2 \ -hls_playlist_type vod \ -hls_flags independent_segments \ -hls_segment_type mpegts \ -hls_segment_filename stream_%v/data%02d.ts \ -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" stream_%v/stream.m3u8
If you see the last line, you’ll notice a function called
var_stream_map. What does this do?
var_stream_map is an FFmpeg function that helps us combine the various video and audio transcodes to create the different HLS playlists.
Suppose you have two renditions that use the same video but different audio. In that case, you can select the different video and audio versions and join them together instead of creating multiple encodes for the sake of creating different playlists.
-var_stream_map "v:0,a:0 v:1,a:0 v:2,a:0" implies that the audio stream denoted by
a:0 is used in all three video renditions.
FFmpeg takes these video-audio combinations and creates the individual variants’
.m3u8 files with the name
%v is an iterator that takes its value from the stream number being packaged.
Create an HLS Master Playlist (m3u8) using FFmpeg
If you have understood how to create an HLS playlist using FFmpeg, then creating a Master Playlist using FFmpeg is very simple. In case you don’t know what a master playlist is, it is simply a file that lists the playlists of the individual variants that hav ebene packaged using HLS.
To create a master playlist using FFmpeg, add the keyword
-master_pl_name to your FFmpeg command and provide the name that you want to assign to the master playlist. For example, if I want to refer to the master playlist as “master.m3u8”, then all I have to say is
That’s it. After FFmpeg is done with the command line, you’ll have an HLS master playlist with the names of the other playlists listed.
Final Script for HLS Packaging using FFmpeg – VOD
Note that I have only one audio track in my video, hence, I have used
-map a:0 in all my commands.
ffmpeg -i brooklynsfinest_clip_1080p.mp4 \ -filter_complex \ "[0:v]split=3[v1][v2][v3]; \ [v1]copy[v1out]; [v2]scale=w=1280:h=720[v2out]; [v3]scale=w=640:h=360[v3out]" \ -map "[v1out]" -c:v:0 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:0 5M -maxrate:v:0 5M -minrate:v:0 5M -bufsize:v:0 10M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map "[v2out]" -c:v:1 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:1 3M -maxrate:v:1 3M -minrate:v:1 3M -bufsize:v:1 3M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map "[v3out]" -c:v:2 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:2 1M -maxrate:v:2 1M -minrate:v:2 1M -bufsize:v:2 1M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map a:0 -c:a:0 aac -b:a:0 96k -ac 2 \ -map a:0 -c:a:1 aac -b:a:1 96k -ac 2 \ -map a:0 -c:a:2 aac -b:a:2 48k -ac 2 \ -f hls \ -hls_time 2 \ -hls_playlist_type vod \ -hls_flags independent_segments \ -hls_segment_type mpegts \ -hls_segment_filename stream_%v/data%02d.ts \ -master_pl_name master.m3u8 \ -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" stream_%v.m3u8
Let’s look at the output of this script.
It first produces a master playlist, three folders containing the individual segments, and the playlists for the variations.
Here is what the
master.m3u8 file looks like –
#EXTM3U #EXT-X-VERSION:6 #EXT-X-STREAM-INF:BANDWIDTH=5605600,RESOLUTION=1920x1080,CODECS="avc1.640032,mp4a.40.2" stream_0.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=3405600,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2" stream_1.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=1205600,RESOLUTION=640x360,CODECS="avc1.64001e,mp4a.40.2" stream_2.m3u8
You can see that the master playlist references the individual playlists for the 1080p, 720p, and 360p HLS variants.
Now, let’s see what the 1080p HLS variant looks like. It clearly says that it is a VOD playlist, that the segments are independent, and the length of each segment is 2 seconds (as per our settings).
#EXTM3U #EXT-X-VERSION:6 #EXT-X-TARGETDURATION:2 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXT-X-INDEPENDENT-SEGMENTS #EXTINF:2.002000, data00.ts #EXTINF:2.002000, data01.ts #EXTINF:2.002011, data02.ts #EXTINF:2.002000, data03.ts #EXTINF:2.002000, data04.ts #EXTINF:2.002000, data05.ts #EXTINF:2.002000, data06.ts #EXTINF:2.002000, data07.ts #EXTINF:2.002011, data08.ts #EXTINF:2.002000, data09.ts #EXTINF:0.041711, data10.ts #EXT-X-ENDLIST
Live HLS Packaging using FFmpeg
If you want to create a live HLS playlist using FFmpeg, then the process is not very different from the VOD steps we just covered. Here are some of the changes you need to make.
-hls_list_sizeand set it to a number that denotes the number of segments you want in the playlists of the individual variations.
For example, if we set
2, then the playlist will contain only two segments and FFmpeg will re-write this playlist by adding the new segments and shifting out the old ones.
Here is an example –
#EXTM3U #EXT-X-VERSION:6 #EXT-X-TARGETDURATION:2 #EXT-X-MEDIA-SEQUENCE:1 #EXT-X-INDEPENDENT-SEGMENTS #EXTINF:2.002000, data01.ts #EXTINF:2.002011, data02.ts
And after a couple of seconds, segment
data01.ts gets dropped and is replaced by segment
#EXTM3U #EXT-X-VERSION:6 #EXT-X-TARGETDURATION:2 #EXT-X-MEDIA-SEQUENCE:2 #EXT-X-INDEPENDENT-SEGMENTS #EXTINF:2.002011, data02.ts #EXTINF:2.002000, data03.ts
Other useful HLS Packaging options in FFmpeg
Finally, let’s take a quick look at some of the interesting options that FFmpeg provides for HLS Packaging for VOD and Live Streaming.
hls_base_url baseurl: This can be used to append the value denoted by baseurl to every entry in the playlist.
hls_fmp4_init_filename filename: Set filename to the fragment files header file, default filename is init.mp4. This is used when you set the segment type to
hls_fmp4_init_resend: Resend init file after m3u8 file refresh every time, default is 0.
iframes_only: Add the
#EXT-X-I-FRAMES-ONLYto playlists that has video segments and can play only I-frames in the
By now, I hope you have a good understanding of how FFmpeg can be used to transcode and package a video using the HLS streaming protocol. For a complete list of options for HLS packaging using FFmpeg, please take a look at the FFmpeg documentation. In a future article, we shall cover HLS Packaging using FFmpeg with Encryption.
Until then, take care and keep visiting OTTVerse.com.
Krishna Rao Vijayanagar
Krishna Rao Vijayanagar, Ph.D., is the Editor-in-Chief of OTTVerse, a news portal covering tech and business news in the OTT industry.
With extensive experience in video encoding, streaming, analytics, monetization, end-to-end streaming, and more, Krishna has held multiple leadership roles in R&D, Engineering, and Product at companies such as Harmonic Inc., MediaMelon, and Airtel Digital. Krishna has published numerous articles and research papers and speaks at industry events to share his insights and perspectives on the fundamentals and the future of OTT streaming.