95 lines
2.9 KiB
Rust
95 lines
2.9 KiB
Rust
use clap::Parser;
|
|
use iroh::{Endpoint, SecretKey, protocol::Router};
|
|
use iroh_live::{
|
|
Live,
|
|
media::{
|
|
audio::AudioBackend,
|
|
av::{AudioPreset, VideoCodec, VideoPreset},
|
|
capture::CameraCapturer,
|
|
ffmpeg::{H264Encoder, OpusEncoder},
|
|
publish::{AudioRenditions, PublishBroadcast, VideoRenditions},
|
|
},
|
|
ticket::LiveTicket,
|
|
};
|
|
use n0_error::StdResultExt;
|
|
|
|
#[tokio::main]
|
|
async fn main() -> n0_error::Result {
|
|
tracing_subscriber::fmt::init();
|
|
let cli = Cli::parse();
|
|
|
|
// Setup audio backend.
|
|
let audio_ctx = AudioBackend::new();
|
|
|
|
// Setup iroh and iroh-live.
|
|
let endpoint = Endpoint::builder()
|
|
.secret_key(secret_key_from_env()?)
|
|
.bind()
|
|
.await?;
|
|
let live = Live::new(endpoint.clone());
|
|
let router = Router::builder(endpoint)
|
|
.accept(iroh_live::ALPN, live.moq.protocol_handler())
|
|
.spawn();
|
|
|
|
// Create a publish broadcast.
|
|
let mut broadcast = PublishBroadcast::new();
|
|
|
|
// Capture audio, and encode with the cli-provided preset.
|
|
if !cli.no_audio {
|
|
let mic = audio_ctx.default_input().await?;
|
|
let audio = AudioRenditions::new::<OpusEncoder>(mic, [cli.audio_preset]);
|
|
broadcast.set_audio(Some(audio))?;
|
|
}
|
|
|
|
// Capture camera, and encode with the cli-provided presets.
|
|
if !cli.no_video {
|
|
let camera = CameraCapturer::new()?;
|
|
let video = VideoRenditions::new::<H264Encoder>(camera, cli.video_presets);
|
|
broadcast.set_video(Some(video))?;
|
|
}
|
|
|
|
// Publish under the name "hello".
|
|
let name = "hello";
|
|
live.publish(name, broadcast.producer()).await?;
|
|
|
|
// Create a ticket string and print
|
|
let ticket = LiveTicket::new(router.endpoint().id(), name);
|
|
println!("publishing at {ticket}");
|
|
let long_ticket = LiveTicket::new(router.endpoint().addr(), name);
|
|
println!("\nticket with addrs: {long_ticket}");
|
|
|
|
// Wait for ctrl-c and then shutdown.
|
|
tokio::signal::ctrl_c().await?;
|
|
live.shutdown();
|
|
router.shutdown().await.std_context("router shutdown")?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[derive(Parser, Debug)]
|
|
struct Cli {
|
|
#[arg(long, default_value_t=VideoCodec::H264)]
|
|
codec: VideoCodec,
|
|
#[arg(long, value_delimiter=',', default_values_t=[VideoPreset::P180, VideoPreset::P360, VideoPreset::P720, VideoPreset::P1080])]
|
|
video_presets: Vec<VideoPreset>,
|
|
#[arg(long, default_value_t=AudioPreset::Hq)]
|
|
audio_preset: AudioPreset,
|
|
#[arg(long)]
|
|
no_video: bool,
|
|
#[arg(long)]
|
|
no_audio: bool,
|
|
}
|
|
|
|
fn secret_key_from_env() -> n0_error::Result<SecretKey> {
|
|
Ok(match std::env::var("IROH_SECRET") {
|
|
Ok(key) => key.parse()?,
|
|
Err(_) => {
|
|
let key = SecretKey::generate(&mut rand::rng());
|
|
println!(
|
|
"Created new secret. Reuse with IROH_SECRET={}",
|
|
data_encoding::HEXLOWER.encode(&key.to_bytes())
|
|
);
|
|
key
|
|
}
|
|
})
|
|
}
|