use bytes::BytesMut;
use std::net;
use url::Url;
use anyhow::Context;
use clap::Parser;
use tokio::io::AsyncReadExt;
use moq_native_ietf::quic;
use moq_pub::Media;
use moq_transport::{coding::TrackNamespace, serve, session::Publisher};
#[derive(Parser, Clone)]
pub struct Cli {
#[arg(long, default_value = "[::]:0")]
pub bind: net::SocketAddr,
#[arg(long, default_value = "24")]
pub fps: u8,
#[arg(long, default_value = "1500000")]
pub bitrate: u32,
#[arg()]
pub url: Url,
#[arg(long)]
pub name: String,
#[command(flatten)]
pub tls: moq_native_ietf::tls::Args,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info,quinn=warn")),
)
.init();
let cli = Cli::parse();
let (writer, _, reader) =
serve::Tracks::new(TrackNamespace::from_utf8_path(&cli.name)).produce();
let media = Media::new(writer)?;
let tls = cli.tls.load()?;
let quic = quic::Endpoint::new(moq_native_ietf::quic::Config::new(
cli.bind,
None,
tls.clone(),
)?)?;
tracing::info!("connecting to relay: url={}", cli.url);
let (session, connection_id, transport) = quic.client.connect(&cli.url, None).await?;
tracing::info!(
"connected with CID: {} (use this to look up qlog/mlog on server)",
connection_id
);
let (session, mut publisher) = Publisher::connect(session, transport)
.await
.context("failed to create MoQ Transport publisher")?;
tokio::select! {
res = session.run() => res.context("session error")?,
res = run_media(media) => {
res.context("media error")?
},
res = publisher.announce(reader) => res.context("publisher error")?,
}
Ok(())
}
async fn run_media(mut media: Media) -> anyhow::Result<()> {
let mut input = tokio::io::stdin();
let mut buf = BytesMut::new();
loop {
input
.read_buf(&mut buf)
.await
.context("failed to read from stdin")?;
media.parse(&mut buf).context("failed to parse media")?;
}
}