use std::process;
use std::time::Duration;
use dash_mpd::parse;
use dash_mpd::{MPD, is_audio_adaptation, is_video_adaptation};
use clap::Arg;
use anyhow::{Context, Result};
use tracing_subscriber::EnvFilter;
use tracing_subscriber::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
let fmt_layer = tracing_subscriber::fmt::layer()
.compact();
let filter_layer = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new("info,reqwest=warn"))
.unwrap();
tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.init();
let matches = clap::Command::new("dash_stream_info")
.about("Show codec and bandwidth for audio and video streams specified in a DASH MPD")
.arg(Arg::new("url")
.num_args(1)
.value_name("URL")
.help("URL of the MPD manifest")
.index(1)
.required(true))
.get_matches();
let url = matches.get_one::<String>("url").unwrap();
let client = reqwest::Client::builder()
.timeout(Duration::new(30, 0))
.build()
.context("creating HTTP client")?;
let xml = client.get(url)
.header("Accept", "application/dash+xml,video/vnd.mpeg.dash.mpd")
.header("Accept-language", "en-US,en")
.send().await
.context("requesting DASH MPD")?
.error_for_status()
.context("requesting DASH MPD")?
.text().await
.context("fetching MPD content")?;
let mpd: MPD = parse(&xml).context("parsing MPD content")?;
if mpd.periods.is_empty() {
println!("MPD file contains zero Period elements");
process::exit(2);
}
let period = &mpd.periods[0];
let unspecified = "<unspecified>".to_string();
if let Some(audio_adaptation) = period.adaptations.iter().find(is_audio_adaptation) {
for representation in audio_adaptation.representations.iter() {
let codec = representation.codecs.as_ref()
.unwrap_or_else(|| audio_adaptation.codecs.as_ref()
.unwrap_or(&unspecified));
let bw = match representation.bandwidth {
Some(b) => b.to_string(),
None => "<unspecified>".to_string(),
};
println!("Audio stream with codec {codec}, bandwidth {bw}");
}
}
if let Some(video_adaptation) = period.adaptations.iter().find(is_video_adaptation) {
for representation in video_adaptation.representations.iter() {
let codec = representation.codecs.as_ref()
.unwrap_or_else(|| video_adaptation.codecs.as_ref()
.unwrap_or(&unspecified));
let bw = match representation.bandwidth {
Some(b) => b.to_string(),
None => "<unspecified>".to_string(),
};
println!("Video stream with codec {codec}, bandwidth {bw}");
}
}
Ok(())
}