DASH Streaming and Playlists

This topic explains how to configure jobs for DASH output.

Introduction

Because DASH isn't a specific video format, codec, or data-agnostic packaging format (such as Zip), we've introduced a new option named streaming_delivery_formatto specify that the media files should be structured / formatted according to a particular standard. Selecting "dash" as the streaming_delivery_format will modify some default options for compatibility with DASH, as well as put the output media files into a directory structure consistent with DASH requirements. DASH media outputs must also have type set to "segmented", to ensure the media is prepared for DASH streaming.

{
    "input": "http://s3.amazonaws.com/zencodertesting/test.mov",
    "outputs": [
    {
        "streaming_delivery_format": "dash",
        "video_bitrate": 700,
        "type": "segmented",
        "url": "s3://mybucket/dash-examples/sbr/rendition.mpd"
    }
    ]
}

Details for outputs with streaming_delivery_format set to "dash":

File structure for segmented DASH outputs:

  • The mpd manifest file will be at the root level of the output.
  • The audio and video media will each have their own subdirectory -- "audio/und" and "video/1" respectively.
  • In each of these directories there will be an "init.mp4" with the initialization data.
  • The media data will be in files named "seg-N.m4f" (where N starts at zero and increases for each segment).

DASH outputs with the streaming_delivery_profile set to "on_demand" will use fragmented media files rather than segments:

  • The mpd manifest file will be at the root level of the output.
  • The audio and video media will each be a single file in the same directory as the manifest.
  • The media files will be named similarly to the manifest, but with "-audio" or "-video" suffixes added, as appropriate.

Supported DASH configurations:

  • Formats supported: m4f and webm
  • Video codecs supported: h264, hevc, vp8, and vp9
  • Audio codecs supported: aac, ac3, eac3, and vorbis
  • Outputs in "webm" format currently only support the "on_demand" streaming_delivery_profile
  • Outputs in "webm" format currently do not support DRM

Multiple Bitrates

Zencoder-generated DASH outputs will have the necessary structure to be played on their own, but to truly benefit from such streaming delivery options, you should generate multiple versions at different bitrates (renditions) along with a "playlist" type output that lists the renditions so that a player can switch up / down bitrates as needed depending on the current network conditions. The playlist output is a dependent output - that is, it'll reference the other outputs by their label, and will use data from those outputs to generate the final rendition of the playlist. Note that you must be careful to set the stream paths in the playlist output so that they are relative paths to the location of the rendition files.

{
    "input": "http://s3.amazonaws.com/zencodertesting/test.mov",
    "outputs": [
    {
        "label": "dash-1200",
        "streaming_delivery_format": "dash",
        "video_bitrate": 1200,
        "type": "segmented",
        "url": "s3://mybucket/dash-examples/mbr/1200k/rendition.mpd"
    },
    {
        "label": "dash-700",
        "streaming_delivery_format": "dash",
        "video_bitrate": 700,
        "type": "segmented",
        "url": "s3://mybucket/dash-examples/mbr/700k/rendition.mpd"
    },
    {
        "label": "dash-400",
        "streaming_delivery_format": "dash",
        "video_bitrate": 400,
        "type": "segmented",
        "url": "s3://mybucket/dash-examples/mbr/400k/rendition.mpd"
    },
    {
        "streaming_delivery_format": "dash",
        "type": "playlist",
        "url": "s3://mybucket/dash-examples/mbr/manifest.mpd",
        "streams": [
        { "source": "dash-1200", "path": "1200k" },
        { "source": "dash-700",  "path": "700k" },
        { "source": "dash-400",  "path": "400k" }
        ]
    }
    ]
}

Encryption

Zencoder also supports generating CENC-encrypted DASH outputs, with necessary headers for integrating with Marlin or Playready license servers. For each output, simply specify a hash of drm options, including the method, key_id, and content_key. The key_id and content_key should either be retrieved from your DRM vendor, or pre-generated and sent to your DRM vendor. Once you have those keys, the Zencoder API structure for a single rendition is:

