Packaging for MPEG-DASH

Unified Packager can generate fragmented MP4 audio/video and manifests required for MPEG-DASH streaming. We recommend packaging media as CMAF files and then creating the media presentation description using the --package-mpd option. The following is a list of all options specific to MPEG-DASH packaging.

Note

To package MPEG DASH compliant media files, use either --package-mpd or --brand=cmfv, but not both. Previously, we recommended packaging media as .ismv or .isma files with --package-mpd option. This adds a fragment index as mfra to the end of a file. CMAF files have a similar (but distinct) sidx index at the start of the file.

Packaging CMAF with the --package-mpd option (i.e. mp4split –package-mpd -o video.cmfv video.mp4) adds both sidx and mfra boxes, which is not invalid but adds unnecessary overhead.

In summary: use --package-mpd only to generate the Media Presentation Description (MPD).

Options for MPEG-DASH packaging

–package_mpd

Enable mp4split’s MPEG-DASH packaging mode.

–fragment_duration

The duration of each fragment (in milliseconds). Defaults to using the GOP size and if specified manually, it should be a multiple of the GOP size.

–segment_list

Uses a SegmentList instead of a SegmentTemplate to list the segments that are part of the MPD.

MPEG-DASH profile

ISOBMFF On Demand

The default MPEG-DASH profile supported by the packager is the ISO Base media file format On Demand profile: urn:mpeg:dash:profile:isoff-on-demand:2011.

This is the most basic profile for On-Demand content and thus generates the best compatibility among DASH players and DASH capable devices.

The requirements imposed by this profile are:

  • A single segment for each representation (one audio or video track per file).
  • Subsegments are aligned across representations (GOP aligned fragments).
  • Subsegments must begin with a Stream Access Point (IDR/keyframe).
  • The segment is indexed using the Segment Index (‘sidx’).

ISOBMFF Main

When the above requirements are not met (for instance when having audio and video in one file), the packager switches to the Main profile: urn:mpeg:dash:profile:isoff-main:2011.

Note that not all DASH players support this profile.

Creating the media files

First we have to package the audio and video tracks so that it fulfills the requirements listed above:

#!/bin/bash

mp4split -o tos-aac-128k-dash.cmfa   tears-of-steel-aac-128k.mp4
mp4split -o tos-avc1-1000k-dash.cmfv tears-of-steel-avc1-1000k.mp4
mp4split -o tos-avc1-400k-dash.cmfv  tears-of-steel-avc1-400k.mp4

Creating the media files with Common Encryption

Besides preparing content in-the-clear, you can use Common Encryption for the audio and video files.

Using ‘cenc’ Common Encryption

The following example encrypts the files with a single pair of Key ID (KID) and Content Encryption Key (CEK), using the ‘cenc’ scheme from the Common Encryption specification (i.e., ‘CTR’ or Counter mode). This encryption scheme is the default.

In this case, the signaling of the content protection is different depending on the DRM systems used. PlayReady signaling requires the License Acquisition URL (LAURL), while Widevine requires the Protection System Speficic Header (PSSH) as drm_specific_data.

See also Common Encryption (CENC).

#!/bin/bash

KID=10000000100010001000100000000001
CEK=3a2a1b68dd2bd9b2eeb25e84c4776668
KID_UUID=10000000-1000-1000-1000-100000000001 #UUID representation of KID
CEK_B64="OiobaN0r2bLusl6ExHdmaA==" #Base64 byte array representation of CEK
LAURL="https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(kid:${KID_UUID},contentkey:${CEK_B64},ckt:aesctr)"

PSSH=YOUR_PSSH #Add your Widevine DRM specific PSSH data

mp4split -o tos-aac-128k-dash-cenc.cmfa  \
  --iss.key=${KID}:${CEK} --iss.license_server_url=${LAURL} \
  --widevine.key=${KID}:${CEK} --widevine.drm_specific_data=${PSSH} \
  tears-of-steel-aac-128k.mp4

mp4split -o tos-avc1-400k-dash-cenc.cmfv \
  --iss.key=${KID}:${CEK} --iss.license_server_url=${LAURL} \
  --widevine.key=${KID}:${CEK} --widevine.drm_specific_data=${PSSH} \
  tears-of-steel-avc1-400k.mp4

