chore: 测试播放和录音

This commit is contained in:
Del Wang
2026-01-02 17:23:24 +08:00
parent 24b3fdddf8
commit a2d06958e1
24 changed files with 712 additions and 1353 deletions
-252
View File
@@ -1,252 +0,0 @@
use crate::app::server::handlers;
use crate::app::server::session::ServerSession;
use crate::audio::codec::OpusCodec;
use crate::audio::config::AudioConfig;
use crate::audio::wav::{WavReader, WavWriter};
use crate::net::discovery::Discovery;
use crate::net::network::{AudioSocket, ControlConnection, ServerNetwork};
use crate::net::protocol::{AudioPacket, ControlPacket, RpcResult};
use anyhow::{Context, Result};
use parking_lot::Mutex;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::Arc;
pub struct Server {
sessions: Arc<Mutex<HashMap<SocketAddr, Arc<ServerSession>>>>,
audio_socket: Arc<AudioSocket>,
}
impl Server {
pub async fn new() -> Result<Self> {
Ok(Self {
sessions: Arc::new(Mutex::new(HashMap::new())),
audio_socket: Arc::new(AudioSocket::bind().await?),
})
}
pub async fn run(self: Arc<Self>, port: u16) -> Result<()> {
let network = ServerNetwork::setup(port).await?;
println!("服务端启动在: {}", network.local_addr()?);
// 启动广播
Discovery::start_broadcast(port).await?;
loop {
let (control, addr) = network.accept().await?;
let server = self.clone();
tokio::spawn(async move {
if let Err(e) = server.handle_connection(control, addr).await {
eprintln!("连接 {} 出错: {}", addr, e);
}
server.sessions.lock().remove(&addr);
println!("客户端 {} 断开连接", addr);
});
}
}
async fn handle_connection(
&self,
mut control: ControlConnection,
addr: SocketAddr,
) -> Result<()> {
println!("新客户端连接: {}", addr);
// 握手认证
let info = match control.recv_packet().await? {
ControlPacket::ClientIdentify { info } => info,
p => return Err(anyhow::anyhow!("预期的握手包,收到: {:?}", p)),
};
println!(
"客户端识别: {} ({}) v{}",
info.model, info.mac, info.version
);
control.send_packet(&ControlPacket::IdentifyOk).await?;
let (mut reader, writer) = control.split();
let session = Arc::new(ServerSession::new(info, writer, addr));
self.sessions.lock().insert(addr, session.clone());
// 处理控制消息循环
loop {
let packet = reader.recv_packet().await?;
let session = session.clone();
tokio::spawn(async move {
if let Err(e) = handlers::handle_packet(session, packet).await {
eprintln!("处理来自 {} 的包时出错: {}", addr, e);
}
});
}
}
// 暴露给外部调用的方法
pub async fn call_shell(&self, addr: SocketAddr, cmd: &str) -> Result<RpcResult> {
let session = self
.sessions
.lock()
.get(&addr)
.cloned()
.context("未找到 Session")?;
let (id, rx) = session.rpc.alloc_id();
session
.writer
.lock()
.await
.send_packet(&ControlPacket::RpcRequest {
id,
method: "shell".to_string(),
args: vec![cmd.to_string()],
})
.await?;
Ok(rx.await?)
}
pub async fn start_recording(&self, addr: SocketAddr, config: AudioConfig) -> Result<()> {
let session = self
.sessions
.lock()
.get(&addr)
.cloned()
.context("未找到 Session")?;
// 发送开始录音指令
session
.writer
.lock()
.await
.send_packet(&ControlPacket::StartRecording {
config: config.clone(),
})
.await?;
let audio_socket = self.audio_socket.clone();
tokio::spawn(async move {
if let Err(e) = save_audio_to_wav(audio_socket, config, "temp/recorded.wav").await {
eprintln!("保存录音失败: {}", e);
}
});
Ok(())
}
pub async fn stop_recording(&self, addr: SocketAddr) -> Result<()> {
let session = self
.sessions
.lock()
.get(&addr)
.cloned()
.context("未找到 Session")?;
session
.writer
.lock()
.await
.send_packet(&ControlPacket::StopRecording)
.await?;
Ok(())
}
pub async fn start_playback(&self, addr: SocketAddr, config: AudioConfig) -> Result<()> {
let session = self
.sessions
.lock()
.get(&addr)
.cloned()
.context("未找到 Session")?;
session
.writer
.lock()
.await
.send_packet(&ControlPacket::StartPlayback {
config: config.clone(),
})
.await?;
let audio_socket = self.audio_socket.clone();
tokio::spawn(async move {
if let Err(e) = stream_wav_to_client(audio_socket, addr, config, "temp/test.wav").await
{
eprintln!("推流失败: {}", e);
}
});
Ok(())
}
pub async fn stop_playback(&self, addr: SocketAddr) -> Result<()> {
let session = self
.sessions
.lock()
.get(&addr)
.cloned()
.context("未找到 Session")?;
session
.writer
.lock()
.await
.send_packet(&ControlPacket::StopPlayback)
.await?;
Ok(())
}
pub fn get_sessions(&self) -> Vec<SocketAddr> {
self.sessions.lock().keys().cloned().collect()
}
}
async fn save_audio_to_wav(
socket: Arc<AudioSocket>,
config: AudioConfig,
path: &str,
) -> Result<()> {
std::fs::create_dir_all("temp")?;
let mut writer = WavWriter::create(path, config.sample_rate, config.channels)?;
let mut codec = OpusCodec::new(&config)?;
let mut pcm_buf = vec![0i16; config.frame_size];
let mut udp_buf = vec![0u8; 4096];
println!("正在录制到 {}...", path);
for _ in 0..100 {
let (packet, _) = socket.recv_packet(&mut udp_buf).await?;
let pcm_len = codec.decode(&packet.data, &mut pcm_buf)?;
writer.write_samples(&pcm_buf[..pcm_len])?;
}
writer.finalize()?;
println!("录制完成: {}", path);
Ok(())
}
async fn stream_wav_to_client(
socket: Arc<AudioSocket>,
target: SocketAddr,
config: AudioConfig,
path: &str,
) -> Result<()> {
let mut reader = WavReader::open(path)?;
let mut codec = OpusCodec::new(&config)?;
let mut pcm_buf = vec![0i16; config.frame_size];
let mut opus_buf = vec![0u8; 4096];
println!("正在从 {} 推流...", path);
loop {
let n = reader.read_samples(&mut pcm_buf)?;
if n == 0 {
break;
}
let opus_len = codec.encode(&pcm_buf[..n], &mut opus_buf)?;
let packet = AudioPacket {
data: opus_buf[..opus_len].to_vec(),
};
socket.send_packet(&packet, target).await?;
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
}
println!("推流结束");
Ok(())
}
@@ -1,57 +0,0 @@
use crate::app::server::core::Server;
use crate::audio::config::AudioConfig;
use anyhow::Result;
use std::sync::Arc;
pub async fn run_server() -> Result<()> {
let server = Arc::new(Server::new().await?);
let s = server.clone();
// 运行服务器
tokio::spawn(async move {
if let Err(e) = s.run(53531).await {
eprintln!("Server error: {}", e);
}
});
// 等待一个客户端连接并进行演示
println!("等待客户端连接以进行功能演示...");
loop {
let sessions = server.get_sessions();
if !sessions.is_empty() {
let addr = sessions[0];
println!("开始对 {} 进行功能测试...", addr);
// 1. 测试 RPC
println!("测试 RPC: echo hello");
let res = server.call_shell(addr, "echo hello").await?;
println!("RPC 结果: {:?}", res);
// 2. 测试录音
println!("测试录制 2 秒音频...");
server.start_recording(addr, AudioConfig::voice()).await?;
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
server.stop_recording(addr).await?;
println!("录制结束,请检查 temp/recorded.wav");
// 3. 测试播放 (如果 temp/test.wav 存在)
if std::path::Path::new("temp/test.wav").exists() {
println!("测试播放 temp/test.wav...");
server.start_playback(addr, AudioConfig::voice()).await?;
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
server.stop_playback(addr).await?;
println!("播放结束");
} else {
println!("跳过播放测试 (temp/test.wav 不存在)");
}
break;
}
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
// 保持运行
loop {
tokio::time::sleep(std::time::Duration::from_secs(3600)).await;
}
}
@@ -1,50 +0,0 @@
use crate::app::server::session::ServerSession;
use crate::net::protocol::{ControlPacket, RpcResult};
use anyhow::Result;
use std::sync::Arc;
pub async fn handle_packet(session: Arc<ServerSession>, packet: ControlPacket) -> Result<()> {
match packet {
ControlPacket::RpcResponse { id, result } => {
session.rpc.fulfill(id, result);
}
ControlPacket::RpcRequest { id, method, args } => {
println!(
"收到来自客户端 {} 的 RPC 请求: {} {:?}",
session.addr, method, args
);
let result = handle_server_rpc(&method, args).await;
session
.writer
.lock()
.await
.send_packet(&ControlPacket::RpcResponse { id, result })
.await?;
}
ControlPacket::Ping => {
session
.writer
.lock()
.await
.send_packet(&ControlPacket::Pong)
.await?;
}
_ => {}
}
Ok(())
}
async fn handle_server_rpc(method: &str, _args: Vec<String>) -> RpcResult {
match method {
"status" => RpcResult {
stdout: "Server is running normally".to_string(),
stderr: "".to_string(),
code: 0,
},
_ => RpcResult {
stdout: "".to_string(),
stderr: format!("Server does not support method: {}", method),
code: -1,
},
}
}
+224 -4
View File
@@ -1,4 +1,224 @@
pub mod core;
pub mod entry;
pub mod handlers;
pub mod session;
use crate::audio::codec::OpusCodec;
use crate::audio::config::AudioConfig;
use crate::audio::wav::{WavReader, WavWriter};
use crate::net::discovery::Discovery;
use crate::net::network::{AudioSocket, Connection};
use crate::net::protocol::{AudioPacket, ControlPacket, DeviceInfo, RpcResult};
use crate::net::rpc::RpcManager;
use anyhow::{Context, Result};
use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::sync::Mutex;
pub struct Session {
pub info: DeviceInfo,
pub conn: Arc<Connection>,
pub rpc: Arc<RpcManager>,
pub audio_addr: SocketAddr,
}
pub struct Server {
sessions: Arc<Mutex<HashMap<SocketAddr, Arc<Session>>>>,
audio: Arc<AudioSocket>,
}
impl Server {
pub async fn new() -> Result<Self> {
Ok(Self {
sessions: Arc::new(Mutex::new(HashMap::new())),
audio: Arc::new(AudioSocket::bind().await?),
})
}
pub async fn run(self: Arc<Self>, port: u16) -> Result<()> {
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", port)).await?;
let addr = listener.local_addr()?;
println!("Server listening on TCP: {}", addr);
Discovery::broadcast(port, self.audio.port()).await?;
loop {
let (stream, addr) = listener.accept().await?;
let server = self.clone();
tokio::spawn(async move {
if let Err(e) = server.handle_connection(stream, addr).await {
eprintln!("Session {} error: {}", addr, e);
}
server.sessions.lock().await.remove(&addr);
});
}
}
async fn handle_connection(
&self,
stream: tokio::net::TcpStream,
addr: SocketAddr,
) -> Result<()> {
println!("New TCP connection from {}", addr);
let conn = Arc::new(Connection::new(stream)?);
let (info, client_udp_port) = match conn.recv().await? {
ControlPacket::ClientIdentify { info, udp_port } => (info, udp_port),
p => {
println!("Expected Identify from {}, got {:?}", addr, p);
return Err(anyhow::anyhow!("Expected Identify, got {:?}", p));
}
};
let audio_addr = SocketAddr::new(addr.ip(), client_udp_port);
println!(
"Client identified: {} ({}) version {}, audio at {}",
info.model, addr, info.version, audio_addr
);
conn.send(&ControlPacket::IdentifyOk).await?;
let session = Arc::new(Session {
info,
conn: conn.clone(),
rpc: Arc::new(RpcManager::new()),
audio_addr,
});
self.sessions.lock().await.insert(addr, session.clone());
loop {
let packet = conn.recv().await?;
let session = session.clone();
tokio::spawn(async move {
if let Err(e) = handle_packet(session, packet).await {
eprintln!("Handle packet error: {}", e);
}
});
}
}
pub async fn get_clients(&self) -> Vec<SocketAddr> {
self.sessions.lock().await.keys().cloned().collect()
}
pub async fn call(
&self,
addr: SocketAddr,
method: &str,
args: Vec<String>,
) -> Result<RpcResult> {
let session = self
.sessions
.lock()
.await
.get(&addr)
.cloned()
.context("Session not found")?;
let (id, rx) = session.rpc.register();
session
.conn
.send(&ControlPacket::RpcRequest {
id,
method: method.to_string(),
args,
})
.await?;
Ok(rx.await?)
}
pub async fn start_record(&self, addr: SocketAddr, config: AudioConfig) -> Result<()> {
let session = self
.sessions
.lock()
.await
.get(&addr)
.cloned()
.context("Session not found")?;
session
.conn
.send(&ControlPacket::StartRecording {
config: config.clone(),
})
.await?;
let audio = self.audio.clone();
tokio::spawn(async move {
let mut writer =
WavWriter::create("temp/recorded.wav", config.sample_rate, config.channels)
.unwrap();
let mut codec = OpusCodec::new(&config).unwrap();
let mut pcm = vec![0i16; config.frame_size];
let mut buf = vec![0u8; 4096];
for _ in 0..500 {
// Record ~10s
if let Ok((packet, _)) = audio.recv(&mut buf).await {
if let Ok(n) = codec.decode(&packet.data, &mut pcm) {
writer.write_samples(&pcm[..n]).unwrap();
}
}
}
writer.finalize().unwrap();
println!("Recording saved to temp/recorded.wav");
});
Ok(())
}
pub async fn start_play(&self, addr: SocketAddr) -> Result<()> {
let session = self
.sessions
.lock()
.await
.get(&addr)
.cloned()
.context("Session not found")?;
let reader = WavReader::open("temp/test.wav")?;
let opus_rate = if reader.sample_rate > 24000 {
48000
} else {
16000
};
let config = AudioConfig {
sample_rate: opus_rate,
channels: reader.channels,
frame_size: (opus_rate / 50) as usize, // 20ms
..AudioConfig::music_48k()
};
session
.conn
.send(&ControlPacket::StartPlayback {
config: config.clone(),
})
.await?;
let audio = self.audio.clone();
let target_addr = session.audio_addr;
tokio::spawn(async move {
let mut reader = reader;
let mut codec = OpusCodec::new(&config).unwrap();
let mut pcm = vec![0i16; config.frame_size];
let mut opus = vec![0u8; 4096];
while let Ok(n) = reader.read_samples(&mut pcm) {
if n == 0 {
break;
}
if let Ok(len) = codec.encode(&pcm[..n], &mut opus) {
let _ = audio
.send(
&AudioPacket {
data: opus[..len].to_vec(),
},
target_addr,
)
.await;
}
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
}
});
Ok(())
}
}
async fn handle_packet(session: Arc<Session>, packet: ControlPacket) -> Result<()> {
match packet {
ControlPacket::RpcResponse { id, result } => session.rpc.resolve(id, result),
ControlPacket::Ping => session.conn.send(&ControlPacket::Pong).await?,
_ => {}
}
Ok(())
}
@@ -1,23 +0,0 @@
use crate::net::network::ControlWriter;
use crate::net::protocol::DeviceInfo;
use crate::net::rpc::RpcManager;
use std::net::SocketAddr;
use std::sync::Arc;
pub struct ServerSession {
pub info: DeviceInfo,
pub writer: Arc<tokio::sync::Mutex<ControlWriter>>,
pub addr: SocketAddr,
pub rpc: Arc<RpcManager>,
}
impl ServerSession {
pub fn new(info: DeviceInfo, writer: ControlWriter, addr: SocketAddr) -> Self {
Self {
info,
writer: Arc::new(tokio::sync::Mutex::new(writer)),
addr,
rpc: Arc::new(RpcManager::new()),
}
}
}