{
    "input": "http://s3.amazonaws.com/zencodertesting/test.mov",
    "outputs": [
    {
        "streaming_delivery_format": "dash",
        "video_bitrate": 700,
        "type": "segmented",
        "url": "s3://mybucket/dash-examples/sbr/rendition.mpd",
        "drm": {
            "method": "cenc",
            "key_id": "d57d416ccd454d5a08d05bff0742ee76",
            "content_key": "f15cae8f4e48a023056e1960ff2228b0"
        }
    }
    ]
}

The content will be encrypted with the content_key, and the .mpd manifest file will include the key_id in its headers. However, to ensure this output is playable, it also needs headers specifying how to retrieve the content_key. On a multi-bitrate DASH encode, these headers only need to be added to the playlist output, but can also be added to each rendition, so the rendition is individually playable. This is done by adding one or more DRM schemas:


    "input": "http://s3.amazonaws.com/zencodertesting/test.mov",
    "outputs": [
    {
        "streaming_delivery_format": "dash",
        "video_bitrate": 700,
        "type": "segmented",
        "url": "s3://mybucket/dash-examples/sbr/rendition.mpd",
        "drm": {
            "method": "cenc",
            "key_id": "d57d416ccd454d5a08d05bff0742ee76",
            "content_key": "f15cae8f4e48a023056e1960ff2228b0",
            "schemas": [
            { "type": "marlin" },
            { "type": "playready", "license_acquisition_url": "https://playready.example.com/license_server/" }
            ]
        }
    }
    ]
}

We can then put this together to create a multi-bitrate, DRM-encrypted DASH job:

{
    "input": "http://s3.amazonaws.com/zencodertesting/test.mov",
    "outputs": [
    {
        "label": "dash-1200",
        "streaming_delivery_format": "dash",
        "video_bitrate": 1200,
        "type": "segmented",
        "url": "s3://mybucket/dash-examples/mbr/1200k/rendition.mpd",
        "drm": {
            "method": "cenc",
            "key_id": "d57d416ccd454d5a08d05bff0742ee76",
            "content_key": "f15cae8f4e48a023056e1960ff2228b0"
        }
    },
    {
        "label": "dash-700",
        "streaming_delivery_format": "dash",
        "video_bitrate": 700,
        "type": "segmented",
        "url": "s3://mybucket/dash-examples/mbr/700k/rendition.mpd",
        "drm": {
            "method": "cenc",
            "key_id": "d57d416ccd454d5a08d05bff0742ee76",
            "content_key": "f15cae8f4e48a023056e1960ff2228b0"
        }
    },
    {
        "label": "dash-400",
        "streaming_delivery_format": "dash",
        "video_bitrate": 400,
        "type": "segmented",
        "url": "s3://mybucket/dash-examples/mbr/400k/rendition.mpd",
        "drm": {
            "method": "cenc",
            "key_id": "d57d416ccd454d5a08d05bff0742ee76",
            "content_key": "f15cae8f4e48a023056e1960ff2228b0"
        }
    },
    {
        "streaming_delivery_format": "dash",
        "type": "playlist",
        "url": "s3://mybucket/dash-examples/mbr/manifest.mpd",
        "streams": [
        { "source": "dash-1200", "path": "1200k" },
        { "source": "dash-700",  "path": "700k" },
        { "source": "dash-400",  "path": "400k" }
        ],
        "drm": {
            "method": "cenc",
            "key_id": "d57d416ccd454d5a08d05bff0742ee76",
            "content_key": "f15cae8f4e48a023056e1960ff2228b0",
            "schemas": [
            { "type": "marlin" },
            { "type": "playready", "license_acquisition_url": "https://playready.example.com/license_server/" }
            ]
        }
    }
    ]
}

Transmuxing

Of course, generating multiple formats of a video can get expensive and wasteful. So Zencoder supports repackaging the same encoded streams into different formats (called transmuxing), by using the copy_video and copy_audio options. Outputs for which the video is transmuxed rather than encoded are charged at 1/4 the regular duration. So if you're planning to make multiple formats of the same video (MP4, HLS, and DASH, for example), you can encode the MP4 version of each bitrate first, and then transmux those outputs to create the HLS and DASH versions. (Note that we don't support transmuxing from a format that's segmented, since the video is no longer a single stream.)

