use std::net;
use anyhow::Context;
use clap::{Parser, Subcommand};
use moq_transfork::Session;
use url::Url;
use moq_karp::{cmaf, BroadcastProducer};
use moq_native::quic;
#[derive(Parser, Clone)]
struct Config {
#[arg(long, default_value = "[::]:0")]
pub bind: net::SocketAddr,
#[command(flatten)]
pub log: moq_native::log::Args,
#[command(flatten)]
pub tls: moq_native::tls::Args,
#[command(subcommand)]
pub command: Command,
}
#[derive(Subcommand, Clone)]
pub enum Command {
Publish {
url: String,
},
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = Config::parse();
config.log.init();
match config.command.clone() {
Command::Publish { url } => publish(config, url).await,
}
}
async fn connect(config: &Config, url: &str) -> anyhow::Result<(Session, String)> {
let tls = config.tls.load()?;
let quic = quic::Endpoint::new(quic::Config { bind: config.bind, tls })?;
tracing::info!(?url, "connecting");
let url = Url::parse(url).context("invalid URL")?;
let path = url.path().to_string();
let session = quic.client.connect(url).await?;
let session = Session::connect(session).await?;
Ok((session, path))
}
#[tracing::instrument(skip_all, fields(?url))]
async fn publish(config: Config, url: String) -> anyhow::Result<()> {
let (session, path) = connect(&config, &url).await?;
let broadcast = BroadcastProducer::new(session.clone(), path)?;
let mut input = tokio::io::stdin();
let mut import = cmaf::Import::new(broadcast);
import.init_from(&mut input).await.context("failed to initialize")?;
tracing::info!("publishing");
tokio::select! {
res = import.read_from(&mut input) => Ok(res?),
res = session.closed() => Err(res.into()),
}
}