Content Creation

This section describes several open source tools along with scripts we have developed to transcode/transrate, encrypt (with one or more DRMs), and perform DASH segmentation/packaging of existing MP4/H.264 content.

For creating content, we have explored options both in the open-source and commercial spaces. While the tools documented below may not represent an exhaustive list of the options available in either space, they provided the shortest path to achieving desired levels of functionality for the project. We have mostly focused on content that follows the DASH-AVC/264 guidelines as established by the DASH Industry Forum.

For our evaluation, we received a functionality-limited version of the commercially available FAMIUM toolset from Fraunhofer FOKUS. The FAMIUM tools are capable of transcoding for ABR and encrypting the resulting content (using CommonEncryption) with Microsoft PlayReady. Please contact Fraunhofer directly for more information about their tools.

Media Transcoding/Transrating

The first step in creating DASH content is to transcode your high-quality source content to AVC/H.264 video and AAC audio in various resolutions and bitrates.

FFMpeg

From open-source offerings, we chose FFMpeg for transcoding and transrating. You may also alse have used libAV ffmpeg , which is a fork of the project and still maintains some compatibility with ffmpeg as far as command-line structure and usage is concerned. Therefore, it may be possible to take some of these instructions and use them with libAV instead. All build instructions below have been tested on Ubuntu 13.10 but may work on any Debian-based Linux distribution.

First install the basic components for git and software compilation.

$ sudo apt-get install essential-build git yasm

For transcoding video to H.264 and AAC-LC you will need to build ffmpeg from source, with those components enabled. First you must install the development libraries for libx264 and libfdk-aac.

$ sudo apt-get install libx264-dev libfdk-aac-dev

Then get the ffmpeg sources from git. Configure, build, and install.

$ git clone git://git.ffmpeg.org/ffmpeg.git
$ cd libav
$ ./configure --enable-nonfree --enable-gpl --enable-libx264 --enable-libfdk-aac
$ make
$ sudo make install

Once you have ffmpeg built, you have a wide variety of choices for generating your adaptive bitrate streams. You must run ffmpeg once for each adaptive profile. Below is an example that will generate 2 adaptive streams (AVC High @ L3.1) at 2Mb/s and 3Mb/s:

$ ffmpeg -i input_720p.mov
          -codec:v libx264 -profile:v high -level 31 -b:v 2000k
          -codec:a libfdk_aac -profile:a aac_low
          abr_720p_h264-2Mb-high-3.1_aac-lc.mp4
$ ffmpeg -i input720p.mov
          -codec:v libx264 -profile:v high -level 31 -b:v 3000k
          -codec:a libfdk_aac -profile:a aac_low
          abr_720p_h264-3Mb-high-3.1_aac-lc.mp4

Here is a more complex example that produces 5 adaptive profiles with varying bitrates, H.264 profiles and levels, and scaled resolutions (all with 1 audio bitrate transcoded to AAC-LC):

$ ffmpeg -i input_1080p.mov -vf "scale=w=512:h=288"
          -codec:v libx264 -profile:v main -level 21 -b:v 360k
          -codec:a libfdk_aac -profile:a aac_low
          abr_512x288_h264-360Kb_aac-lc.mp4
$ ffmpeg -i input_1080p.mov -vf "scale=w=704:h=396"
          -codec:v libx264 -profile:v main -level 30 -b:v 620k
          -codec:a libfdk_aac -profile:a aac_low
          abr_704x396_h264-620Kb_aac-lc.mp4
$ ffmpeg -i input_1080p -vf "scale=w=896:h=504"
          -codec:v libx264 -profile:v high -level 31 -b:v 1340k
          -codec:a libfdk_aac -profile:a aac_low
          abr_896x504_h264-1340Kb_aac-lc.mp4
$ ffmpeg -i input_1080p.mov -vf "scale=w=1280:h=720"
          -codec:v libx264 -profile:v high -level 32 -b:v 2500k
          -codec:a libfdk_aac -profile:a aac_low
          abr_1280x720_h264-2500Kb_aac-lc.mp4
$ ffmpeg -i input_1080p.mov -vf "scale=w=1920:h=1080"
          -codec:v libx264 -profile:v high -level 40 -b:v 4500k
          -codec:a libfdk_aac -profile:a aac_low
          abr_1920x1080_h264-4500Kb_aac-lc.mp4

While the previous ffmpeg examples produce multi-bitrate files which could be suitable for some uses, our goal is to create content suitable for the DASH-AVC/264 interoperability points provided by the DASH-IF. These guidelines require a couple of things. First, audio and video data may not be multiplexed into a single stream. Second, each DASH segment may vary from its expected duration by no more than 50%, so we need to ensure that our segmenter will find sufficient I-Frames to support this. The following script (located in GitHub) will produce streams that meet all of our requirements.

