smooth-runtime 0.1.0

Sans-IO Smooth Streaming (MS-SSTR) engine: a pull client (manifest fetch/refresh, stream/quality selection, chunk enumeration, fragment URLs, init/tfxd handling, retry/backoff, live join) and a live origin that renders the client Manifest and serves fragments from a media-plane Trunk. No I/O, no clock reads.
Documentation
use smooth_runtime::{Action, Output, SmoothConfig};
use transmux::TrackSpec;

use crate::common::{Host, fixture, is_ended, is_fatal};

const SECOND_VIDEO_BITRATE: &str = "99999";
const FIRST_FRAGMENT: &str = "Fragments(video=0)";
const FIRST_BITRATE: &str = "64528";

fn two_quality_manifest() -> Vec<u8> {
    let text = String::from_utf8(fixture("av/Manifest")).expect("utf8");
    let first = text
        .lines()
        .find(|l| l.contains("Bitrate=\"64528\""))
        .expect("video quality level");
    let second = first
        .replace("64528", SECOND_VIDEO_BITRATE)
        .replace("Index=\"0\"", "Index=\"1\"");
    text.replace(first, &format!("{first}\n{second}"))
        .into_bytes()
}

fn host_after_manifest() -> Host {
    let mut host = Host::new(SmoothConfig::default());
    host.serve_dir("av");
    let Some(Action::FetchManifest { id, .. }) = host.client.poll(0) else {
        panic!("first action is the manifest fetch");
    };
    host.client.on_manifest(0, id, &two_quality_manifest());
    host
}

#[test]
fn a_selected_quality_names_the_later_fragment_urls() {
    let mut host = host_after_manifest();
    assert!(host.client.select_video_quality(1));
    host.run_until(|o| o.iter().any(|o| is_fatal(o) || is_ended(o)));
    let selected = format!("QualityLevels({SECOND_VIDEO_BITRATE})/{FIRST_FRAGMENT}");
    let original = format!("QualityLevels({FIRST_BITRATE})/{FIRST_FRAGMENT}");
    assert!(host.count_requests(&selected) > 0);
    assert_eq!(host.count_requests(&original), 0);
}

#[test]
fn an_unknown_quality_index_is_refused() {
    let mut host = host_after_manifest();
    assert!(!host.client.select_video_quality(2));
}

const SKIPPED_STREAM: &str = "<StreamIndex Type=\"video\" TimeScale=\"0\" QualityLevels=\"1\" Chunks=\"1\" Url=\"Skipped({bitrate})/Fragments(video={start time})\">\n<QualityLevel Index=\"0\" Bitrate=\"11111\" FourCC=\"H264\" MaxWidth=\"320\" MaxHeight=\"240\" CodecPrivateData=\"00\" />\n<c n=\"0\" d=\"10000000\" />\n</StreamIndex>\n";

const LOW_LEVEL_BITRATE: &str = "30000";
const FIRST_LEVEL_WIDTH: u32 = 320;
const LOW_LEVEL_WIDTH: u32 = 160;

fn ladder_manifest(second_level: &str) -> Vec<u8> {
    let text = String::from_utf8(fixture("av/Manifest")).expect("utf8");
    let first = text
        .lines()
        .find(|l| l.contains("Bitrate=\"64528\""))
        .expect("video quality level");
    text.replace(first, &format!("{first}\n{second_level}"))
        .into_bytes()
}

fn real_low_level() -> String {
    String::from_utf8(fixture("av-low/Manifest"))
        .expect("utf8")
        .lines()
        .find(|l| l.contains(&format!("Bitrate=\"{LOW_LEVEL_BITRATE}\"")))
        .expect("low quality level")
        .replace("Index=\"0\"", "Index=\"1\"")
}

fn announced_host(manifest: &[u8]) -> Host {
    let mut host = Host::new(SmoothConfig::default());
    host.serve_dir("av");
    host.serve_dir("av-low");
    let Some(Action::FetchManifest { id, .. }) = host.client.poll(0) else {
        panic!("first action is the manifest fetch");
    };
    host.client.on_manifest(0, id, manifest);
    while !host.has(|o| matches!(o, Output::Tracks(_))) {
        assert!(host.step(), "tracks are announced");
    }
    host
}

