chore: 测试播放和录音
This commit is contained in:
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user