HLS Streaming and Playlists

HLS Streaming make it easy to adapt your video or audio stream to a user based on their available bandwidth, switching between streams in mid-play as the transfer speed changes. Pulling all of the pieces can be confusing, so we'll show you how to create a few simple segmented files for different bandwidths and a playlist to provide to users on iOS devices so that they can play them.

Segmented Files

We'll start off by making several segmented files, each targeted at a given bandwidth. With these outputs Zencoder creates a manifest file and segmented video files, which allow the player to download each file individually as it plays the video.

{
    "audio_bitrate": 56,
    "audio_sample_rate": 22050,
    "base_url": "s3://my-bucket/",
    "decoder_bitrate_cap": 900,
    "decoder_buffer_size": 2400,
    "filename": "file-640k.m3u8",
    "max_frame_rate": 30,
    "public": 1,
    "type": "segmented",
    "video_bitrate": 600,
    "width": 400,
    "format": "ts"
}

The m3u8 file created by the above output is a segmented file. Requesting this output, plus the others described below, will create multiple segmented files targeted at different bandwidths.

This output has a type of "segmented", a format of "ts", and defaults to a video codec of "h264". Each segmented output will have a manifest file in M3U8 format created as its main output, and the segments will be named like the filename of the output. The segmented files will be uploaded along-side the manifest, and named with "-00001" suffixes before the extension. These outputs can be used as-is for standard streaming, simply by referencing the .m3u8 file from the client, and ensuring the segment files are available from the same location as the manifest. Multiple .m3u8 file can also be referenced by a playlist to support adaptive streaming.

The Playlist