# Override paths to FFMPEG tools here
ffmpeg=ffmpeg
ffprobe=ffprobe

function usage {
  echo ""
  echo "Transcode/Transrate Script"
  echo "usage:"
  echo "   video_scaled_demux_5bitrates  "
}

if [ -z $1 ]; then
  echo "Must provide input media file"
  usage
  exit 1
fi
if [ -z $2 ]; then
  echo "Must provide output directory for transcoded/transrated files"
  usage
  exit 1
fi

mkdir -p $2

framerate=$((`./ffprobe $1 -select_streams v -show_entries stream=avg_frame_rate -v quiet -of csv="p=0"`))

function transcode_video {
  vbitrate=$1
  res=$2
  profile=$3
  level=$4
  outdir=$5
  infile=$6
  outfile=video_`echo $res | sed 's/:/x/'`_h264-${vbitrate}.mp4

  $ffmpeg -i $infile -s $res -map_chapters -1 -maxrate $vbitrate -minrate $vbitrate -bufsize $vbitrate -an -codec:v libx264 -profile:v $profile -level $level -b:v $vbitrate -x264opts "keyint=$framerate:min-keyint=$framerate:no-scenecut" $outdir/$outfile
}

function transcode_audio {
  abitrate=$1
  outdir=$2
  infile=$3
  outfile=audio_aac-lc_${abitrate}.mp4

  $ffmpeg -i $infile -map_chapters -1 -vn -codec:a libfdk_aac -profile:a aac_low -b:a $abitrate $outdir/$outfile
}

transcode_video "360k" "512:288" "main" 30 $2 $1
transcode_video "620k" "704:396" "main" 30 $2 $1
transcode_video "1340k" "896:504" "high" 31 $2 $1
transcode_video "2500k" "1280:720" "high" 32 $2 $1
transcode_video "4500k" "1920:1080" "high" 40 $2 $1

transcode_audio "128k" $2 $1
transcode_audio "192k" $2 $1

This script makes separate calls to ffmpeg for each bitrate and for each audio or video component which provides the demultiplexed output we require. Additionally, the script ensures that I-frames are spaced evenly apart at 1 second intervals. The ffprobe ffmpeg utility is used to detect the framerate of the source media after which the keyint and min-keyint x264 arguments dictate the keyframe interval. The noscenecut argument ensures that ffmpeg does not insert arbitrary I-frames when it detects a visual scene change in the source content.

Encryption

All of the examples in this documentation will use the ISO Common Encryption standard for ISO BMFF files (ISO 23009-7). The Common Encryption specification defines a single set of encryption parameters for all content to make the decryption process more simple and consistent across players. The following restrictions are enforced:

  • AES 128-bit block encryption
  • CBC (Cipher Block Chaining) or CTR (Counter) Block Modes
  • 64-bit or 128-bit Initialization Vectors
  • 128-bit Key IDs

In addition to the required encryption standards, the specification defines several new boxes that describe how encrypted content is to be represented in ISO BMFF containers. These new boxes indicate which tracks within the media have encrypted samples and which samples within that track are actually encrypted. Each set of encrypted samples is assoicated with an encryption key ID. It is possible to have alternating sequences of encrypted and unencrypted samples. It is also possible to have multiple encryption key IDs associated with various samples throughout the media. However, each contiguous set of encrypted samples may only be encrypted by a single key ID.

The box that contains DRM-specific information is called the 'pssh' (Protection System Specific Header). This box contains all the information the CDM and/or player application will need to retrieve decryption keys from the DRM-specific license server. The following sections desribe how to use our tools to encrypt the content and build and insert one or more DRM-specific 'pssh' boxes.

To encrypt our ABR content, we use the open-source MP4Box tool from the GPAC project (maintained by Telecom ParisTech). Please note that it is possible to do some forms of non-CommonEncryption using the MP4Box tool, but it will not be covered in this documentation. We have added features and fixed many bugs in MP4Box over the course of our development, so we recommend that you download one of the nightly builds or build it from source.

The basic usage of MP4Box for content encryption is:

MP4Box -crypt <cryptfile> -out <outputfile> <inputfile>

A helper script is provided with our tools to execute the MP4box encryption process on multiple files. This is very helpful when encrypting multiple ABR content files.

$ ./encrypt.sh
Encryption Script
usage:
 encrypt.sh -o <output_directory> -c <cryptfile> [-i <input_directory>] [INPUT_FILE]...

OPTIONS
-o
     The output directory where encrypted files will be written

-c
     The cryptfile.  XML MP4Box input file used to define the encryption.

-i
     All files in the given directory will be encrypted.  Can be used instead of or in addition
     to the list of files at the end of the command
            

CryptfileBuilder

