sound: dynamically construct streams/chmaps based on --streams flag

Also fixes existing bug where chmaps count was hard-coded to 1
despite 2 chmap entries being created.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Davíð Steinn Geirsson 2026-03-17 19:20:55 +00:00
parent 8050df66f0
commit 500a78cd68

View file

@ -493,39 +493,50 @@ pub struct VhostUserSoundBackend {
impl VhostUserSoundBackend {
pub fn new(config: SoundConfig) -> Result<Self> {
let streams = vec![
Stream {
id: 0,
direction: Direction::Output,
..Stream::default()
},
Stream {
id: 1,
direction: Direction::Input,
..Stream::default()
},
];
let streams_no = streams.len();
let streams = Arc::new(RwLock::new(streams));
let jacks: Arc<RwLock<Vec<VirtioSoundJackInfo>>> = Arc::new(RwLock::new(Vec::new()));
let mut streams = Vec::new();
let mut chmaps_info: Vec<VirtioSoundChmapInfo> = Vec::new();
let mut positions = [VIRTIO_SND_CHMAP_NONE; VIRTIO_SND_CHMAP_MAX_SIZE];
positions[0] = VIRTIO_SND_CHMAP_FL;
positions[1] = VIRTIO_SND_CHMAP_FR;
let chmaps_info: Vec<VirtioSoundChmapInfo> = vec![
VirtioSoundChmapInfo {
if config.has_output() {
streams.push(Stream {
id: streams.len(),
direction: Direction::Output,
..Stream::default()
});
chmaps_info.push(VirtioSoundChmapInfo {
direction: VIRTIO_SND_D_OUTPUT,
channels: 2,
positions,
..VirtioSoundChmapInfo::default()
},
VirtioSoundChmapInfo {
});
}
if config.has_input() {
streams.push(Stream {
id: streams.len(),
direction: Direction::Input,
..Stream::default()
});
chmaps_info.push(VirtioSoundChmapInfo {
direction: VIRTIO_SND_D_INPUT,
channels: 2,
positions,
..VirtioSoundChmapInfo::default()
},
];
});
}
let chmaps_no = chmaps_info.len();
let streams_no = streams.len();
let streams = Arc::new(RwLock::new(streams));
let jacks: Arc<RwLock<Vec<VirtioSoundJackInfo>>> = Arc::new(RwLock::new(Vec::new()));
let chmaps: Arc<RwLock<Vec<VirtioSoundChmapInfo>>> = Arc::new(RwLock::new(chmaps_info));
if streams_no == 0 {
return Err(Error::UnexpectedAudioBackendConfiguration);
}
log::trace!("VhostUserSoundBackend::new(config = {:?})", &config);
let threads = if config.multi_thread {
vec![
@ -576,7 +587,7 @@ impl VhostUserSoundBackend {
virtio_cfg: VirtioSoundConfig {
jacks: 0.into(),
streams: Le32::from(streams_no as u32),
chmaps: 1.into(),
chmaps: Le32::from(chmaps_no as u32),
controls: 0.into(),
},
exit_consumer,
@ -1066,4 +1077,46 @@ mod tests {
test_dir.close().unwrap();
}
#[test]
fn test_sound_backend_output_only() {
crate::init_logger();
let config = SoundConfig::new(false, BackendType::Null, true, false);
let backend = VhostUserSoundBackend::new(config).expect("Could not create backend.");
// VirtioSoundConfig: jacks(4) + streams(4) + chmaps(4) + controls(4) = 16 bytes
let cfg = backend.get_config(0, 16);
assert_eq!(cfg.len(), 16);
// streams is at offset 4, little-endian u32
let streams = u32::from_le_bytes([cfg[4], cfg[5], cfg[6], cfg[7]]);
let chmaps = u32::from_le_bytes([cfg[8], cfg[9], cfg[10], cfg[11]]);
assert_eq!(streams, 1);
assert_eq!(chmaps, 1);
}
#[test]
fn test_sound_backend_input_only() {
crate::init_logger();
let config = SoundConfig::new(false, BackendType::Null, false, true);
let backend = VhostUserSoundBackend::new(config).expect("Could not create backend.");
let cfg = backend.get_config(0, 16);
let streams = u32::from_le_bytes([cfg[4], cfg[5], cfg[6], cfg[7]]);
let chmaps = u32::from_le_bytes([cfg[8], cfg[9], cfg[10], cfg[11]]);
assert_eq!(streams, 1);
assert_eq!(chmaps, 1);
}
#[test]
fn test_sound_backend_both_streams() {
crate::init_logger();
let config = SoundConfig::new(false, BackendType::Null, true, true);
let backend = VhostUserSoundBackend::new(config).expect("Could not create backend.");
let cfg = backend.get_config(0, 16);
let streams = u32::from_le_bytes([cfg[4], cfg[5], cfg[6], cfg[7]]);
let chmaps = u32::from_le_bytes([cfg[8], cfg[9], cfg[10], cfg[11]]);
assert_eq!(streams, 2);
assert_eq!(chmaps, 2); // Also verifies the chmaps bug fix (was hard-coded to 1)
}
}