HLS Manifest DSL

HTTP Live Streaming is a communication protocol for adaptive bitrate streaming.

What this means is that with HLS we can enable HTML5 video clients to stream video online. One of the main benefits of the HLS protocol is that it allows clients to change the video quality on the fly based on several factors. This ability to change the video quality improves the overall user experience as it helps reduce buffering times, provides the best possible video resolution based on your screen size and more.

There are two essential parts to HLS

  • Master Playlist
  • Media Playlist

The master playlist defines a list of available media playlists. More on this in the Master Playlist Section. The media playlist defines where the different video segments are located. More on this in the Media Playlist Section.

A library to define HLS playlists

The HLS DSL is a library that enables you to correctly define a very small yet correct and playable HLS manifest that can be used for video streaming.

With this DSL, you can define a Master and Media playlist.

A master playlist defines multiple media playlists for different video renditions.

Installation

You can download the DSL from GitHub or install it using gradle or maven:

Gradle

implementation("io.onema:playlist-dsl:0.1.0")

Maven

<dependency>
  <groupId>io.onema</groupId>
  <artifactId>playlist-dsl</artifactId>
  <version>0.1.0</version>
</dependency>

Master Playlist

import io.onema.manifestservice.playlist.master

val master = master playlist {
    version set 5
    add stream {
        streamInf resolution  "resolution1" codecs  "codec" bandwidth 12345 frameRate  123F
        info name "videoName" rendition "rendition"
    }
}
val expectation = """#EXTM3U
                    |#EXT-X-VERSION:5
                    |#EXT-X-STREAM-INF:RESOLUTION=resolution1,CODECS="codec",BANDWIDTH=12345,FRAME-RATE=123.000
                    |media/rendition""".trimMargin()
assertEquals(expectation, master)

Media Playlist

import io.onema.manifestservice.playlist.PlaylistTypeEnum.VOD
import io.onema.manifestservice.playlist.media

val media = media playlist {
    version set 5
    type set VOD
    mediaSequence set 0
    targetDuration set 6
    method value "NONE"
    add segment {
        extInf duration 2.0F
        byteRange length 123 position 321
        info name "videoName" rendition "rendition"
    }
}
val expectation = """#EXTM3U
                    |#EXT-X-VERSION:5
                    |#EXT-X-PLAYLIST-TYPE:VOD
                    |#EXT-X-MEDIA-SEQUENCE:0
                    |#EXT-X-TARGETDURATION:6
                    |#EXT-X-KEY:METHOD=NONE
                    |#EXTINF:2.0
                    |#EXT-X-BYTERANGE:123@321
                    |segment/rendition
                    |#EXT-X-ENDLIST""".trimMargin()
assertEquals(expectation, media, "Media playlist didn't match expected value")

.

Workflow

When using HLS, a basic workflow of how the client interacts with the different playlist looks like this:

sequenceDiagram
    autonumber
    Client ->> Master Playlist: GET
    Master Playlist -->> Client: master.m3u8
    alt media playlist 480x270
        Client ->> Media Playlist: GET 480x270
        Media Playlist -->> Client: 480 x 270.m3u8
    else media playlist 640x360
        Client ->> Media Playlist: GET 640x360
        Media Playlist -->> Client: 640 x 360.m3u8
    end
    par segment 1
        Client ->> Media Origin: GET Byte 0 - 491431
        Media Origin -->> Client: Bytes 🎥
    and segment 2
        Client ->> Media Origin: GET Byte 491432 - 1013319
        Media Origin -->> Client: Bytes 🎥
    and segment N
        Client ->> Media Origin: GET Bytes ...
    end

Master Playlist

The master playlist (master.m3u8) defines the available media playlists. Each media playlist entry describes the main characteristics of the video renditions they represent. These include the codec, video resolution, frame rate, the location of the playlist, and more.

Based on this information, the video player will select the best possible video rendition for you.

Media Playlist

My video player selected, to begin with, the 640x360 media playlist. In this playlist, a series of video segments and byte ranges. The video player will start downloading each of these segments and initiate the video playback.

Media Origin

The origin is where the media files are located. There are many ways to store the files, one file for each rendition or one file for each segment for each rendition. In this example, there is one file for each rendition, and the media playlist knows what byte range to request from that file.

HLS Adaptive Bitrate Streaming

Adaptive bitrate streaming allows your player to download different video renditions based on your screen size, network conditions, device type, and more.

Have you ever seen a video that starts pixelated and improves over time? This is adaptive bitrate streaming at work!

In this particular example, after a little while, the video player downloaded a different media playlist (1280x720) and eventually started downloading and playing the segments with better quality.

Code and Demo

I’ve created an end-to-end example of a serverless streaming platform (transcoding and video streaming) where I use this library to generate the master and media playlists dynamically.

To run the demo, use Safari, as it will support HLS natively.

Conclusion

The HLS DSL, is a Kotlin DSL that enables users to generate HLS playlists. With these playlists, you will be able to create a basic HLS streaming service.

What’s Next

I’ll be creating a follow-up article defining how to develop a simple transcoding pipeline and streaming service. If you want to learn more about HLS or adaptive bitrate streaming, take a look at this series of articles on Internet Video Streaming by Eyevinn Technology:

References