Professional Documents
Culture Documents
Param(
# Shaka Packager input video file path.
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$InputVideo,
# Key ID.
[Parameter(Mandatory = $false)]
[System.Guid]$KeyId = [System.Guid]::NewGuid()
)
#########
# About #
#########
<#
This script demonstrates how to use Axinom Key Server's Widevine CENC API
together with Shaka Packager to package encrypted DASH and HLS content that
can be played back using Widevine, Playready and FairPlay DRMs.
Encrypted content is created using CENC and CBCS encryption schemes. Clear
content is also created.
For more information consult Axinom Key Server documentation and Shaka Packager
online documentation.
#>
###############
# Main script #
###############
$main =
{
# The Shaka Packager executable is expected to be in the current directory.
if ((Get-Variable IsLinux -Scope Global -ErrorAction Ignore) -and ($IsLinux -eq $true))
{
$shakaPackagerExecutable = "./packager-linux";
}
else
{
$shakaPackagerExecutable = "./packager-win.exe";
}
if (!(Test-Path $shakaPackagerExecutable))
{
throw "Shaka Packager executable was not found at: $($shakaPackagerExecutable)"
}
# Let's create two content key requests: one with the CBCS scheme and the other
# with the CENC scheme, to get encryption-scheme specific PSSHs for use with
# the CBCS and CENC content respectively. Since in the current demo we'll use
# the same keys for both content, we can parse the rest of the data we from
# either response.
# Parse the data that will be fed to Shaka Packager. From the "CENC" response
# we're currently only interested in the PSSH.
$cencTrack = $cencContentKeyResponseObject.tracks[0];
$cbcsTrack = $cbcsContentKeyResponseObject.tracks[0];
$keyIdAsBase64 = $cbcsTrack.key_id;
$keyAsBase64 = $cbcsTrack.key;
$ivAsBase64 = $cbcsTrack.iv;
$fairPlaySkdUri = $cbcsTrack.skd_uri;
$widevineCbcsPsshBoxAsHex = "";
$widevineCencPsshBoxAsHex = "";
$playReadyCbcsPsshBoxAsHex = "";
$playReadyCencPsshBoxAsHex = "";
# Step 2. Use information from the Key Server to construct Shaka Packager
# arguments and call the packager.
Write-Host
Write-Host "Running Shaka Packager"
Write-Host "======================"
# Shaka takes several input PSSH boxes as one long concatenated hex string.
$cbcsPsshs = $widevineCbcsPsshBoxAsHex + $playReadyCbcsPsshBoxAsHex;
$cencPsshs = $widevineCencPsshBoxAsHex + $playReadyCencPsshBoxAsHex;
# Create encrypted content using the CBCS encryption scheme (DASH and HLS).
PackageEncryptedContent $InputVideo $outputPath $keyIdAsHex $keyAsHex $cbcsPsshs
"cbcs" $ivAsHex $fairPlaySkdUri;
# Create encrypted content using the CENC encryption scheme (DASH and HLS).
PackageEncryptedContent $InputVideo $outputPath $keyIdAsHex $keyAsHex $cencPsshs
"cenc" $ivAsHex $fairPlaySkdUri;
Write-Host
Write-Host "All done!";
Write-Host
}
#############
# Functions #
#############
function StringToBase64String($string)
{
return [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($string));
}
function Base64StringToString($base64)
{
return [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($base64));
}
function HexStringToBytes($hexString)
{
$bytes = [byte[]]::new($hexString.Length / 2)
return $bytes;
}
function BytesToHexString($bytes)
{
return ($bytes | ForEach-Object ToString x2) -join '';
}
function Base64StringToHexString($base64)
{
return BytesToHexString ([Convert]::FromBase64String($base64));
}
# Encrypt the hash with AES-CBC (PKCS7 padding) using the signing key and IV.
# The result is the signature.
$aes = New-Object System.Security.Cryptography.AesManaged;
$aes.KeySize = 256;
$aes.Mode = [System.Security.Cryptography.CipherMode]::CBC;
$aes.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7;
$aes.Key = HexStringToBytes $signingKeyAsHex;
$aes.Iv = HexStringToBytes $signingIvAsHex;
$encryptor = $aes.CreateEncryptor();
$signature = $encryptor.TransformFinalBlock($requestHash, 0, $requestHash.Length);
$encryptor.Dispose();
$aes.Dispose();
$signatureAsBase64 = [Convert]::ToBase64String($signature);
return $signatureAsBase64;
}
# As the Key Server provides "PSSH data", but Shaka Packager accepts "PSSH boxes",
# conversions must be made.
function PsshDataAsBase64ToPsshBoxAsHex($psshDataAsBase64, $drmType)
{
switch ($drmType)
{
"WIDEVINE" { $systemIdBigEndianBytes = [Byte[]] (0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed); }
"PLAYREADY" { $systemIdBigEndianBytes = [Byte[]] (0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40,
0x42, 0x86, 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95); }
default { throw "PsshDataAsBase64ToPsshBoxAsHex() supports only WIDEVINE and
PLAYREADY DRM types";}
}
$dataBytes = [Convert]::FromBase64String($psshDataAsBase64);
$dataLengthBigEndianBytes = [System.BitConverter]::GetBytes($dataBytes.Length);
[array]::Reverse($dataLengthBigEndianBytes);
$boxBytes = New-Object System.Collections.Generic.List[byte];
$boxBytes.AddRange($boxLengthBigEndianBytes);
$boxBytes.AddRange([Byte[]] (0x70, 0x73, 0x73, 0x68)); # Box type ('p', 's', 's', 'h').
$boxBytes.AddRange([Byte[]] (0x00, 0x00, 0x00, 0x00)); # Version and flags.
$boxBytes.AddRange($systemIdBigEndianBytes);
$boxBytes.AddRange($dataLengthBigEndianBytes);
$boxBytes.AddRange($dataBytes);
return $boxAsHex;
}
function PostKeyServerRequestAndExtractContentKeyResponse($keyServerRequestJson,
$keyServerUrl)
{
# Post the Key Server request to the Key Server.
$httpResponse = Invoke-WebRequest $keyServerUrl -Method "POST" -ContentType
"application/json" -Body $keyServerRequestJson;
Write-Host
Write-Host "Key server response"
Write-Host "==================="
Write-Host
Write-Host ($keyServerResponseObject | ConvertTo-Json -Depth 100)
# Get the content key response from the "response" member of the key server response.
$contentKeyResponseObject = (Base64StringToString
$keyServerResponseObject.response) | ConvertFrom-Json;
Write-Host
Write-Host "Content key response"
Write-Host "===================="
Write-Host
Write-Host ($contentKeyResponseObject | ConvertTo-Json -Depth 100)
$status = $contentKeyResponseObject.status;
Write-Host
Write-Host "Content key request"
Write-Host "==================="
Write-Host
Write-Host $contentKeyRequestJson
return $contentKeyRequestJson;
}
Write-Host
Write-Host "Key server request"
Write-Host "=================="
Write-Host
Write-Host $keyServerRequestJson
return $keyServerRequestJson;
}
$shakaArguments =
@(
"in=$($inputFilePath),stream=audio,output=$($outputPath)/audio.mp4"
"in=$($inputFilePath),stream=video,output=$($outputPath)/video.mp4"
"--mpd_output=$($outputPath)/manifest.mpd"
"--hls_master_playlist_output=$($outputPath)/manifest.m3u8"
);
Write-Host
Write-Host "Packaging clear content...";
Write-Host "--------------------------"
Write-Host
Write-Host "Packager arguments:";
Write-Host
$shakaArguments;
Write-Host
&$shakaPackagerExecutable $shakaArguments;
}
$shakaArguments =
@(
"in=$($inputFilePath),stream=audio,output=$($outputPath)/audio.mp4"
"in=$($inputFilePath),stream=video,output=$($outputPath)/video.mp4"
"--enable_raw_key_encryption"
"--keys=key_id=$($keyIdAsHex):key=$($keyAsHex)"
"--pssh=$($psshsAsHex)"
"--protection_scheme=$($scheme)"
"--mpd_output=$($outputPath)/manifest.mpd"
"--hls_master_playlist_output=$($outputPath)/manifest.m3u8"
"--clear_lead=0"
);
# These are needed only for FairPlay and FairPlay only supports CBCS.
if ($scheme -eq "cbcs")
{
$shakaArguments += "--protection_systems=FairPlay";
$shakaArguments += "--iv=$($ivAsHex)"
$shakaArguments += "--hls_key_uri=$($skdUri)"
}
Write-Host
Write-Host "Packaging encrypted '$($scheme)' content...";
Write-Host "-------------------------------------"
Write-Host
Write-Host "Packager arguments:";
Write-Host
$shakaArguments;
Write-Host
&$shakaPackagerExecutable $shakaArguments;
}
&$main;