1pub use crate::error::SteamAudioError;
2use std::fmt::Debug;
3
4#[cfg(feature = "opus")]
5mod decoder;
6mod error;
7#[cfg(feature = "opus")]
8pub use decoder::SteamVoiceDecoder;
9
10#[derive(Debug)]
12#[repr(u8)]
13enum PacketType {
14 Silence = 0,
15 OpusPlc = 6,
16 SampleRate = 11,
17}
18
19impl TryFrom<u8> for PacketType {
20 type Error = SteamAudioError;
21
22 fn try_from(value: u8) -> Result<Self, Self::Error> {
23 match value {
24 0 => Ok(Self::Silence),
25 6 => Ok(Self::OpusPlc),
26 11 => Ok(Self::SampleRate),
27 _ => Err(SteamAudioError::UnknownPacketType { ty: value }),
28 }
29 }
30}
31
32fn read_bytes<const N: usize>(data: &[u8]) -> Result<([u8; N], &[u8]), SteamAudioError> {
33 if data.len() < N {
34 Err(SteamAudioError::InsufficientData)
35 } else {
36 let (result, rest) = data.split_at(N);
37 Ok((result.try_into().unwrap(), rest))
38 }
39}
40
41fn read_u16(data: &[u8]) -> Result<(u16, &[u8]), SteamAudioError> {
42 let (bytes, data) = read_bytes(data)?;
43 Ok((u16::from_le_bytes(bytes), data))
44}
45
46#[derive(Debug)]
48pub enum Packet<'a> {
49 Silence(u16),
51 OpusPlc(SteamOpusData<'a>),
53 SampleRate(u16),
55}
56
57impl<'a> Packet<'a> {
58 pub fn read(data: &'a [u8]) -> Result<(Self, &'a [u8]), SteamAudioError> {
59 let ty = PacketType::try_from(*data.first().ok_or(SteamAudioError::InsufficientData)?)?;
60 let data = &data[1..];
61
62 let (next, data) = read_u16(data)?;
63
64 Ok(match ty {
65 PacketType::Silence => (Packet::Silence(next), data),
66 PacketType::OpusPlc => {
67 if data.len() < next as usize {
68 return Err(SteamAudioError::InsufficientData);
69 } else {
70 let (result, data) = data.split_at(next as usize);
71 (Packet::OpusPlc(SteamOpusData { data: result }), data)
72 }
73 }
74 PacketType::SampleRate => (Packet::SampleRate(next), data),
75 })
76 }
77}
78
79#[derive(Debug)]
87pub struct SteamVoiceData<'a> {
88 pub steam_id: u64,
89 packet_data: &'a [u8],
90}
91
92impl<'a> SteamVoiceData<'a> {
93 pub fn new(data: &'a [u8]) -> Result<Self, SteamAudioError> {
95 let (data, crc_data) = data.split_at(data.len() - 4);
96 let expected_crc = u32::from_le_bytes(crc_data.try_into().unwrap());
97 let calculated_crc = crc32b(data);
98 if expected_crc != calculated_crc {
99 return Err(SteamAudioError::CrcMismatch {
100 actual: calculated_crc,
101 expected: expected_crc,
102 });
103 }
104
105 let (steam_id_bytes, data) = read_bytes(data)?;
106 let steam_id = u64::from_le_bytes(steam_id_bytes);
107 Ok(SteamVoiceData {
108 steam_id,
109 packet_data: data,
110 })
111 }
112
113 pub fn packets(&self) -> impl Iterator<Item = Result<Packet<'a>, SteamAudioError>> {
115 SteamPacketIterator {
116 data: self.packet_data,
117 }
118 }
119}
120
121struct SteamPacketIterator<'a> {
122 data: &'a [u8],
123}
124
125impl Debug for SteamPacketIterator<'_> {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 f.debug_struct("SteamPacketIterator")
128 .field("data_length", &self.data.len())
129 .finish_non_exhaustive()
130 }
131}
132
133impl<'a> Iterator for SteamPacketIterator<'a> {
134 type Item = Result<Packet<'a>, SteamAudioError>;
135
136 fn next(&mut self) -> Option<Self::Item> {
137 if self.data.is_empty() {
138 None
139 } else {
140 match Packet::read(self.data) {
141 Ok((packet, rest)) => {
142 self.data = rest;
143 Some(Ok(packet))
144 }
145 Err(e) => Some(Err(e)),
146 }
147 }
148 }
149}
150
151fn crc32b(data: &[u8]) -> u32 {
152 let mut crc: u32 = 0xFFFFFFFF;
153 for &byte in data {
154 crc ^= byte as u32;
155 for _ in 0..8 {
156 let mask = (-((crc & 1) as i32)) as u32;
157 crc = (crc >> 1) ^ (0xEDB88320 & mask);
158 }
159 }
160 !crc
161}
162
163pub struct SteamOpusData<'a> {
165 data: &'a [u8],
166}
167
168impl<'a> SteamOpusData<'a> {
169 pub fn as_slice(&self) -> &'a [u8] {
170 self.data
171 }
172}
173
174impl Debug for SteamOpusData<'_> {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 f.debug_struct("SteamOpusData")
177 .field("data_length", &self.data.len())
178 .finish_non_exhaustive()
179 }
180}