CableLabs has developed a set of tools to assist in the generation of MP4Box cryptfiles. A cryptfile is the primary input to MP4Box that describes exactly how the content should be encrypted. A cryptfile also specifies the contents of any 'pssh' boxes to be inserted into the encrypted content. One of the CableLabs tools, CryptfileBuilder, is a Java library on top of which you can develop your own DRM-specific cryptfile generator applications. It defines the basic building blocks for MP4Box cryptfiles including bitstream (<BS>), crypt track (<CryptTrack>), and key (<key>) elements.

A top-level Ant build script is provided to build all of the current cryptfile generators. The build script is located in create/encrypt subfolder of the source repository. You must have Ant and Java installed on your system and available in your executable PATH.

Using MP4Box to do Common Encryption is pretty well documented here. In the following sections we detail several DRM-specific tools CableLabs has developed using the CryptfileBuilder library.

Microsoft PlayReady

Microsoft provides several documents that describe the PlayReady DRM system. These documents detail the DRM metadata that is inserted in content files along with special instructions for doing PlayReady content protection in MPEG-DASH. The first step in creating PlayReady encrypted content is to generate your encryption keys.

Encryption Key Generation

Microsoft has made a test license server available for developers who wish to test their implementations. This server can accept query string arguments that apply different attributes to the returned license based on the desired content rights. Since we can not store our own encryption keys in the test license server, we need a way to ensure that the keys we use to encrypt our content will be the same keys that are returned by the license server upon playback. Microsoft has defined an algorithm (detailed at the end of this document in the section titled Content Key Algorithm) that developers can use to generate their own keys based on a unique key seed assigned to the server and a user-defined key ID. The CableLabs cryptfile generator for PlayReady will generate encryption keys according to this algorithm along with a few other crucial pieces of data required for the encyption process. Here is an example of a generated encryption key and checksum based a specified key ID. The binary-encoded forms of the key ID (discussed more later) are also shown.

===============================================
Content key ID =
  0x10000000100010001000100000000001
  0x00000010001000101000100000000001 (MS binary)
  EAAAABAAEAAQABAAAAAAAQ== (Base64)
  AAAAEAAQABAQABAAAAAAAQ== (Base64, MS binary)
Content key =
  0x3A2A1B68DD2BD9B2EEB25E84C4776668
  OiobaN0r2bLusl6ExHdmaA== (Base64)
Checksum =
  0xE53CC8610DA1ACE6
  5TzIYQ2hrOY= (Base64)
===============================================

PlayReady Header Object

The data contents of the 'pssh' box as described by the Microsoft documentation is the PlayReady Header Object (PRO). The PRO is a list of PlayReady records. Records can be of various types, but the only one we will be concerned with is the rights management header (type 0x0001). The rights managment header is a UTF-16 little-endian encoded XML document designated by the top-level Windows Rights Management Header element (WRMHEADER). Here is an example rights management header generated internally by our PlayReady cryptfile builder:

<WRMHEADER xmlns="http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader" version="4.0.0.0">
  <DATA>
    <PROTECTINFO>
      <KEYLEN>16</KEYLEN>
      <ALGID>AESCTR</ALGID>
    </PROTECTINFO>
    <KID>AAAAEAAQABAQABAAAAAAAQ==</KID>
    <CHECKSUM>5TzIYQ2hrOY=</CHECKSUM>
    <LA_URL>http://playready.directtaps.net/pr/svc/rightsmanager.asmx?PlayRight=1&amp;UseSimpleNonPersistentLicense=1</LA_URL>
  </DATA>
</WRMHEADER>

The rights management header in this example conforms to version 4.0.0.0. The Microsoft documentation also describes a version 4.1.0.0 rights management header that contains the same data, but it a little more compact. We haven't been able to get content to play properly when using the 4.1.0.0 header as of the writing of this document.

You can see two values in the rights management header that we pulled from the key generation process. The first value (Checksum, Base64) goes into the CHECKSUM element. The other value (Content Key ID, Base64, MS binary) goes into the KID element. This is the base64 and binary-encoded key ID that we associated with our encryption key. Microsoft's method for binary-encoding GUIDs is little bit different than what you expect. The first 8 bytes are divided into 3 fields of 4 bytes, 2 bytes, and 2 bytes. Each field is encoded using native byte ordering (little-endian on x86 systems). The last 8 bytes are always encoded big-endian, regardless of the native OS. Look closely at the output of the PlayReadyKeygen tool to see the difference between the original key ID and the binary-encoded key ID.

A1B1C1D1-A2B2-A3B3-A4B4-A5B5C5D5E5F5
D1C1B1A1-B2A2-B3A3-A4B4-A5B5C5D5E5F5

PlayReady Cryptfile Generator

