如何在Rust中捕获Linux(Pipewire/Alsa)下其他应用的播放音频?
在Linux(PipeWire/ALSA)下用Rust捕获应用播放音频的方案
要捕获Chrome等应用的播放音频,核心是要获取系统的音频输出监听流(而非麦克风输入),以下是针对PipeWire和ALSA环境的具体实现方案:
一、优先用PipeWire原生方案(推荐)
PipeWire本身支持直接捕获应用的音频流,无需额外创建回环设备,用官方Rust绑定pipewire-rs可以直接操作:
- 添加依赖
在Cargo.toml中加入:
[dependencies] pipewire = "0.7" # 请使用最新稳定版本
- 捕获默认输出监听流的示例代码
use pipewire::{ context::Context, core::Core, stream::{Stream, StreamFlags}, properties::Properties, }; use std::error::Error; fn main() -> Result<(), Box<dyn Error>> { // 初始化PipeWire上下文与核心 let context = Context::new()?; let core = Core::new(&context)?; let main_loop = pipewire::main_loop::MainLoop::new()?; let loop_handle = main_loop.handle(); // 设置流属性:指定捕获输出监听流 let props = Properties::new([ ("media.class", "Audio/Sink/Media"), ("audio.position", "FL,FR"), ("stream.capture.monitor", "true"), // 关键:开启监听模式 ]); // 创建音频捕获流 let mut stream = Stream::new(&core, "app-audio-capture", &props)?; // 注册音频数据处理回调 stream.add_listener_local() .process(move |stream| { let buffer = stream.dequeue_buffer().unwrap(); // 这里的buffer就是捕获到的应用音频数据,格式为f32(可根据配置调整) let audio_samples = buffer.data::<f32>(); // 在此处将数据传给你的可视化模块 println!("捕获到 {} 个音频采样", audio_samples.len()); stream.queue_buffer(buffer); }) .register(); // 连接流到PipeWire音频图 stream.connect(None, StreamFlags::AUTOCONNECT | StreamFlags::MAP_BUFFERS, None)?; // 启动主循环持续捕获 main_loop.run(); Ok(()) }
二、ALSA回环设备方案(兼容传统ALSA环境)
如果需要基于ALSA实现,需先创建回环设备并路由音频:
- 加载ALSA回环模块
sudo modprobe snd-aloop
- 添加依赖
[dependencies] alsa = "0.7"
- 捕获回环设备音频的示例代码
use alsa::{pcm, ValueOr}; use std::error::Error; fn main() -> Result<(), Box<dyn Error>> { // 打开ALSA回环捕获设备(子设备1是捕获端,子设备0是输出端) let pcm = pcm::PCM::new("hw:Loopback,1,0", pcm::Direction::Capture, false)?; // 配置音频参数:双声道、44100采样率、16位有符号整数 let hwp = pcm::HwParams::any(&pcm)?; hwp.set_channels(2)?; hwp.set_rate(44100, ValueOr::Nearest)?; hwp.set_format(pcm::Format::s16())?; hwp.set_access(pcm::Access::RWInterleaved)?; pcm.set_hw_params(&hwp)?; let io = pcm.io_i16()?; let mut buffer = [0; 44100 * 2]; // 1秒长度的缓冲区 loop { let captured_frames = io.readi(&mut buffer)?; // 处理捕获到的音频数据,传给可视化模块 println!("捕获到 {} 帧音频", captured_frames); } }
- 路由应用音频
用pavucontrol工具,将Chrome的音频输出切换到Loopback设备,即可从捕获端拿到数据。
三、用你熟悉的cpal实现
cpal也能捕获监听流,只需找到正确的设备:
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; use std::error::Error; fn main() -> Result<(), Box<dyn Error>> { let host = cpal::default_host(); // 枚举输入设备,找到名称含"Monitor"或"Loopback"的监听设备 let target_device = host.input_devices()? .find(|device| { let device_name = device.name().unwrap_or_default(); device_name.contains("Monitor") || device_name.contains("Loopback") }) .ok_or("未找到音频监听设备")?; println!("使用设备:{}", target_device.name()?); let default_config = target_device.default_input_config()?; // 创建捕获流并处理数据 let stream = target_device.build_input_stream( &default_config.into(), move |audio_data: &[f32], _| { // 此处处理音频数据,用于可视化 println!("捕获到 {} 个采样", audio_data.len()); }, move |err| eprintln!("捕获错误:{}", err), )?; stream.play()?; loop {} // 保持程序运行 }
注意事项
- 确保用户属于
audio组,避免权限问题:sudo usermod -aG audio $USER - 用
pavucontrol可以直观查看和调整音频路由,方便定位监听设备 - 捕获到的音频数据格式(采样率、位深、通道数)需与可视化模块兼容,必要时做格式转换
内容的提问来源于stack exchange,提问作者LearnerCoderGamer
相关产品推荐
相关产品推荐

