direct_play_nice 0.1.0-beta.3

CLI program that converts video files to direct-play-compatible formats.
Documentation
use direct_play_nice::devices::{
    self, plan_output_profile, resolve_target_profile, ContainerFormat, DeviceFamily,
    InputMediaProfile, StreamingDevice,
};
use rsmpeg::ffi;

fn min_video_limit(devices: &[&StreamingDevice]) -> Option<i64> {
    devices.iter().filter_map(|d| d.max_video_bitrate).min()
}

fn min_audio_limit(devices: &[&StreamingDevice]) -> Option<i64> {
    devices.iter().filter_map(|d| d.max_audio_bitrate).min()
}

#[test]
fn test_chromecast_constraints() {
    let selected = devices::devices_for_family(DeviceFamily::Chromecast);
    let input = InputMediaProfile {
        container: Some(ContainerFormat::Mkv),
        video_codec: ffi::AV_CODEC_ID_AV1,
        audio_codec: ffi::AV_CODEC_ID_AC3,
        video_bitrate: Some(80_000_000),
        audio_bitrate: Some(1_500_000),
    };

    let planned = plan_output_profile(&selected, &input).expect("plan should resolve");
    assert_eq!(planned.container, ContainerFormat::Mp4);
    assert_eq!(planned.video_codec, ffi::AV_CODEC_ID_H264);
    assert_eq!(planned.audio_codec, ffi::AV_CODEC_ID_AAC);
    assert!(planned.target_video_bitrate <= min_video_limit(&selected));
    assert!(planned.target_audio_bitrate <= min_audio_limit(&selected));
}

#[test]
fn test_roku_constraints() {
    let selected = devices::devices_for_family(DeviceFamily::Roku);
    let input = InputMediaProfile {
        container: Some(ContainerFormat::Mkv),
        video_codec: ffi::AV_CODEC_ID_HEVC,
        audio_codec: ffi::AV_CODEC_ID_EAC3,
        video_bitrate: Some(90_000_000),
        audio_bitrate: Some(2_000_000),
    };

    let planned = plan_output_profile(&selected, &input).expect("plan should resolve");
    assert_eq!(planned.container, ContainerFormat::Mkv);
    assert_eq!(planned.video_codec, ffi::AV_CODEC_ID_HEVC);
    assert_eq!(planned.audio_codec, ffi::AV_CODEC_ID_EAC3);
    assert!(planned.target_video_bitrate <= min_video_limit(&selected));
    assert!(planned.target_audio_bitrate <= min_audio_limit(&selected));
}

#[test]
fn test_apple_tv_constraints() {
    let selected = devices::devices_for_family(DeviceFamily::AppleTv);
    let input = InputMediaProfile {
        container: Some(ContainerFormat::Mkv),
        video_codec: ffi::AV_CODEC_ID_HEVC,
        audio_codec: ffi::AV_CODEC_ID_EAC3,
        video_bitrate: Some(70_000_000),
        audio_bitrate: Some(2_000_000),
    };

    let planned = plan_output_profile(&selected, &input).expect("plan should resolve");
    assert_eq!(planned.container, ContainerFormat::Mp4);
    assert_eq!(planned.video_codec, ffi::AV_CODEC_ID_H264);
    assert_eq!(planned.audio_codec, ffi::AV_CODEC_ID_EAC3);
    assert!(planned.target_video_bitrate <= min_video_limit(&selected));
    assert!(planned.target_audio_bitrate <= min_audio_limit(&selected));
}

#[test]
fn test_fire_tv_constraints() {
    let selected = devices::devices_for_family(DeviceFamily::FireTv);
    let input = InputMediaProfile {
        container: Some(ContainerFormat::Mkv),
        video_codec: ffi::AV_CODEC_ID_AV1,
        audio_codec: ffi::AV_CODEC_ID_FLAC,
        video_bitrate: Some(80_000_000),
        audio_bitrate: Some(2_000_000),
    };

    let planned = plan_output_profile(&selected, &input).expect("plan should resolve");
    assert_eq!(planned.container, ContainerFormat::Mkv);
    assert_eq!(planned.video_codec, ffi::AV_CODEC_ID_AV1);
    assert_eq!(planned.audio_codec, ffi::AV_CODEC_ID_FLAC);
    assert!(planned.target_video_bitrate <= min_video_limit(&selected));
    assert!(planned.target_audio_bitrate <= min_audio_limit(&selected));
}

#[test]
fn test_all_devices_constraints() {
    let selected: Vec<&StreamingDevice> = devices::STREAMING_DEVICES.iter().collect();
    let all_profile = resolve_target_profile(&selected).expect("all profile should resolve");

    for device in devices::STREAMING_DEVICES {
        assert!(
            all_profile.is_compatible_with_device(device),
            "all-device profile must be valid for {}",
            device.model
        );
    }

    let input = InputMediaProfile {
        container: Some(ContainerFormat::Mkv),
        video_codec: ffi::AV_CODEC_ID_AV1,
        audio_codec: ffi::AV_CODEC_ID_DTS,
        video_bitrate: Some(90_000_000),
        audio_bitrate: Some(2_000_000),
    };
    let planned = plan_output_profile(&selected, &input).expect("plan should resolve");

    for device in devices::STREAMING_DEVICES {
        assert!(
            device.containers.contains(&planned.container),
            "planned container must be valid for {}",
            device.model
        );
        assert!(
            device.video_codecs.contains(&planned.video_codec),
            "planned video codec must be valid for {}",
            device.model
        );
        assert!(
            device.audio_codecs.contains(&planned.audio_codec),
            "planned audio codec must be valid for {}",
            device.model
        );
        if let (Some(target), Some(limit)) =
            (planned.target_video_bitrate, device.max_video_bitrate)
        {
            assert!(
                target <= limit,
                "video bitrate target exceeds device limit for {}",
                device.model
            );
        }
        if let (Some(target), Some(limit)) =
            (planned.target_audio_bitrate, device.max_audio_bitrate)
        {
            assert!(
                target <= limit,
                "audio bitrate target exceeds device limit for {}",
                device.model
            );
        }
    }
}