Our PlayReady Cryptfile Generator tool performs all of the previous steps to create a cryptfile that will allow MP4Box to encrypt multiple tracks (potentianlly with multiple keys per track). Here is an example of what a complete PlayReady cryptfile would look like:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<GPACDRM type="CENC AES-CTR">
  <DRMInfo type="pssh" version="0">
    <BS ID128="9a04f07998404286ab92e65be0885f95"/>
    <BS bits="32" endian="little" value="762"/>
    <BS bits="16" endian="little" value="1"/>
    <BS bits="16" endian="little" value="1"/>
    <BS bits="16" endian="little" value="752"/>
    <BS data64="PABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBBAEEAQQBBAEUAQQBBAFEAQQBCAEEAUQBBAEIAQQBBAEEAQQBBAEEAQQBRAD0APQA8AC8ASwBJAEQAPgA8AEMASABFAEMASwBTAFUATQA+ADUAVAB6AEkAWQBRADIAaAByAE8AWQA9ADwALwBDAEgARQBDAEsAUwBVAE0APgA8AEwAQQBfAFUAUgBMAD4AaAB0AHQAcAA6AC8ALwBwAGwAYQB5AHIAZQBhAGQAeQAuAGQAaQByAGUAYwB0AHQAYQBwAHMALgBuAGUAdAAvAHAAcgAvAHMAdgBjAC8AcgBpAGcAaAB0AHMAbQBhAG4AYQBnAGUAcgAuAGEAcwBtAHgAPwBQAGwAYQB5AFIAaQBnAGgAdAA9ADEAJgBhAG0AcAA7AFUAcwBlAFMAaQBtAHAAbABlAE4AbwBuAFAAZQByAHMAaQBzAHQAZQBuAHQATABpAGMAZQBuAHMAZQA9ADEAPAAvAEwAQQBfAFUAUgBMAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA="/>
  </DRMInfo>
  <CrypTrack IV_size="8" first_IV="0x81171442af182143" isEncrypted="1" saiSavedBox="senc" trackID="1">
    <key KID="0x10000000100010001000100000000001" value="0x3a2a1b68dd2bd9b2eeb25e84c4776668"/>
  </CrypTrack>
</GPACDRM>

Let's look at the bottom portion of the file first. You can add a CrypTrack element for each track in the content that you want to encrypt (identified by its track ID from the Track Header ('tkhd') box). Within that element, you can specific one or more encryption keys and associated key ID. If you provide more than one key, use the roll attribute of the CrypTrack element to indicate how many samples should be encrypted with each key.

The DRMInfo element defines the 'pssh' box that will be inserted into the encrypted content. As per the Common Encryption spec, the DRM type is identified by the 16-byte GUID "System ID" field followed by the DRM-specific data. The CrypTrack element identifies a track within the source media to be encrypted and associates one or more encryption key/keyID pairs to perform the encryption.

There are a few fine details about how the WRM header object XML must be formatted in order to be properly parsed by a PlayReady CDM. This information is provided simply for a better understanding of the PlayReady DRM syntax -- the cryptfile generation tool takes care of all of these details for you.

  1. There can be no whitespace anywhere within the XML, except to separate XML attributes within an element.This step is essential. If you leave even a single newline character in the file, the CDM will not successfully parse it.
  2. When outputting the XML for insertion into the PSSH, you must use UTF-16, little-endian encoding

The entire process of generating a PlayReady cryptfile has been rolled up into a single tool in playready/cryptgen. Check out the arguments and options you can pass to this tool:

$ java -jar playready/cryptgen/playready.jar -h
Microsoft PlayReady MP4Box cryptfile generation tool.

usage:  CryptfileGen [OPTIONS] <track_id>:{@<keyid_file>|<key_id>[,<key_id>...]} [<track_id>:{@<keyid_file>|<key_id>[,<key_id>...]}]...

  <track_id> is the track ID from the MP4 file to be encrypted.
  After the '<track_id>:', you can specify either a file containing key IDs OR a
  comma-separated list of key IDs.  Key IDs are always represented in GUID form
  (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). Multiple key IDs indicate the use of
  rolling keys.

    <keyid_file> is a file that contains a list of key IDs, one key ID per line.

    <keyid> is a key ID in GUID form.

  OPTIONS:

  -help
    Display this usage message.

  -out <filename>
    If present, the cryptfile will be written to the given file. Otherwise output will be
    written to stdout.

  -version {4000|4100}
    If present, specifies the WRMHeader version to generate.  Must be either '4000' for v4.0.0.0
    or '4100' for v4.1.0.0.  Default is '4000'.

  -url <license_url>
    If present, specifies the license URL to embed in the WRMHeaders.  If not specified, will
    use the default url of:
    'http://playready.directtaps.net/pr/svc/rightsmanager.asmx?PlayRight=1&UseSimpleNonPersistentLicense=1'

  -roll <sample_count>
    Used for rolling keys only.  <sample_count> is the number of consecutive samples to be
    encrypted with each key before moving to the next.

  -ck
    Add ClearKey PSSH to the cryptfile.

  -cp
    Print a DASH <ContentProtection> element that can be pasted into the MPD
        CableLabs 'JSON' ClearKey PSSH to the cryptfile.
            

