pub mod common;
use std::fs;
use std::env;
use std::time::Duration;
use ffprobe::ffprobe;
use file_format::FileFormat;
use dash_mpd::fetch::DashDownloader;
use common::{check_file_size_approx, check_media_duration, setup_logging};
#[tokio::test]
#[cfg(not(feature = "libav"))]
async fn test_concat_noaudio_ffmpeg() {
setup_logging();
if env::var("CI").is_ok() {
return;
}
let mpd_url = "https://dash.akamaized.net/fokus/adinsertion-samples/xlink/twoperiodsOR.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-noaudio-ffmpeg.mp4");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.with_concat_preference("mp4", "ffmpeg")
.download_to(&out).await
.unwrap();
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::Mpeg4Part14Video);
check_file_size_approx(&out, 5_781_840);
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")));
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("h264")));
assert!(video.width.is_some());
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
async fn test_concat_noaudio_ffmpegdemuxer() {
setup_logging();
if env::var("CI").is_ok() {
return;
}
let mpd_url = "https://dash.akamaized.net/fokus/adinsertion-samples/xlink/twoperiodsOR.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-noaudio-ffmpegdemuxer.mp4");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.with_concat_preference("mp4", "ffmpegdemuxer")
.download_to(&out).await
.unwrap();
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::Mpeg4Part14Video);
check_file_size_approx(&out, 5_781_840);
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")));
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("h264")));
assert!(video.width.is_some());
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
#[should_panic(expected = "all concat helpers failed")]
async fn test_concat_noaudio_mkvmerge_mp4() {
setup_logging();
if env::var("CI").is_ok() {
panic!("all concat helpers failed");
}
let mpd_url = "https://dash.akamaized.net/fokus/adinsertion-samples/xlink/twoperiodsOR.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-noaudio-mkvmerge.mp4");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.with_concat_preference("mp4", "mkvmerge")
.download_to(&out).await
.unwrap();
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
async fn test_concat_noaudio_mkv_concat_fallback() {
setup_logging();
if env::var("CI").is_ok() {
return;
}
let mpd_url = "https://dash.akamaized.net/fokus/adinsertion-samples/xlink/twoperiodsOR.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-noaudio-mkvmerge.mkv");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.with_concat_preference("mp4", "mkvmerge")
.with_concat_preference("mkv", "mkvmerge,ffmpeg")
.download_to(&out).await
.unwrap();
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::MatroskaVideo);
check_file_size_approx(&out, 7_258_379);
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("vorbis")));
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("h264")));
assert!(video.width.is_some());
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
async fn test_concat_singleases_ffmpeg() {
setup_logging();
if env::var("CI").is_ok() {
return;
}
let mpd_url = "https://dash.akamaized.net/fokus/adinsertion-samples/xlink/singleases.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-singleases-ffmpeg.mp4");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.with_concat_preference("mp4", "ffmpeg")
.minimum_period_duration(Duration::new(10, 0))
.download_to(&out).await
.unwrap();
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::Mpeg4Part14Video);
check_file_size_approx(&out, 5_781_840);
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")));
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("h264")));
assert!(video.width.is_some());
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
async fn test_concat_singleases_ffmpegdemuxer() {
setup_logging();
if env::var("CI").is_ok() {
return;
}
let mpd_url = "https://dash.akamaized.net/fokus/adinsertion-samples/xlink/singleases.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-singleases-ffmpeg.mp4");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.with_concat_preference("mp4", "ffmpegdemuxer")
.minimum_period_duration(Duration::new(10, 0))
.download_to(&out).await
.unwrap();
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::Mpeg4Part14Video);
check_file_size_approx(&out, 5_781_840);
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")));
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("h264")));
assert!(video.width.is_some());
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
#[should_panic(expected = "all concat helpers failed")]
async fn test_concat_singleases_mkvmerge() {
setup_logging();
if env::var("CI").is_ok() {
panic!("all concat helpers failed");
}
let mpd_url = "https://dash.akamaized.net/fokus/adinsertion-samples/xlink/singleases.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-singleases-mkvmerge.mp4");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.with_concat_preference("mp4", "mkvmerge")
.minimum_period_duration(Duration::new(10, 0))
.download_to(&out).await
.unwrap();
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
async fn test_concat_heliocentrism_ffmpeg_mp4() {
setup_logging();
let mpd_url = "https://storage.googleapis.com/shaka-demo-assets/heliocentrism/heliocentrism.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-helio-ffmpeg.mp4");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.with_concat_preference("mp4", "ffmpeg")
.download_to(&out).await
.unwrap();
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::Mpeg4Part14Video);
check_file_size_approx(&out, 36_829);
let meta = ffprobe(&out).unwrap();
assert_eq!(meta.streams.len(), 1);
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("h264")));
assert!(video.width.is_some());
check_media_duration(&out, 4.9);
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
async fn test_concat_heliocentrism_ffmpegdemuxer_mp4() {
setup_logging();
let mpd_url = "https://storage.googleapis.com/shaka-demo-assets/heliocentrism/heliocentrism.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-helio-ffmpeg-demuxer.mp4");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.with_concat_preference("mp4", "ffmpegdemuxer")
.download_to(&out).await
.unwrap();
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::Mpeg4Part14Video);
let meta = ffprobe(&out).unwrap();
assert_eq!(meta.streams.len(), 1);
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("h264")));
assert!(video.width.is_some());
check_media_duration(&out, 4.9);
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
async fn test_concat_heliocentrism_ffmpeg_mkv() {
setup_logging();
let mpd_url = "https://storage.googleapis.com/shaka-demo-assets/heliocentrism/heliocentrism.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-helio-ffmpeg.mkv");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.verbosity(3)
.with_muxer_preference("mkv", "ffmpeg")
.with_concat_preference("mkv", "ffmpeg")
.download_to(&out).await
.unwrap();
let fp = std::process::Command::new("ffprobe")
.env("LANG", "C")
.arg("-hide_banner")
.arg(out.to_str().unwrap())
.output()
.expect("spawning ffprobe");
let stdout = String::from_utf8_lossy(&fp.stdout);
if !stdout.is_empty() {
println!("ffprobe stdout> {stdout}");
}
let stderr = String::from_utf8_lossy(&fp.stderr);
if !stderr.is_empty() {
println!("ffprobe stderr> {stderr}");
}
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::MatroskaVideo);
check_file_size_approx(&out, 35_937);
let meta = ffprobe(&out).unwrap();
assert_eq!(meta.streams.len(), 1);
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("h264")));
assert!(video.width.is_some());
check_media_duration(&out, 4.9);
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
async fn test_concat_heliocentrism_ffmpegdemuxer_mkv() {
setup_logging();
let mpd_url = "https://storage.googleapis.com/shaka-demo-assets/heliocentrism/heliocentrism.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-helio-ffmpeg.mkv");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.verbosity(2)
.with_muxer_preference("mkv", "ffmpeg")
.with_concat_preference("mkv", "ffmpegdemuxer")
.download_to(&out).await
.unwrap();
let fp = std::process::Command::new("ffprobe")
.env("LANG", "C")
.arg("-hide_banner")
.arg(out.to_str().unwrap())
.output()
.expect("spawning ffprobe");
let stdout = String::from_utf8_lossy(&fp.stdout);
if !stdout.is_empty() {
println!("ffprobe stdout> {stdout}");
}
let stderr = String::from_utf8_lossy(&fp.stderr);
if !stderr.is_empty() {
println!("ffprobe stderr> {stderr}");
}
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::MatroskaVideo);
let meta = ffprobe(&out).unwrap();
assert_eq!(meta.streams.len(), 1);
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("h264")));
assert!(video.width.is_some());
check_media_duration(&out, 4.9);
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}
#[ignore]
#[tokio::test]
#[cfg(not(feature = "libav"))]
#[should_panic(expected = "all concat helpers failed")]
async fn test_concat_heliocentrism_mkvmerge_mp4() {
setup_logging();
let mpd_url = "https://storage.googleapis.com/shaka-demo-assets/heliocentrism/heliocentrism.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-helio-mkvmerge.mp4");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.verbosity(2)
.with_concat_preference("mp4", "mkvmerge")
.download_to(&out).await
.unwrap();
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::Mpeg4Part14Video);
check_file_size_approx(&out, 42_060);
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")));
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("h264")));
assert!(video.width.is_some());
check_media_duration(&out, 4.9);
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
async fn test_concat_heliocentrism_mkvmerge_mkv() {
setup_logging();
let mpd_url = "https://storage.googleapis.com/shaka-demo-assets/heliocentrism/heliocentrism.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-helio-mkvmerge.mkv");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.verbosity(3)
.with_concat_preference("mkv", "mkvmerge")
.download_to(&out).await
.unwrap();
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::MatroskaVideo);
let meta = ffprobe(&out).unwrap();
assert_eq!(meta.streams.len(), 1);
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("h264")));
assert!(video.width.is_some());
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
async fn test_concat_heliocentrism_p1p2() {
setup_logging();
let mpd_url = "https://storage.googleapis.com/shaka-demo-assets/heliocentrism/heliocentrism.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-helio_p1p2.mp4");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.with_concat_preference("mp4", "ffmpeg")
.minimum_period_duration(Duration::new(2, 0))
.download_to(&out).await
.unwrap();
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::Mpeg4Part14Video);
check_file_size_approx(&out, 30_496);
let meta = ffprobe(out).unwrap();
assert_eq!(meta.streams.len(), 1);
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("h264")));
assert!(video.width.is_some());
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
async fn test_concat_dashif_5bnomor2() {
setup_logging();
if env::var("CI").is_ok() {
return;
}
let mpd_url = "https://dash.akamaized.net/dash264/TestCases/5b/nomor/2.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-5bnomor2.mp4");
DashDownloader::new(mpd_url)
.worst_quality()
.sandbox(true)
.with_concat_preference("mp4", "ffmpegdemuxer")
.download_to(&out).await
.unwrap();
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::Mpeg4Part14Video);
check_file_size_approx(&out, 119_710_971);
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")));
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("h264")));
assert!(video.width.is_some());
check_media_duration(&out, 710.0);
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}
#[tokio::test]
#[cfg(not(feature = "libav"))]
async fn test_concat_axinom_multiperiod() {
setup_logging();
if env::var("CI").is_ok() {
return;
}
let mpd_url = "https://media.axprod.net/TestVectors/v7-MultiDRM-MultiKey-MultiPeriod/Manifest.mpd";
let tmpd = tempfile::tempdir().unwrap();
let out = tmpd.path().join("concat-axinom-multiperiod.mp4");
DashDownloader::new(mpd_url)
.verbosity(2)
.sandbox(true)
.worst_quality()
.add_decryption_key(String::from("0872786ef9e7465fa3a24e5b0ef8fa45"),
String::from("c3261179bab61eeec979d2d4069511cf"))
.add_decryption_key(String::from("29f05e8fa1ae46e480e922dcd44cd7a1"),
String::from("0711b17c84a90cbb41097264c901b732"))
.with_concat_preference("mp4", "ffmpegdemuxer")
.download_to(&out).await
.unwrap();
let fp = std::process::Command::new("ffprobe")
.env("LANG", "C")
.arg("-hide_banner")
.arg(out.to_str().unwrap())
.output()
.expect("spawning ffprobe");
let stdout = String::from_utf8_lossy(&fp.stdout);
if !stdout.is_empty() {
println!("ffprobe stdout> {stdout}");
}
let stderr = String::from_utf8_lossy(&fp.stderr);
if !stderr.is_empty() {
println!("ffprobe stderr> {stderr}");
}
let mi = std::process::Command::new("mediainfo")
.env("LANG", "C")
.arg(out.to_str().unwrap())
.output()
.expect("spawning mediainfo");
let stdout = String::from_utf8_lossy(&mi.stdout);
if !stdout.is_empty() {
println!("mediainfo stdout> {stdout}");
}
let stderr = String::from_utf8_lossy(&mi.stderr);
if !stderr.is_empty() {
println!("mediainfo stderr> {stderr}");
}
let format = FileFormat::from_file(&out).unwrap();
assert_eq!(format, FileFormat::Mpeg4Part14Video);
check_file_size_approx(&out, 83_015_660);
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")));
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("h264")));
assert!(video.width.is_some());
if ! env::consts::OS.eq("windows") {
check_media_duration(&out, 1468.5);
}
let entries = fs::read_dir(tmpd.path()).unwrap();
let count = entries.count();
assert_eq!(count, 1, "Expecting a single output file, got {count}");
let _ = fs::remove_dir_all(tmpd);
}