HLS Packaging using FFmpeg – Easy Step-by-Step Tutorial

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.

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?

  1. read an input video from disk.
  2. scale/resize the video to the multiple resolutions required.
  3. transcode each of the scaled videos to the required bitrates
  4. transcode the audio to the required bitrates.
  5. combine the video and audio, package each combination, and create the individual TS segments and the playlists.
  6. 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 –

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.

And, [v1out], [v2out], [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 [v1out], [v2out], and [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:0 3M -maxrate:v:0 3M -minrate:v:0 3M -bufsize:v:0 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:0 1M -maxrate:v:0 1M -minrate:v:0 1M -bufsize:v:0 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 [v1out], [v2out], and [v3out] as our inputs and transcoded each of the inputs using libx264‘s slow preset, and at the desired bitrates.

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-SEGMENTS to 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.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 in combining the various video and audio transcodes to create the different HLS playlists. If you have two renditions that use the same video but different audio, then 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.

For example, -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 stream_%v.m3u8 where %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, simply 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

-master_pl_name master.m3u8

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

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:0 3M -maxrate:v:0 3M -minrate:v:0 3M -bufsize:v:0 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:0 1M -maxrate:v:0 1M -minrate:v:0 1M -bufsize:v:0 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.

HLS packaging using FFmpeg

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.

  1. remove -hls_playlist_type vod
  2. add -hls_list_size and 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 -hls_list_size to 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 data03.ts.

#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.

  1. hls_base_url baseurl: This can be used to append the value denoted by baseurl to every entry in the playlist.
  2. 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 fmp4 instead of mpegts.
  3. hls_fmp4_init_resend: Resend init file after m3u8 file refresh every time, default is 0.
  4. iframes_only : Add the #EXT-X-I-FRAMES-ONLY to playlists that has video segments and can play only I-frames in the #EXT-X-BYTERANGE mode.

Conclusion

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.

About The Author

I’m Krishna Rao Vijayanagar, Ph.D., and I am the Founder and Editor of OTTVerse.com. I've spent several years working hands-on with Video Codecs (AVC, HEVC, MultiView Plus Depth), ABR streaming, and Video Analytics (QoE, Content & Audience, and Ad). I hope to use my experience and love for video streaming to bring you information and insights into the OTT universe. Please use the Contact Page to get in touch with me.

Leave a Reply

Scroll to Top