Notice that you can use CommonEncryption to add CableLabs ClearKey DRM to the encryption process (-ck argument). More on this later. The tool will also display a ContentProtection element (-cp argument) that can be inserted into the MPD. This element can be parsed by media players to pre-fetch DRM licenses prior to downloading media segments that contain the PSSH boxes.

Adobe PrimeTime

Coming Soon

Google Widevine

Generating cryptfiles for Widevine DRM is also supported by the tools. In order to use the tools, you must have contacted Google to setup your own Widevine portal. After setting up the account, you will be given a provider name, signing key and initialization vector, and URLs for license creation and retrieval. For encrypting content, the Widevine server will create key/keyID pairs based on a given contentID (String) and media type (HD, SD, or AUDIO). The server will also return a PSSH box that can be placed in the media during the encyrption process. The CableLabs cryptfile tool actually does not use this Widevine-generated PSSH and instead generates its own. Mostly this was done since the PSSH format is public and we wanted to ensure that we could generate one ourselves. Google uses its Protocol Buffer to serialize the PSSH data into a compact format suitable for the PSSH. The basic structure of the protobuf looks something like this:

message WidevineCencHeader {
  enum Algorithm {
    UNENCRYPTED = 0;
    AESCTR = 1;
  };
  optional Algorithm algorithm = 1;
  repeated bytes key_id = 2;
  // Content provider name.
  optional string provider = 3;
  // A content identifier, specified by content provider.
  optional bytes content_id = 4;
  // Track type. Acceptable values are SD, HD and AUDIO. Used to // differentiate content keys used by an asset.
  optional string track_type = 5;
  // The name of a registered policy to be used for this asset.
  optional string policy = 6;
  // Crypto period index, for media using key rotation.
  optional uint32 crypto_period_index = 7;
}

The process for creating a Widevine cryptfile is supported in the utility located in widevine/cryptgen. Here are the arguments and options you can pass to this tool:

$ java -jar widevine.jar -help
Google Widevine MP4Box cryptfile generation tool.

usage:  CryptfileGen [OPTIONS] <content_id> <track_id>:<track_type>] [<track_id>:<track_type>]...

  <content_id> is a unique string representing the content to be encrypted

  <track_id> is the track ID from the MP4 file to be encrypted

  <track_type> is one of HD, SD, or AUDIO describing the type of the associated track

  OPTIONS:

  -help
    Display this usage message.

  -out <filename>
    If present, the cryptfile will be written to the given file. Otherwise output will be
    written to stdout

  -sign <sign_props_file>
    If present, key requests will be signed with the given key information.  <sign_props_file> is
    a Java properties file with the following properties:
      url:      Your assigned key server URL
      key:      Your assigned 32-byte signing key, base64 notation
      iv:       Your assigned 16-byte initialization vector, base64 notation
      provider: Your assigned provider name
    If this argument is not present, the requests will be unsigned and the
    "widevine_test" provider and URL will be used

  -roll <start_time>,<key_count>,<sample_count>
    Used for rolling keys only.  <start_time> is the integer time basis for the first
    requested key.  Could be epoch or media time or anything else meaningful.  <key_count>
    is the integer number of keys requested.  <sample_count> is the number of consecutive
    samples to be encrypted with each key before moving to the next.

  -ck
    Add ClearKeyPSSH to the cryptfile.

  -cp
    Print a DASH <ContentProtection> element that can be pasted into the MPD
            

Just as with PlayReady, you can use CommonEncryption to mix in ClearKey DRM. Output of ContentProtection element is also supported.

ClearKey

The EME spec mandates browser support for a Simple Decryption system called ClearKey. ClearKey is a "test" DRM in which keys are, at some point, exposed over the network or to Javascript applications. The spec defines the ClearKey system string (org.w3.clearkey) and the format of decryption key data passed to the MediaKeySession.update() API. We wanted to develop a simple implementation in our tools that would show evidence of support (or lack of support) for Clear Key in browsers.

Here is the command-line usage for the ClearKey crypfile generation tool:

$ java -jar clearkey.jar -help
CableLabs ClearKey MP4Box cryptfile generation tool.