mp4split -o tos-avc1-1000k-dash-cenc.cmfv \
  --iss.key=${KID}:${CEK} --iss.license_server_url=${LAURL} \
  --widevine.key=${KID}:${CEK} --widevine.drm_specific_data=${PSSH} \
  tears-of-steel-avc1-1000k.mp4

Using ‘cenc’ Common Encryption

The following example encrypts the files with a single pair of Key ID (KID) and Content Encryption Key (CEK), using the ‘cbcs’ scheme from the Common Encryption specification (i.e., Cipher Block Chaining mode). This encryption scheme is specified using --scheme_type=cbcs.

In this case, Protection System Specific Header (PSSH) data must be added for each of the DRM systems that you want to use (except Marlin, which doesn’t require such signaling):

#!/bin/bash

KID=10000000100010001000100000000001
CEK=3a2a1b68dd2bd9b2eeb25e84c4776668

PR-PSSH=<PLAYREADY_PSSH> #Add your PlayReady DRM specific PSSH data
WV-PSSH=<WIDEVINE_PSSH> #Add your Widevine DRM specific PSSH data

mp4split -o tos-aac-128k-dash-cenc.cmfa  \
  --cenc.key=${KID}:${CEK} \
  --scheme_type=cbcs \
  --iss.drm_specific_data=${PR-PSSH} \
  --widevine.drm_specific_data=${WV-PSSH} \
  tears-of-steel-aac-128k.mp4

mp4split -o tos-avc1-400k-dash-cenc.cmfv \
  --cenc.key=${KID}:${CEK} \
  --scheme_type=cbcs \
  --iss.drm_specific_data=${PR-PSSH} \
  --widevine.drm_specific_data=${WV-PSSH} \
  tears-of-steel-avc1-400k.mp4

mp4split -o tos-avc1-1000k-dash-cenc.cmfv \
  --cenc.key=${KID}:${CEK} \
  --scheme_type=cbcs \
  --iss.drm_specific_data=${PR-PSSH} \
  --widevine.drm_specific_data=${WV-PSSH} \
  tears-of-steel-avc1-1000k.mp4

Creating the Media Presentation Description file (.mpd)

After the content is properly packaged, step two is creating the Media Presentation Description (MPD) with the packaged media files as input:

#!/bin/bash

mp4split --package_mpd -o tears-of-steel.mpd \
  tos-aac-128k-dash.cmfa \
  tos-avc1-400k-dash.cmfv \
  tos-avc1-1000k-dash.cmfv

If the media is encrrypted, content protection signaling is automatically added to the MPD using the protection information stored in the media files. When PlayReady and/or Widevine information is present, these schemes are added to the MPD. Marlin has no explicit signaling in the media files, and is always added to the MPD:

#!/bin/bash

mp4split --package_mpd -o tears-of-steel-cenc.mpd \
  tos-aac-128k-dash-cenc.cmfa \
  tos-avc1-400k-dash-cenc.cmfv \
  tos-avc1-1000k-dash-cenc.cmfv

Please download the mpeg-dash-cenc.sh sample script that creates the various server manifest as discussed above. The sample content is Tears of Steel.

Adding WebVTT or TTML subtitles

To add subtitles when statically packaging MPEG-DASH, you must first make sure that for each subtitles track you have packaged the WebVTT, SRT or TTML formatted source files in fMP4 (.ismt or .cmft), as explained in Packaging Subtitles.

When you have packaged your subtitles in fMP4, you simply add each fMP4 that contains a subtitles track that you want to add to your input when creating the MPEG-DASH client manifest (.mpd):

#!/bin/bash

mp4split -o tos-wvtt-en.cmft \
  tears-of-steel-en.srt --track_language=eng

mp4split --package_mpd -o tears-of-steel.mpd \
  tos-aac-128k-dash.cmfa \
  tos-avc1-400k-dash.cmfv \
  tos-avc1-1000k-dash.cmfv \
  tos-wvtt-en.cmft

Plain-text sidecar subtitles can also be added (this option is for static packaging only, as Unified Origin does not support sidecar subtitles):

Adding TTML or WebVTT sidecar subtitles for MPEG-DASH

New in version 1.7.12.