To ensure that the MP4 is prepared according to the constraints of the subsequent segmented streaming formats, also use the prepare_for_segmenting option on the MP4 output. This will control default values for keyframes and captions when encoding the video stream of the MP4 to ensure that it will be compatible with transmuxing to segmented formats afterward.

Using the MP4 as a source for the transmuxed outputs works similarly to the playlist outputs. You specify a source option that matches the label of another output, but this time it's part of the main-level options for the output instead of in the streams hash.

{
    "input": "http://s3.amazonaws.com/zencodertesting/test.mov",
    "outputs": [
    {
        "label": "mp4-700k",
        "prepare_for_segmenting": ["hls", "dash"],
        "video_bitrate": 700,
        "url": "s3://mybucket/transmux-examples/sbr/video.mp4"
    },
    {
        "source": "mp4-700k",
        "copy_video": true,
        "copy_audio": true,
        "type": "segmented",
        "url": "s3://mybucket/transmux-examples/sbr/hls/video.m3u8"
    },
    {
        "source": "mp4-700k",
        "copy_video": true,
        "copy_audio": true,
        "streaming_delivery_format": "dash",
        "type": "segmented",
        "url": "s3://mybucket/transmux-examples/sbr/dash/video.mpd"
    }
    ]
}

Optimizing for delivery

Since DASH is intended for streaming delivery, we recommend controlling the peaks of the bitrates in each rendition to avoid having the player rebuffer or switch to lower bitrate encodes unnecessarily. The options that control the variability of the encoded stream according to what the decoder/player can handle are

Our general recommendation is to set decoder_bitrate_cap to the maximum bitrate available for the video stream. Remember you need to take into account the audio, as well as a bit of overhead for the file format. The decoder_buffer_size is technically supposed to match the available video buffer memory on the decoding device, but is often better used to control how much content may be pre-buffered. It works as a multiple of the decoder_bitrate_cap, so that for example, if decoder_bitrate_cap was 500 (Kilobits per second), and decoder_buffer_size was 1000 (Kilobits), then the buffer could store 2 seconds worth of video. We also recommend setting the video_bitrate a little lower than the decoder_bitrate_cap, which allows the encoder to use more data to encode complex scenes and less data on simpler scenes. A decent starting point would be to assume up to 10% of overhead for the file format, setting the video_bitrate 10% lower than the decoder_bitrate_cap, and allowing about 1.5 seconds of video to be buffered.

Assuming a 1000 Kbps connection is available to the customer:

  • 1000 * 0.90 = 900 Kbps available after subtracting 10% for format overhead.
  • 900 - 128 = 772 Kbps video decoder_bitrate_cap after subtracting 128 Kbps for the audio.
  • 772 * 1.5 = 1158 Kb decoder_buffer_size (for 1.5 seconds worth of video).
  • 772 * 0.90 = 695 Kbps target video bitrate (10% lower than the decoder_bitrate_cap).

So, for a more correct streaming encode example:

{
    "input": "http://s3.amazonaws.com/zencodertesting/test.mov",
    "outputs": [
    {
        "label": "mp4-1000k",
        "prepare_for_segmenting": ["hls", "dash"],
        "audio_bitrate": 128,
        "decoder_bitrate_cap": 772,
        "decoder_buffer_size": 1158,
        "video_bitrate": 695,
        "url": "s3://mybucket/transmux-examples/sbr/video.mp4"
    },
    {
        "source": "mp4-1000k",
        "copy_video": true,
        "copy_audio": true,
        "type": "segmented",
        "url": "s3://mybucket/transmux-examples/sbr/hls/video.m3u8"
    },
    {
        "source": "mp4-1000k",
        "copy_video": true,
        "copy_audio": true,
        "streaming_delivery_format": "dash",
        "type": "segmented",
        "url": "s3://mybucket/transmux-examples/sbr/dash/video.mpd"
    }
    ]
}

Putting it all together