fn coded_width(spec: &TrackSpec) -> u32 {
    spec.display.as_ref().expect("display geometry").coded_width
}

#[test]
fn a_ladder_with_a_different_resolution_switches_and_re_announces_before_its_samples() {
    let mut host = announced_host(&ladder_manifest(&real_low_level()));
    let announced = host
        .outputs
        .iter()
        .find_map(|o| match o {
            Output::Tracks(specs) => Some(specs.clone()),
            _ => None,
        })
        .expect("tracks");
    assert_eq!(coded_width(&announced[0]), FIRST_LEVEL_WIDTH);
    assert!(host.client.select_video_quality(1));
    host.run_until(|o| o.iter().any(|o| is_fatal(o) || is_ended(o)));
    let changed: Vec<usize> = (0..host.outputs.len())
        .filter(|&i| matches!(host.outputs[i], Output::TracksChanged(_)))
        .collect();
    assert_eq!(changed.len(), 1);
    let Output::TracksChanged(specs) = &host.outputs[changed[0]] else {
        panic!("tracks changed");
    };
    assert_eq!(specs.len(), announced.len());
    assert_eq!(specs[0].track_id, announced[0].track_id);
    assert_eq!(coded_width(&specs[0]), LOW_LEVEL_WIDTH);
    let samples_after = host.outputs[changed[0]..]
        .iter()
        .filter(|o| matches!(o, Output::Sample { track, .. } if *track == announced[0].track_id))
        .count();
    let samples_before = host.outputs[..changed[0]]
        .iter()
        .filter(|o| matches!(o, Output::Sample { track, .. } if *track == announced[0].track_id))
        .count();
    assert!(samples_before > 0);
    assert!(samples_after > 0);
    let low = format!("QualityLevels({LOW_LEVEL_BITRATE})/{FIRST_FRAGMENT}");
    assert_eq!(host.count_requests(&low), 0);
    assert!(
        host.count_requests(&format!(
            "QualityLevels({LOW_LEVEL_BITRATE})/Fragments(video=20000000)"
        )) > 0
    );
}

#[test]
fn a_level_of_another_codec_family_is_refused_once_announced() {
    let other_family = real_low_level().replace("H264", "H265");
    let mut host = announced_host(&ladder_manifest(&other_family));
    assert!(!host.client.select_video_quality(1));
    host.run_until(|o| o.iter().any(|o| is_fatal(o) || is_ended(o)));
    assert!(!host.has(|o| matches!(o, Output::TracksChanged(_))));
}

#[test]
fn a_level_with_the_same_config_switches_without_re_announcing() {
    let mut host = announced_host(&two_quality_manifest());
    assert!(host.client.select_video_quality(1));
    host.run_until(|o| o.iter().any(|o| is_fatal(o) || is_ended(o)));
    assert!(!host.has(|o| matches!(o, Output::TracksChanged(_))));
}
#[test]
fn the_offered_levels_index_the_stream_that_select_video_quality_switches() {
    let manifest = String::from_utf8(two_quality_manifest())
        .expect("utf8")
        .replace(
            "<StreamIndex Type=\"video\" QualityLevels=\"1\"",
            &format!("{SKIPPED_STREAM}<StreamIndex Type=\"video\" QualityLevels=\"1\""),
        );
    let mut host = Host::new(SmoothConfig::default());
    host.serve_dir("av");
    let Some(Action::FetchManifest { id, .. }) = host.client.poll(0) else {
        panic!("first action is the manifest fetch");
    };
    host.client.on_manifest(0, id, manifest.as_bytes());
    assert_eq!(host.client.video_quality_count(), 2);
    assert!(host.client.select_video_quality(1));
    host.run_until(|o| o.iter().any(|o| is_fatal(o) || is_ended(o)));
    let selected = format!("QualityLevels({SECOND_VIDEO_BITRATE})/{FIRST_FRAGMENT}");
    assert!(host.count_requests(&selected) > 0);
}