While fragmented MP4 is the format of choice for streaming subtitles, occasionally it may be desirable or necessary to expose raw unsegmented subtitles to the player. In these cases, a WebVTT or TTML sidecar file can be added to the MPD.

For instance, to cater (pre-release) Google Shaka player (which supports WebVTT rather than fragmented TTML) the following commands expose German subtitles as WebVTT (as well as fragmented TTML):

#!/bin/bash

mp4split -o subtitles.ttml \
  subtitles_deu.webvtt --track_language=ger

mp4split -o subtitles.cmft subtitles.ttml

mp4split --package-mpd -o movie.mpd \
  [audio/video] \
  subtitles.ismt \
  subtitles_deu.webvtt --track_language=ger

This adds an adaptation set with mime type text/vtt (or application/ttml+xml for TTML):

<AdaptationSet contentType="text" lang="de" mimeType="text/vtt">
  <Representation id="textstream_ger=0" bandwidth="0">
     <BaseURL>subtitles_deu.webvtt</BaseURL>
  </Representation>
</AdaptationSet>

Important

When you add sidecar subtitles, they are added as-is. That is, mp4split won’t any metadata from the file. This means that metadata that is of importance for the file should be passed on the command-line (like the track’s language, for example).

Dolby Vision Support

New in version 1.7.18.

Packaging MPEG-DASH with Dolby Vision Profile 5, aka Single Layer Non Backwards Compatible (NBC) is supported.

First take the input files containing Dolby Vision NBC to create a fragmented MP4. Then use the fragmented mp4 output to create an MPD:

#!/bin/bash

mp4split -o video-with-dolby-vision.cmfv \
  video-with-dolby-vision.mp4

mp4split --package_mpd -o video.mpd \
  video-with-dolby-vision.cmfv \
  example-audio.cmfa

Playback

Playback is supported by at least the following sample players:

  • dash-as, an open source ActionScript project.
  • YouTube, javascript.
  • dash.js. the DASH IF Reference Player using Javascript.

See Which devices support DASH playback? and Which browsers support DASH playback? for more information.

Javascript / HTML5 based players

The Cross-Origin Resource Sharing (CORS) HTTP headers are necessary when the (javascript) player and content are hosted on different domains.

The are set like the following in the (Apache) virtual host configuration:

# Necessary for Media Source Extensions (MSE)
Header always set Access-Control-Allow-Headers "origin, range"
Header always set Access-Control-Allow-Methods "GET, HEAD, OPTIONS"
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Expose-Headers "Server,range"

Action Script / Flash based players

For Action Script (Flash) based players the webserver needs to be setup to support Byte-Range requests through a query parameter. This is achieved by rewriting the byte range in the query parameter to the appropriate Range HTTP header in the request. The dash.as player appends ‘?bytes=’ and the dash.js player appends ‘?range=’ to the URL. The two rewrite rules are given in the configuration below.

  <IfModule headers_module>
    <IfModule rewrite_module>
      # dash.as
      # ^(.*) if .htaccess in a root; ^/(.*) otherwise
      RewriteEngine On
      RewriteCond %{QUERY_STRING} ^(.*)bytes=(.*)$
      RewriteRule ^(.*) - [L,E=range:%2]
      RequestHeader set Range "bytes=%{range}e" env=range

      # dash.js
      # ^(.*) if .htaccess in a root; ^/(.*) otherwise
      RewriteEngine On
      RewriteCond %{QUERY_STRING} ^(.*)range=(.*)$
      RewriteRule ^(.*) - [L,E=range:%2]
      RequestHeader set Range "bytes=%{range}e" env=range
  </IfModule>
</IfModule>

Important

When prepackaging files for MPEG-DASH playout, please make sure that the MP4 files are hosted on a location that doesn’t have a running Unified Origin handling the MP4 files. Otherwise the progressive MP4 download feature will take over and it’ll reformat the prepackaged MP4 files to progressive MP4 instead. This may prevent playout and cause SIDX errors to be displayed by the player.

Advanced recipes

Tuning Common Encryption

–disable_selective_nal_encryption

For NAL structured video samples, only video data in slice NALs should be encrypted with other NAL types kept in the clear.

Specifying this option overrides the (selective) encryption and encrypts all NAL units with a fixed number of bytes in the clear.