usage:  CryptfileGen [OPTIONS] <track_id>:{@<key_file>|<key_id>=<key>[,<key_id>:<key>...]} [<track_id>:{@<key_file>|<key_id>:<key>[,<key_id>:<key>...]}]...

  <track_id> is the track ID from the MP4 file to be encrypted.
  After the '<track_id>:', you can specify either a file containing key/keyID pairs
  OR a comma-separated list of keyID/key pairs separated by '='.  Key IDs are always
  represented in GUID form (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).  Key values are
  always in hexadecimal.  Multiple key IDs indicate the use of rolling keys.

    <keyid_file is a file that contains a list of key pairs, one pair per line in
    the form <key_id>:<key>

    <keyid> is a key ID in GUID form.

    <key> is a 16-byte key value in hexadecimal (with or without the leading '0x').

  OPTIONS:

  -help
    Display this usage message.

  -out <filename>
    If present, the cryptfile will be written to the given file. Otherwise output will be
    written to stdout.

  -roll <sample_count>
    Used for rolling keys only.  <sample_count> is the number of consecutive samples to be
    encrypted with each key before moving to the next.

  -cp
    Print a DASH <ContentProtection> element that can be pasted into the MPD

Common Encryption

In previous versions of the EME specification, the ClearKey PSSH box was not defined, so CableLabs was forced to define its own. Today, the EME Stream Format and Initialization Data Format Registry, which describes how initialization data is to be carried in various media containers, defines the cenc initialiation data type for ISO Common Encryption. Within this cenc registry entry, the ClearKey system ID (1077efec-c0b2-4d02-ace3-3c1e52e2fb4b) and the PSSH box format is called out. It identifies the ClearKey PSSH as a "Version 1" ISO CommonEncryption PSSH box which contains all KeyIDs used by the media.

We have enough now to allow us to create ClearKey-encrypted media, but the spec stops at that point. The process of obtaining ClearKeys is left up to the implementer. The CableLabs EME platform implements two examples ways to access these keys.

  1. Decryption key(s) contained within the player application. The dash.js player's ClearKey implementation will look inside a ProtectionData object that can be associated with a media asset. In the example player application, a JSON file contains a large set of test media for trying out the player. The JSON description for each asset can also specify the ProtectionData object and any clearkeys associated with it. At playback-time, the player will automatically load these keys into the ClearKey CDM.
  2. Decryption key(s) retrieved from a remote server (similar to real DRMs). Keys may be transported securely over the network (e.g. via HTTPS) and are only in the clear within the javascript code running on the browser. CableLabs has implemented a very primitive, node.js-based ClearKey license server that takes KeyIDs as query parameters and returns the associated keys from a hard-coded list.

Common Encryption Multi-DRM

One of the fundamental benfits of Common Encryption is the ability to encrypt content once while still supporting multiple proprietary DRM systems. Up until this point, we have only demonstrated the encryption of content using a single DRM. This section takes what we have learned in the previous sections and describes how to use MP4Box to encrypt a piece of content with multiple key systems that can be played back on the disparate platforms that support those systems.

With MP4Box, it is quite simple to apply multiple DRMs to a piece of content; you just add multiple <DRMInfo> elements to the cryptfile. Each one will introduce a new 'pssh' box into the content.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<GPACDRM type="CENC AES-CTR">
  <DRMInfo type="pssh" version="0">
    <BS ID128="9a04f07998404286ab92e65be0885f95"/>
    <BS bits="32" endian="little" value="762"/>
    <BS bits="16" endian="little" value="1"/>
    <BS bits="16" endian="little" value="1"/>
    <BS bits="16" endian="little" value="752"/>
    <BS data64="PABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBBAEEAQQBBAEUAQQBBAFEAQQBCAEEAUQBBAEIAQQBBAEEAQQBBAEEAQQBRAD0APQA8AC8ASwBJAEQAPgA8AEMASABFAEMASwBTAFUATQA+ADUAVAB6AEkAWQBRADIAaAByAE8AWQA9ADwALwBDAEgARQBDAEsAUwBVAE0APgA8AEwAQQBfAFUAUgBMAD4AaAB0AHQAcAA6AC8ALwBwAGwAYQB5AHIAZQBhAGQAeQAuAGQAaQByAGUAYwB0AHQAYQBwAHMALgBuAGUAdAAvAHAAcgAvAHMAdgBjAC8AcgBpAGcAaAB0AHMAbQBhAG4AYQBnAGUAcgAuAGEAcwBtAHgAPwBQAGwAYQB5AFIAaQBnAGgAdAA9ADEAJgBhAG0AcAA7AFUAcwBlAFMAaQBtAHAAbABlAE4AbwBuAFAAZQByAHMAaQBzAHQAZQBuAHQATABpAGMAZQBuAHMAZQA9ADEAPAAvAEwAQQBfAFUAUgBMAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA="/>
  </DRMInfo>
  <DRMInfo type="pssh" version="0">
    <BS ID128="00000000000000000000000000000001"/>
    <BS bits="8" value="1"/>
    <BS bits="16" string="eyJrZXlzIjpbeyJrdHkiOiJvY3QiLCJhbGciOiJBMTI4R0NNIiwia2lkIjoiRUFBQUFCQUFFQUFRQUJBQUFBQUFBUSIsImsiOiJPaW9iYU4wcjJiTHVzbDZFeEhkbWFBIn1dfQ=="/>
  </DRMInfo>
  <CrypTrack IV_size="8" first_IV="0x8bdb060ffd68016d" isEncrypted="1" saiSavedBox="senc" trackID="1">
    <key KID="0x10000000100010001000100000000001" value="0x3a2a1b68dd2bd9b2eeb25e84c4776668"/>
  </CrypTrack>