You can create adaptive-bitrate playlists yourself, or have Zencoder do it as part of a job. To have us create the playlist, just add another output to your job with a type of "playlist" and specify one or more streams with a path (that is relative to the playlist file's location) and the source of the stream, which is the label of the corresponding output. Bandwidth, resolution, and codec can also be specified, but by default these are inferred from the source. The stream information will be formatted into a playlist and uploaded just like any other output.

This playlist file references the segmented outputs created above, specifying the bandwidth at which each should be played. Loading this file with a device that supports HLS will allow the stream to be adjusted as available bandwidth changes.

The first entry in the streams will be used when a user opens the file and is used as part of a test to determine which stream is most appropriate. The order of the other entries is irrelevant.

HLS outputs (with video) that have max_hls_protocol_version of 4 or greater will now generate an I-Frame playlist, also known as a "keyframe manifest". By default, the filename is the same as the main manifest file, but with "-iframe-index" appended. For example, for a manifest with the filename "master.m3u8", the keyframe manifest would be named "master-iframe-index.m3u8". This can be overridden using keyframe_manifest_filename. If an HLS playlist output specifies a "source" stream that has a keyframe manifest, then that keyframe manifest will automatically be included into the playlist as an EXT-X-I-FRAMES-ONLY stream.

{
    "base_url": "s3://my-bucket/",
    "filename": "playlist.m3u8",
    "public": 1,
    "streams": [
    {
        "bandwidth": 440,
        "path": "file-440k.m3u8"
    },
    {
        "bandwidth": 640,
        "path": "file-640k.m3u8"
    },
    {
        "bandwidth": 240,
        "path": "file-240k.m3u8"
    },
    {
        "bandwidth": 150,
        "path": "file-150k.m3u8"
    },
    {
        "bandwidth": 64,
        "path": "file-64k.m3u8"
    }
    ],
    "type": "playlist"
}

The Whole Picture

Multiple outputs of different bitrates may be specified and referenced by the playlist. These settings match the guidelines given in our iOS encoding guide.

{
    "input": "s3://zencodertesting/test.mov",
    "output": [
    {
        "label": "hls-64",
        "audio_bitrate": 56,
        "audio_sample_rate": 22050,
        "base_url": "s3://my-bucket/",
        "filename": "file-64k.m3u8",
        "format": "aac",
        "public": 1,
        "type": "segmented"
    },
    {
        "label": "hls-240",
        "audio_bitrate": 56,
        "audio_sample_rate": 22050,
        "base_url": "s3://my-bucket/",
        "decoder_bitrate_cap": 300,
        "decoder_buffer_size": 800,
        "filename": "file-240k.m3u8",
        "max_frame_rate": 15,
        "public": 1,
        "type": "segmented",
        "video_bitrate": 200,
        "width": 400,
        "format": "ts"
    },
    {
        "label": "hls-440",
        "audio_bitrate": 56,
        "audio_sample_rate": 22050,
        "base_url": "s3://my-bucket/",
        "decoder_bitrate_cap": 600,
        "decoder_buffer_size": 1600,
        "filename": "file-440k.m3u8",
        "public": 1,
        "type": "segmented",
        "video_bitrate": 400,
        "width": 400,
        "format": "ts"
    },
    {
        "label": "hls-640",
        "audio_bitrate": 56,
        "audio_sample_rate": 22050,
        "base_url": "s3://my-bucket/",
        "decoder_bitrate_cap": 900,
        "decoder_buffer_size": 2400,
        "filename": "file-640k.m3u8",
        "public": 1,
        "type": "segmented",
        "video_bitrate": 600,
        "width": 480,
        "format": "ts"
    },
    {
        "label": "hls-1040",
        "audio_bitrate": 56,
        "audio_sample_rate": 22050,
        "base_url": "s3://my-bucket/",
        "decoder_bitrate_cap": 1500,
        "decoder_buffer_size": 4000,
        "filename": "file-1040k.m3u8",
        "public": 1,
        "type": "segmented",
        "video_bitrate": 1000,
        "width": 640,
        "format": "ts"
    },
    {
        "label": "hls-1540",
        "audio_bitrate": 56,
        "audio_sample_rate": 22050,
        "base_url": "s3://my-bucket/",
        "decoder_bitrate_cap": 2250,
        "decoder_buffer_size": 6000,
        "filename": "file-1540k.m3u8",
        "public": 1,
        "type": "segmented",
        "video_bitrate": 1500,
        "width": 960,
        "format": "ts"
    },
    {
        "label": "hls-2040",
        "audio_bitrate": 56,
        "audio_sample_rate": 22050,
        "base_url": "s3://my-bucket/",
        "decoder_bitrate_cap": 3000,
        "decoder_buffer_size": 8000,
        "filename": "file-2040k.m3u8",
        "public": 1,
        "type": "segmented",
        "video_bitrate": 2000,
        "width": 1024,
        "format": "ts"
    },
    {
        "base_url": "s3://my-bucket/",
        "filename": "playlist.m3u8",
        "public": 1,
        "streams": [
        {
            "source": "hls-2040",
            "path": "file-2040k.m3u8"
        },
        {
            "source": "hls-1540",
            "path": "file-1540k.m3u8"
        },
        {
            "source": "hls-1040",
            "path": "file-1040k.m3u8"
        },
        {
            "source": "hls-640",
            "path": "file-640k.m3u8"
        },
        {
            "source": "hls-440",
            "path": "file-440k.m3u8"
        },
        {
            "source": "hls-240",
            "path": "file-240k.m3u8"
        },
        {
            "source": "hls-64",
            "path": "file-64k.m3u8"
        }
        ],
        "type": "playlist"
    }
    ]
}

NOTES

  • The playlist generation currently uses no information from the other outputs in the job, but is simply a way to easily generate a correctly formatted adaptive-bitrate playlist and have it uploaded along with the other files.
  • While iOS devices will play the adaptive bitrate playlist created, VLC won't, due to errors in handling relative URLs. It will look for the segment files in the same directory as the playlist file, rather than the directory of the manifest file.
  • decoder_bitrate_cap is set to 1.5x the target bitrate of the file. decoder_buffer_size is set to 3.5x to 5x the target bitrate of the file. These settings will help keep a consistent bitrate throughout the file, so that the segmented segments won't vary too much in size and bitrate.
  • If a segmented output is uploaded to temporary storage, it will be uploaded as a ZIP archive.