pub mod common;
use fs_err as fs;
use std::env;
use std::process::Command;
use ffprobe::ffprobe;
use file_format::FileFormat;
use assert_cmd::cargo::cargo_bin_cmd;
use assert_fs::{prelude::*, TempDir};
use test_log::test;
use common::check_file_size_approx;
#[test]
fn test_decryption_widevine_cenc () {
if env::var("CI").is_ok() {
return;
}
let mpd = "https://refapp.hbbtv.org/videos/spring_h265_v8/cenc/manifest_wvcenc.mpd";
let tmpd = TempDir::new().unwrap()
.into_persistent_if(env::var("TEST_PERSIST_FILES").is_ok());
let out = tmpd.child("spring.mp4");
cargo_bin_cmd!()
.args(["-v",
"--quality", "worst",
"--key", "43215678123412341234123412341237:12341234123412341234123412341237",
"--key", "43215678123412341234123412341236:12341234123412341234123412341236",
"-o", &out.to_string_lossy(), mpd])
.assert()
.success();
check_file_size_approx(&out, 33_746_341);
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
}
#[test]
fn test_decryption_widevine_cbcs () {
if env::var("CI").is_ok() {
return;
}
let mpd = "https://refapp.hbbtv.org/videos/tears_of_steel_h265_v8/cbcs/manifest_wvcenc.mpd";
let tmpd = TempDir::new().unwrap()
.into_persistent_if(env::var("TEST_PERSIST_FILES").is_ok());
let out = tmpd.child("tears-steel.mp4");
cargo_bin_cmd!()
.args(["-v",
"--quality", "worst",
"--key", "43215678123412341234123412341237:12341234123412341234123412341237",
"--key", "43215678123412341234123412341236:12341234123412341234123412341236",
"-o", &out.to_string_lossy(), mpd])
.assert()
.success();
check_file_size_approx(&out, 79_731_116);
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
}
#[test]
fn test_decryption_playready_cenc () {
if env::var("CI").is_ok() {
return;
}
let mpd = "https://refapp.hbbtv.org/videos/00_llama_h264_v8_8s/cenc/manifest_prcenc.mpd";
let tmpd = TempDir::new().unwrap()
.into_persistent_if(env::var("TEST_PERSIST_FILES").is_ok());
let out = tmpd.child("llama.mp4");
cargo_bin_cmd!()
.args(["-v",
"--quality", "worst",
"--key", "43215678123412341234123412341236:12341234123412341234123412341236",
"-o", &out.to_string_lossy(), mpd])
.assert()
.success();
check_file_size_approx(&out, 26_420_624);
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
}
#[test]
fn test_decryption_marlin_cenc () {
if env::var("CI").is_ok() {
return;
}
let mpd = "https://refapp.hbbtv.org/videos/agent327_h264_v8/cenc/manifest_mlcenc.mpd";
let tmpd = TempDir::new().unwrap()
.into_persistent_if(env::var("TEST_PERSIST_FILES").is_ok());
let out = tmpd.child("llama-cenc.mp4");
cargo_bin_cmd!()
.args(["-v",
"--quality", "worst",
"--key", "43215678123412341234123412341234:12341234123412341234123412341234",
"-o", &out.to_string_lossy(), mpd])
.assert()
.success();
check_file_size_approx(&out, 14_357_917);
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
}
#[test]
fn test_decryption_marlin_cbcs () {
if env::var("CI").is_ok() {
return;
}
let mpd = "https://refapp.hbbtv.org/videos/agent327_h264_v8/cbcs/manifest_mlcenc.mpd";
let tmpd = TempDir::new().unwrap()
.into_persistent_if(env::var("TEST_PERSIST_FILES").is_ok());
let out = tmpd.child("llama-cbcs.mp4");
cargo_bin_cmd!()
.args(["-v",
"--quality", "worst",
"--key", "43215678123412341234123412341234:12341234123412341234123412341234",
"-o", &out.to_string_lossy(), mpd])
.assert()
.success();
check_file_size_approx(&out, 14_357_925);
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
}
#[test]
fn test_decryption_cmaf_h265_multikey () {
if env::var("CI").is_ok() {
return;
}
let mpd = "https://media.axprod.net/TestVectors/H265/protected_cmaf_1080p_h265_multikey/manifest.mpd";
let tmpd = TempDir::new().unwrap()
.into_persistent_if(env::var("TEST_PERSIST_FILES").is_ok());
let out = tmpd.child("axinom-h264-multikey.mp4");
cargo_bin_cmd!()
.args(["-v",
"--quality", "worst",
"--key", "53dc3eaa5164410a8f4ee15113b43040:620045a34e839061ee2e9b7798fdf89b",
"--key", "9dbace9e41034c5296aa63227dc5f773:a776f83276a107a3c322f9dbd6d4f48c",
"--key", "a76f0ca68e7d40d08a37906f3e24dde2:2a99b42f08005ab4b57af20f4da3cc05",
"-o", &out.to_string_lossy(), mpd])
.assert()
.success();
check_file_size_approx(&out, 48_233_447);
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
}
#[ignore]
#[test]
fn test_decryption_cenc_kaltura () {
let mpd = "https://cdnapisec.kaltura.com/p/2433871/sp/243387100/playManifest/protocol/https//entryId/1_pgssezc1/format/mpegdash/tags/mbr/f/a.mpd";
let tmpd = TempDir::new().unwrap()
.into_persistent_if(env::var("TEST_PERSIST_FILES").is_ok());
let out = tmpd.child("kaltura.mp4");
cargo_bin_cmd!()
.args(["--mp4decrypt-location", "mp4decrypt",
"--key", "a07c5d499dcead0fb416fed5913967be:caee457911302478487e6680bf0b3d1b",
"-o", &out.to_string_lossy(), mpd])
.assert()
.success();
check_file_size_approx(&out, 1_323_079);
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::Mpeg4Part14Video);
let meta = ffprobe(out).unwrap();
assert_eq!(meta.streams.len(), 2);
let audio = meta.streams.iter()
.find(|s| s.codec_type.eq(&Some(String::from("audio"))))
.expect("finding audio stream");
assert_eq!(audio.codec_name, Some(String::from("aac")));
assert!(audio.width.is_none());
let tags = audio.tags.as_ref().unwrap();
assert_eq!(tags.language, Some(String::from("eng")));
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
}
#[test]
fn test_decryption_webm() {
let mpd = "https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine/dash.mpd";
let tmpd = TempDir::new().unwrap()
.into_persistent_if(env::var("TEST_PERSIST_FILES").is_ok());
let out = tmpd.child("angel.webm");
let cli = cargo_bin_cmd!()
.args(["-v",
"--quality", "worst",
"--decryption-application", "shaka",
"--key", "4d97930a3d7b55fa81d0028653f5e499:429ec76475e7a952d224d8ef867f12b6",
"--key", "d21373c0b8ab5ba9954742bcdfb5f48b:150a6c7d7dee6a91b74dccfce5b31928",
"--key", "6f1729072b4a5cd288c916e11846b89e:a84b4bd66901874556093454c075e2c6",
"--key", "800aacaa522958ae888062b5695db6bf:775dbf7289c4cc5847becd571f536ff2",
"--key", "67b30c86756f57c5a0a38a23ac8c9178:efa2878c2ccf6dd47ab349fcf90e6259",
"-o", &out.to_string_lossy(), mpd])
.output()
.expect("failed spawning cli");
let msg = String::from_utf8_lossy(&cli.stdout);
if !msg.is_empty() {
println!("dash-mpd-cli stdout: {msg}");
}
let msg = String::from_utf8_lossy(&cli.stderr);
if !msg.is_empty() {
println!("dash-mpd-cli stderr: {msg}");
}
assert!(cli.status.success());
check_file_size_approx(&out, 1_331_284);
let meta = ffprobe(&out).unwrap();
assert_eq!(meta.streams.len(), 2);
let audio = meta.streams.iter()
.find(|s| s.codec_type.eq(&Some(String::from("audio"))))
.expect("finding audio stream");
assert!(audio.codec_name.eq(&Some(String::from("vorbis"))) ||
audio.codec_name.eq(&Some(String::from("opus"))));
let video = meta.streams.iter()
.find(|s| s.codec_type.eq(&Some(String::from("video"))))
.expect("finding video stream");
assert_eq!(video.codec_name, Some(String::from("vp9")));
assert!(video.width.is_some());
let ffmpeg = Command::new("ffmpeg")
.args(["-nostdin",
"-v", "error",
"-i", &out.to_string_lossy(),
"-f", "null", "-"])
.output()
.expect("spawning ffmpeg");
let msg = String::from_utf8_lossy(&ffmpeg.stderr);
if !msg.is_empty() {
eprintln!("FFMPEG stderr {msg}");
}
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
}
#[test]
fn test_decryption_small_shaka () {
let mpd = "https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine/dash.mpd";
let tmpd = TempDir::new().unwrap()
.into_persistent_if(env::var("TEST_PERSIST_FILES").is_ok());
let out = tmpd.child("cf-shaka.mp4");
let cli = cargo_bin_cmd!()
.args(["-v",
"--quality", "worst",
"--decryption-application", "shaka",
"--key", "4d97930a3d7b55fa81d0028653f5e499:429ec76475e7a952d224d8ef867f12b6",
"--key", "d21373c0b8ab5ba9954742bcdfb5f48b:150a6c7d7dee6a91b74dccfce5b31928",
"--key", "6f1729072b4a5cd288c916e11846b89e:a84b4bd66901874556093454c075e2c6",
"--key", "800aacaa522958ae888062b5695db6bf:775dbf7289c4cc5847becd571f536ff2",
"--key", "67b30c86756f57c5a0a38a23ac8c9178:efa2878c2ccf6dd47ab349fcf90e6259",
"-o", &out.to_string_lossy(), mpd])
.output()
.expect("failed spawning cli");
let msg = String::from_utf8_lossy(&cli.stdout);
if !msg.is_empty() {
println!("dash-mpd-cli stdout: {msg}");
}
let msg = String::from_utf8_lossy(&cli.stderr);
if !msg.is_empty() {
println!("dash-mpd-cli stderr: {msg}");
}
assert!(cli.status.success());
check_file_size_approx(&out, 1_316_391);
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::Mpeg4Part14Video);
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
}