</GPACDRM>

There is an interesting caveat when using our tools to do multi-DRM Common Encryption; KeyID and decryption key must be the same across all DRMs. Content samples are only encrypted with a single key/keyID. Multiple keys can be used to encrypt a single piece of content (rolling keys), but only a single key per individual media sample. With the PlayReady test server, we can't install our own keys manually. We must rely on the key generation algorithm. Luckily, our Clear Key test server is completely under our control and we can simply install the same key generated by the PlayReady algorithm. In commercial deployments, DRM license servers must support the manual installation of keys so that a single content encryption process can select the keyID and key and then install it across all DRM servers.

As stated earlier, our current situation is that we only have control of both keys and key IDs from within our ClearKey environment. The other DRMs we are using have test servers that control the generation (or at least the method of generation) of encryption keys. Therefore we can currently only do multi-DRM encryption with ClearKey and one other. Please note the -ck arguments supported by our cryptfile generation tools. Refer to the specific tool's help message (-h arg) for more information.

Since the creation of this documentation, Widevine has release support for foreign keys which allow you to install keys of your choosing into the Widevine license server. This functionality has not yet been integrated into the Widevine toolset.

DRMToday

In order to support to true, multi-DRM content licensing, CableLabs has partnered with castLabs to integrate support for their DRMToday multi-DRM licensing system. DRMToday is a full-featured service for encrypting and packaging premium video content. They provide a multi-DRM license server in the cloud to facilitate client playback on a multitude of devices. To use the CableLabs content creation tools with DRMToday, you must contact them to setup your account.

Unlike our Widevine and PlayReady tools, the DRMToday utility (drmtoday/cryptgen) generates random keyID/key pairs for ingest into the license server. This allows us to select our own keys to use for ClearKey in addition to the other commercial DRMs to generate real, multi-DRM content. The -help mesage from the utility provides more information on its usage:

$ java -jar drmtoday/cryptgen/drmtoday.jar -help
DRMToday MP4Box cryptfile generation tool.

usage:  CryptfileGen [OPTIONS] <drmtoday_props_file> <assetId> <track_id>:<track_type> [<track_id>:<track_type>]...

  <drmtoday_props_file>
    DRMToday properties file that contains merchant login info.  It
    is a Java properties file with the following properties:
      merchant: Your assigned merchant ID
      username: Your DRMToday frontend username
      password: Your DRMToday frontend password
      authHost: Host to use for DRMToday CAS operations
      feHost: Host to use for DRMToday frontend operations

  <assetId> The DRMToday assetId

  <track_id> is the track ID from the MP4 file to be encrypted

  <track_type> is one of AUDIO, VIDEO, or VIDEO_AUDIO describing the content type of the
  associated track

  OPTIONS:

  -help
    Display this usage message.

  -out <filename>
    If present, the cryptfile will be written to the given file. Otherwise output will be
    written to stdout

  -variantId
    Optional DRMToday asset variantId.

  -ck
    Add ClearKey PSSH to the cryptfile.

  -wv
    Add Widevine PSSH to the cryptfile.

  -pr
    Add PlayReady PSSH to the cryptfile.

  -cp
    Print a DASH <ContentProtection> element (for each DRM) that can be pasted into the MPD
            

As you can see, the tools lets you include all of our other supported DRMs when generating the cryptfile!

The DRMToday service provides a REST API that our tools use to communicate with the licensing service. In order to call these APIs, the tools must know some information about your subscription to the DRMToday system. This comes in the form of a Java properties file and it has the following structure:

merchant=my_merchant_id
username=username
password=thisisaterriblepassword
authHost=lic.drmtoday.com
feHost=fe.drmtoday.com

Each organization registered with DRMToday receives a unique merchant ID that is used to associate your requests with the services you have purchased. Then, one or more users are assigned to the organization with the rights to call the DRMToday REST APIs. Each user is assigned a unique username and password. castLabs uses a ticket-based, Central Authentication Service (CAS) for authenticating access to their APIs. Before calling the APIs, you must contact the DRMToday authorization host to receive a "ticket" that will be used in future API calls. Finally, DRMToday operations are performed using the REST APIs at the frontend (fe) host.

When using the tools, you must specify a unique assetID and an optional variantID to identify a piece of content. Key/KeyID pairs will be created for you and delivered securely to the DRMToday license server for ingest. Do not use the same assetID multiple times as you will get an error from DRMToday API call to indicate that keys for that asset already exist. There is a REST API for deleting keys, but it is not supported by the tools at this time

