NetDesk 1.5.0 — source release

This commit is contained in:
NetDesk committed 2026-10-18 01:35:59 +02:00
commit ec4955682f
227 files changed
+80669

No files matched your search

+94
View File
@@ -0,0 +1,94 @@
//! Remote audio playback — "Play remote computer sound".
//!
//! The host streams its system audio as Opus (CHAN_AUDIO `0xa1`), one 20 ms
//! frame at 48 kHz stereo per packet — exactly what the web viewer decodes with
//! WebCodecs. Here we decode each Opus packet with libopus (via the vendored
//! `netdesk-opus` crate) and play the PCM through the default output device with
//! `cpal` (WASAPI on Windows). A short ring buffer bridges the decode side (net
//! thread) and the audio callback (cpal's own thread) and caps latency.
//!
//! Developed by Akronlogic.
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use netdesk_opus::Decoder;
const SR: u32 = 48_000;
const CH: usize = 2;
/// Decodes the host's Opus audio and plays it. Keep the instance alive for the
/// whole session — dropping it stops the output stream.
pub struct AudioPlayer {
_stream: cpal::Stream, // keep alive: dropping stops playback
decoder: Decoder,
ring: Arc<Mutex<VecDeque<f32>>>,
}
impl AudioPlayer {
/// Open the default output device at 48 kHz stereo and start playing silence.
pub fn new() -> anyhow::Result<Self> {
let host = cpal::default_host();
let device = host
.default_output_device()
.ok_or_else(|| anyhow::anyhow!("no default audio output device"))?;
let config = cpal::StreamConfig {
channels: CH as u16,
sample_rate: cpal::SampleRate(SR),
buffer_size: cpal::BufferSize::Default,
};
let ring: Arc<Mutex<VecDeque<f32>>> =
Arc::new(Mutex::new(VecDeque::with_capacity(SR as usize)));
let ring_cb = ring.clone();
let stream = device.build_output_stream(
&config,
move |out: &mut [f32], _: &cpal::OutputCallbackInfo| match ring_cb.lock() {
Ok(mut r) => {
for s in out.iter_mut() {
*s = r.pop_front().unwrap_or(0.0);
}
}
Err(_) => {
for s in out.iter_mut() {
*s = 0.0;
}
}
},
move |err| eprintln!("[audio] output stream error: {err}"),
None,
)?;
stream.play()?;
let decoder = Decoder::new(SR, CH as u32)?;
Ok(Self {
_stream: stream,
decoder,
ring,
})
}
/// Decode one Opus packet and enqueue the resulting PCM for playback.
/// Silently drops malformed/undecodable packets (best-effort, like the web
/// viewer).
pub fn push_opus(&mut self, data: &[u8]) {
let pcm = match self.decoder.decode(data) {
Ok(p) => p, // interleaved i16, stereo
Err(_) => return,
};
if let Ok(mut r) = self.ring.lock() {
for &s in &pcm {
r.push_back(s as f32 / 32768.0);
}
// Cap buffered audio to ~400 ms so playback can't drift far behind
// the video (drop the oldest samples on overrun).
let cap = SR as usize * CH * 2 / 5;
while r.len() > cap {
r.pop_front();
}
}
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff