use std::time::Duration;
use clap::Args as ClapArgs;
use moq_mux::catalog::CatalogFormat;
use crate::subscribe::{CatalogFormatArg, SelectArgs};
#[derive(ClapArgs, Clone)]
pub struct Args {
#[arg(long)]
pub catalog_format: Option<CatalogFormatArg>,
#[arg(long, default_value = "500ms", value_parser = humantime::parse_duration)]
pub latency_max: Duration,
#[command(flatten)]
pub select: SelectArgs,
}
impl Args {
pub(super) fn catalog_format(&self, broadcast: &str) -> CatalogFormat {
self.catalog_format
.map(Into::into)
.or_else(|| CatalogFormat::detect(broadcast))
.unwrap_or_default()
}
pub fn validate(&self) -> anyhow::Result<()> {
use crate::subscribe::VideoCodecArg;
anyhow::ensure!(
!matches!(self.select.video_codec, Some(VideoCodecArg::Vp8 | VideoCodecArg::Vp9)),
"`play` cannot decode vp8 or vp9; pass --video-codec h264, h265, or av1"
);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
#[derive(Parser)]
struct Cli {
#[command(flatten)]
args: Args,
}
fn parse(flags: &[&str]) -> Args {
let mut argv = vec!["play"];
argv.extend_from_slice(flags);
Cli::try_parse_from(argv).expect("parse the play flags").args
}
#[test]
fn undecodable_video_codecs_are_refused() {
parse(&[]).validate().unwrap();
parse(&["--video-codec", "h264"]).validate().unwrap();
parse(&["--video-codec", "av1"]).validate().unwrap();
let err = parse(&["--video-codec", "vp9"]).validate().unwrap_err().to_string();
assert!(err.contains("vp8 or vp9"), "{err}");
assert!(parse(&["--video-codec", "vp8"]).validate().is_err());
}
#[test]
fn the_catalog_format_follows_the_broadcast_suffix() {
assert_eq!(parse(&[]).catalog_format("room.hang"), CatalogFormat::Hang);
assert_eq!(parse(&[]).catalog_format("room.msf"), CatalogFormat::Msf);
assert_eq!(parse(&[]).catalog_format("room"), CatalogFormat::DEFAULT);
assert_eq!(
parse(&["--catalog-format", "hangz"]).catalog_format("room.msf"),
CatalogFormat::HangZ
);
}
}