{
    "input": "http://s3.amazonaws.com/zencodertesting/test.mov",
    "outputs": [
    {
        "label": "mp4-1500k",
        "prepare_for_segmenting": ["hls", "dash"],
        "audio_bitrate": 128,
        "decoder_bitrate_cap": 1222,
        "decoder_buffer_size": 1833,
        "video_bitrate": 1100,
        "size": "1280x720",
        "url": "s3://mybucket/full-examples/mp4/1500.mp4"
    },
    {
        "label": "mp4-1000k",
        "prepare_for_segmenting": ["hls", "dash"],
        "audio_bitrate": 128,
        "decoder_bitrate_cap": 772,
        "decoder_buffer_size": 1158,
        "video_bitrate": 695,
        "size": "960x540",
        "url": "s3://mybucket/full-examples/mp4/1000.mp4"
    },
    {
        "label": "mp4-500k",
        "prepare_for_segmenting": ["hls", "dash"],
        "audio_bitrate": 128,
        "decoder_bitrate_cap": 322,
        "decoder_buffer_size": 483,
        "video_bitrate": 290,
        "size": "640x360",
        "url": "s3://mybucket/full-examples/mp4/500.mp4"
    },
    {
        "label": "hls-1500k",
        "source": "mp4-1500k",
        "copy_video": true,
        "copy_audio": true,
        "type": "segmented",
        "url": "s3://mybucket/full-examples/hls/1500.m3u8"
    },
    {
        "label": "hls-1000k",
        "source": "mp4-1000k",
        "copy_video": true,
        "copy_audio": true,
        "type": "segmented",
        "url": "s3://mybucket/full-examples/hls/1000.m3u8"
    },
    {
        "label": "hls-500k",
        "source": "mp4-500k",
        "copy_video": true,
        "copy_audio": true,
        "type": "segmented",
        "url": "s3://mybucket/full-examples/hls/500.m3u8"
    },
    {
        "type": "playlist",
        "url": "s3://mybucket/full-examples/hls/multi_bitrate_playlist.m3u8",
        "streams": [
        { "path": "1000.m3u8", "source": "hls-1000k" },
        { "path": "1500.m3u8", "source": "hls-1500k" },
        { "path": "500.m3u8", "source": "hls-500k" }
        ]
    },
    {
        "label": "dash-1500k",
        "source": "mp4-1500k",
        "copy_video": true,
        "copy_audio": true,
        "streaming_delivery_format": "dash",
        "type": "segmented",
        "url": "s3://mybucket/full-examples/dash/1500k/rendition.mpd",
        "drm": {
            "method": "cenc",
            "key_id": "d57d416ccd454d5a08d05bff0742ee76",
            "content_key": "f15cae8f4e48a023056e1960ff2228b0"
        }
    },
    {
        "label": "dash-1000k",
        "source": "mp4-1000k",
        "copy_video": true,
        "copy_audio": true,
        "streaming_delivery_format": "dash",
        "type": "segmented",
        "url": "s3://mybucket/full-examples/dash/1000k/rendition.mpd",
        "drm": {
            "method": "cenc",
            "key_id": "d57d416ccd454d5a08d05bff0742ee76",
            "content_key": "f15cae8f4e48a023056e1960ff2228b0"
        }
    },
    {
        "label": "dash-500k",
        "source": "mp4-500k",
        "copy_video": true,
        "copy_audio": true,
        "streaming_delivery_format": "dash",
        "type": "segmented",
        "url": "s3://mybucket/full-examples/dash/500k/rendition.mpd",
        "drm": {
            "method": "cenc",
            "key_id": "d57d416ccd454d5a08d05bff0742ee76",
            "content_key": "f15cae8f4e48a023056e1960ff2228b0"
        }
    },
    {
        "streaming_delivery_format": "dash",
        "type": "playlist",
        "url": "s3://mybucket/full-examples/dash/manifest.mpd",
        "streams": [
            { "source": "dash-1500k", "path": "1500k" },
            { "source": "dash-1000k", "path": "1000k" },
            { "source": "dash-500k",  "path": "500k" }
        ],
        "drm": {
            "method": "cenc",
            "key_id": "d57d416ccd454d5a08d05bff0742ee76",
            "content_key": "f15cae8f4e48a023056e1960ff2228b0",
            "schemas": [
                { "type": "marlin" },
                { "type": "playready", "license_acquisition_url": "https://playready.example.com/license_server/" }
            ]
        }
    }
    ]
}