use symphonia::core::audio::Channels;
fn ffmpeg_bit(index: u32) -> Option<u64> {
match index {
0..=17 => Some(1 << index),
18..=23 => Some(1 << (index + 17)),
24 => Some(1 << 6),
25 => Some(1 << 7),
_ => None,
}
}
pub(super) fn ffmpeg_layout(channels: &Channels, count: usize) -> (Option<u64>, Vec<usize>) {
let keep = (None, (0..count).collect());
if count == 1 {
return (Some(1 << 2), vec![0]);
}
let Channels::Positioned(positions) = channels else {
return keep;
};
let bits: Option<Vec<u64>> = (0..64u32)
.filter(|b| positions.bits() & (1 << b) != 0)
.map(ffmpeg_bit)
.collect();
let Some(bits) = bits.filter(|b| b.len() == count) else {
return keep;
};
let mask = bits.iter().fold(0u64, |m, b| m | b);
if mask.count_ones() as usize != count {
return keep;
}
let mut order: Vec<usize> = (0..count).collect();
order.sort_by_key(|&plane| bits[plane]);
(Some(mask), order)
}
#[cfg(test)]
mod tests {
use super::*;
use symphonia::core::audio::Position;
#[test]
fn positions_map_to_ffmpeg_bits_in_its_order() {
let surround = Position::FRONT_LEFT
| Position::FRONT_RIGHT
| Position::FRONT_CENTER
| Position::LFE1
| Position::SIDE_LEFT
| Position::SIDE_RIGHT;
assert_eq!(
ffmpeg_layout(&Channels::Positioned(surround), 6),
(Some(0x60F), vec![0, 1, 2, 3, 4, 5])
);
let wide = surround | Position::FRONT_LEFT_WIDE | Position::FRONT_RIGHT_WIDE;
assert_eq!(
ffmpeg_layout(&Channels::Positioned(wide), 8),
(Some(0x6CF), vec![0, 1, 2, 3, 6, 7, 4, 5])
);
let lfe2 = Position::FRONT_LEFT | Position::FRONT_RIGHT | Position::LFE2;
assert_eq!(
ffmpeg_layout(&Channels::Positioned(lfe2), 3),
(Some(0x8_0000_0003), vec![0, 1, 2])
);
let mono = Channels::Positioned(Position::FRONT_LEFT);
assert_eq!(ffmpeg_layout(&mono, 1), (Some(0x4), vec![0]));
assert_eq!(ffmpeg_layout(&Channels::Discrete(2), 2), (None, vec![0, 1]));
let shared = Position::FRONT_LEFT_CENTER | Position::FRONT_LEFT_WIDE;
assert_eq!(
ffmpeg_layout(&Channels::Positioned(shared), 2),
(None, vec![0, 1])
);
}
}