NetDesk 1.5.0 — source release
This commit is contained in:
commit
37aa876908
227 files changed
+80694
No files matched your search
@@ -0,0 +1,81 @@
|
||||
//! NetDesk voice send-test — drives the EXACT path the native viewer's voice
|
||||
//! button uses (`netdesk_host::audio::run_audio` with source = microphone, and
|
||||
//! `Session::voice` per Opus packet), headlessly, so we can prove viewer->host
|
||||
//! two-way voice end to end against a scratch host. Developed by Akronlogic.
|
||||
//!
|
||||
//! Usage: netdesk-voice-send <host_id> <password> [ws_url] [secs]
|
||||
//! Watch the HOST log for `voice: rx N pkts from viewer mic, recent peak X`.
|
||||
|
||||
use netdesk_core::{connect, ConnectConfig, SessionEvent};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let a: Vec<String> = std::env::args().collect();
|
||||
if a.len() < 3 {
|
||||
eprintln!("usage: netdesk-voice-send <host_id> <password> [ws_url] [secs]");
|
||||
std::process::exit(2);
|
||||
}
|
||||
let secs: u64 = a.get(4).and_then(|s| s.parse().ok()).unwrap_or(6);
|
||||
let cfg = ConnectConfig {
|
||||
host_id: a[1].clone(),
|
||||
password: a[2].clone(),
|
||||
ws_url: a.get(3).cloned().unwrap_or_else(|| "wss://netdesk.space/ws".into()),
|
||||
totp: String::new(),
|
||||
takeover: false,
|
||||
};
|
||||
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<SessionEvent>();
|
||||
eprintln!("[voice-send] connecting to host {} …", cfg.host_id);
|
||||
let session = connect(cfg, tx).await.map_err(|e| anyhow::anyhow!("connect: {e}"))?;
|
||||
eprintln!("[voice-send] connected; capturing microphone and streaming CHAN_VOICE for {secs}s");
|
||||
|
||||
// Mirror viewer::toggle_voice exactly: share the Session behind a Mutex and
|
||||
// feed each captured Opus packet to Session::voice from the capture thread.
|
||||
let sess = Arc::new(Mutex::new(session));
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
let sent = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let s2 = sess.clone();
|
||||
let st2 = stop.clone();
|
||||
let n2 = sent.clone();
|
||||
std::thread::spawn(move || {
|
||||
let src = Arc::new(AtomicU8::new(2)); // 2 = microphone (default capture device)
|
||||
netdesk_host::audio::run_audio(st2, || true, src, move |opus| {
|
||||
s2.lock().unwrap().voice(&opus);
|
||||
n2.fetch_add(1, Ordering::Relaxed);
|
||||
});
|
||||
});
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
eprintln!("[voice-send] microphone capture is Windows-only");
|
||||
}
|
||||
|
||||
// Drain session events (ignore them) while the capture thread streams.
|
||||
let deadline = tokio::time::Instant::now() + Duration::from_secs(secs);
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = tokio::time::sleep_until(deadline) => break,
|
||||
ev = rx.recv() => match ev {
|
||||
Some(SessionEvent::Ended { .. }) | None => break,
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stop.store(true, Ordering::Relaxed);
|
||||
let n = sent.load(Ordering::Relaxed);
|
||||
sess.lock().unwrap().close();
|
||||
println!("VOICE_SEND_DONE sent={n} packets");
|
||||
if n == 0 {
|
||||
eprintln!("[voice-send] WARNING: 0 packets — no microphone/capture device?");
|
||||
std::process::exit(3);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in new issue
Block a user