DASH Segmenting and Packaging

The final step in the content creation process is to segment/index our media files and then create a DASH manifest file (.mpd). Our test tools create content that attempts to adhere to the DASH-AVC/264 guidelines. Therefore, we are restricted to a subset of the functionality that appears in the MPEG-DASH specification. MP4Box, once again, is our tool of choice for performing DASH segmenting and packaging. MP4Box supports a large assortment of command-line options when DASHing content. You can run MP4Box -h dash to see a complete description of all options. The basic usage of the tool looks likes this:

MP4Box -dash <seg_duration> -profile <profile> -out <mpdfile> <inputfiles>

isobmff-ondemand Profile

For On-Demand (VOD) content, MP4Box will produce a single fragmented MP4 file for each audio/video/text component of your input video across each ABR profile. If your input contains multiplexed A/V data, the output files from MP4Box will be de-multiplexed audio and video in separate files; one for each ABR bitrate. In fact, DASH-AVC/264 guidelines require demultiplexed content and separate Adaptation Sets for each A/V component. Here is an example command line from one of our scripts that creates DASH On-Demand content:

MP4Box -dash 10000 -rap -bs-switching no -sample-groups-traf \
  -profile onDemand -out bbb_720p_h264_enc.mpd \
  $content_root_dir/bbb_720p_h264-2Mb-high-3.1_aac-lc_enc.mp4#video:id=2Mb \
  $content_root_dir/bbb_720p_h264-2Mb-high-3.1_aac-lc_enc.mp4#audio:id=audio \
  $content_root_dir/bbb_720p_h264-3Mb-high-3.1_aac-lc_enc.mp4#video:id=3Mb

With this command we are generating MPEG-DASH content with 10 second segment duration from 2 video bitrates and 1 audio bitrate. Audio and video Representations will be separated into their own AdaptationSet. The important takeaways from that command are:

Here is an example MPD generated from command line shown above. The content was encrypted using our Common Encryption tools with Microsoft PlayReady. As you can see, there is no indication that PlayReady DRM is being used or if more than one DRM available to decrypt the content. All of that is hidden by Common Encryption and will be discovered by the client player application at playback-time.

<?xml version="1.0"?>
<!-- MPD file Generated with GPAC version 0.5.1-DEV-rev5178M  on 2014-03-28T17:35:25Z-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" minBufferTime="PT1.500000S" type="static" mediaPresentationDuration="PT0H0M33.05S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" xmlns:cenc="urn:mpeg:cenc:2013">
 <ProgramInformation moreInformationURL="http://gpac.sourceforge.net">
  <Title>bbb_720p_h264_enc.mpd generated by GPAC</Title>
 </ProgramInformation>

 <Period id="" duration="PT0H0M33.05S">
  <AdaptationSet segmentAlignment="true" maxWidth="1280" maxHeight="720" maxFrameRate="25" par="16:9" subsegmentStartsWithSAP="1">
   <ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc" cenc:default_KID="a1b1c1d1-a2b2-a3b3-a4b4-a5b5c5d5e5f5"/>
   <Representation id="2Mb" mimeType="video/mp4" codecs="avc1.64001f" width="1280" height="720" frameRate="25" sar="1:1" startWithSAP="1" bandwidth="1971252">
    <BaseURL>bbb_720p_h264-2Mb-high-3.1_aac-lc_enc_track1_dashinit.mp4</BaseURL>
    <SegmentBase indexRangeExact="true" indexRange="1783-1862"/>
   </Representation>
   <Representation id="3Mb" mimeType="video/mp4" codecs="avc1.64001f" width="1280" height="720" frameRate="25" sar="1:1" startWithSAP="1" bandwidth="2912401">
    <BaseURL>bbb_720p_h264-3Mb-high-3.1_aac-lc_enc_track1_dashinit.mp4</BaseURL>
    <SegmentBase indexRangeExact="true" indexRange="1783-1862"/>
   </Representation>
  </AdaptationSet>
  <AdaptationSet segmentAlignment="true" subsegmentStartsWithSAP="1">
   <Representation id="audio" mimeType="audio/mp4" codecs="mp4a.40.2" audioSamplingRate="48000" startWithSAP="1" bandwidth="490735">
    <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
    <BaseURL>bbb_720p_h264-2Mb-high-3.1_aac-lc_enc_track2_dashinit.mp4</BaseURL>
    <SegmentBase indexRangeExact="true" indexRange="1635-1714"/>
   </Representation>
  </AdaptationSet>
 </Period>
</MPD>

Even though there is only one file generated for each Representation, the files are still segmented. The player application can quickly access the segment index ('sidx') box to determine where the various segments begin and end within the file. The <SegmentBase> element tells the application exactly where the 'sidx' box is within the overall file.

isobmff-live Profile

Coming Soon