1pub use crate::error::SteamAudioError;
2use opus::{Channels, Decoder};
3use std::fmt::Debug;
4
5mod error;
6
7#[derive(Debug)]
8#[repr(u8)]
9enum PacketType {
10 Silence = 0,
11 OpusPlc = 6,
12 SampleRate = 11,
13}
14
15impl TryFrom<u8> for PacketType {
16 type Error = SteamAudioError;
17
18 fn try_from(value: u8) -> Result<Self, Self::Error> {
19 match value {
20 0 => Ok(Self::Silence),
21 6 => Ok(Self::OpusPlc),
22 11 => Ok(Self::SampleRate),
23 _ => Err(SteamAudioError::UnknownPacketType { ty: value }),
24 }
25 }
26}
27
28fn read_bytes<const N: usize>(data: &[u8]) -> Result<([u8; N], &[u8]), SteamAudioError> {
29 if data.len() < N {
30 Err(SteamAudioError::InsufficientData)
31 } else {
32 let (result, rest) = data.split_at(N);
33 Ok((result.try_into().unwrap(), rest))
34 }
35}
36
37fn read_u16(data: &[u8]) -> Result<(u16, &[u8]), SteamAudioError> {
38 let (bytes, data) = read_bytes(data)?;
39 Ok((u16::from_le_bytes(bytes), data))
40}
41
42#[derive(Debug)]
43pub enum Packet<'a> {
44 Silence(u16),
46 OpusPlc(SteamOpusData<'a>),
48 SampleRate(u16),
50}
51
52impl<'a> Packet<'a> {
53 pub fn read(data: &'a [u8]) -> Result<(Self, &'a [u8]), SteamAudioError> {
54 let ty = PacketType::try_from(*data.first().ok_or(SteamAudioError::InsufficientData)?)?;
55 let data = &data[1..];
56
57 let (next, data) = read_u16(data)?;
58
59 Ok(match ty {
60 PacketType::Silence => (Packet::Silence(next), data),
61 PacketType::OpusPlc => {
62 if data.len() < next as usize {
63 return Err(SteamAudioError::InsufficientData);
64 } else {
65 let (result, data) = data.split_at(next as usize);
66 (Packet::OpusPlc(SteamOpusData { data: result }), data)
67 }
68 }
69 PacketType::SampleRate => (Packet::SampleRate(next), data),
70 })
71 }
72}
73
74#[derive(Debug)]
75pub struct SteamVoiceData<'a> {
76 pub steam_id: u64,
77 packet_data: &'a [u8],
78}
79
80impl<'a> SteamVoiceData<'a> {
81 pub fn new(data: &'a [u8]) -> Result<Self, SteamAudioError> {
82 let (data, crc_data) = data.split_at(data.len() - 4);
83 let expected_crc = u32::from_le_bytes(crc_data.try_into().unwrap());
84 let calculated_crc = crc32b(data);
85 if expected_crc != calculated_crc {
86 return Err(SteamAudioError::CrcMismatch {
87 actual: calculated_crc,
88 expected: expected_crc,
89 });
90 }
91
92 let (steam_id_bytes, data) = read_bytes(data)?;
93 let steam_id = u64::from_le_bytes(steam_id_bytes);
94 Ok(SteamVoiceData {
95 steam_id,
96 packet_data: data,
97 })
98 }
99
100 pub fn packets(&self) -> impl Iterator<Item = Result<Packet<'a>, SteamAudioError>> {
102 SteamPacketIterator {
103 data: self.packet_data,
104 }
105 }
106}
107
108struct SteamPacketIterator<'a> {
109 data: &'a [u8],
110}
111
112impl Debug for SteamPacketIterator<'_> {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 f.debug_struct("SteamPacketIterator")
115 .field("data_length", &self.data.len())
116 .finish_non_exhaustive()
117 }
118}
119
120impl<'a> Iterator for SteamPacketIterator<'a> {
121 type Item = Result<Packet<'a>, SteamAudioError>;
122
123 fn next(&mut self) -> Option<Self::Item> {
124 if self.data.is_empty() {
125 None
126 } else {
127 match Packet::read(self.data) {
128 Ok((packet, rest)) => {
129 self.data = rest;
130 Some(Ok(packet))
131 }
132 Err(e) => Some(Err(e)),
133 }
134 }
135 }
136}
137
138fn crc32b(data: &[u8]) -> u32 {
139 let mut crc: u32 = 0xFFFFFFFF;
140 for &byte in data {
141 crc ^= byte as u32;
142 for _ in 0..8 {
143 let mask = (-((crc & 1) as i32)) as u32;
144 crc = (crc >> 1) ^ (0xEDB88320 & mask);
145 }
146 }
147 !crc
148}
149
150#[derive(Default)]
151pub struct SteamVoiceDecoder {
152 decoder: Option<Decoder>,
153 sample_rate: u16,
154 seq: u16,
155}
156
157pub struct SteamOpusData<'a> {
158 data: &'a [u8],
159}
160
161impl Debug for SteamOpusData<'_> {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 f.debug_struct("SteamOpusData")
164 .field("data_length", &self.data.len())
165 .finish_non_exhaustive()
166 }
167}
168
169impl SteamVoiceDecoder {
170 pub fn new() -> Self {
171 Self::default()
172 }
173
174 pub fn decode(
175 &mut self,
176 voice_data: SteamVoiceData,
177 output_buffer: &mut [i16],
178 ) -> Result<usize, SteamAudioError> {
179 let mut total = 0;
180 for packet in voice_data.packets() {
181 let packet = packet?;
182 match packet {
183 Packet::SampleRate(rate) => {
184 if self.sample_rate != rate {
185 self.decoder = Some(Decoder::new(rate as u32, Channels::Mono)?);
186 self.sample_rate = rate;
187 }
188 }
189 Packet::OpusPlc(opus) => {
190 let count = self.decode_opus(opus.data, &mut output_buffer[total..])?;
191 total += count;
192 if total >= output_buffer.len() {
193 return Err(SteamAudioError::InsufficientOutputBuffer);
194 }
195 }
196 Packet::Silence(silence) => {
197 total += silence as usize;
198 }
199 }
200 }
201 Ok(total)
202 }
203
204 fn decode_opus(
205 &mut self,
206 mut data: &[u8],
207 output_buffer: &mut [i16],
208 ) -> Result<usize, SteamAudioError> {
209 let mut total = 0;
210 let Some(decoder) = self.decoder.as_mut() else {
211 return Err(SteamAudioError::NoSampleRate);
212 };
213
214 while data.len() > 2 {
215 let (len, remainder) = read_u16(data)?;
216 data = remainder;
217 if len == u16::MAX {
218 decoder.reset_state()?;
219 self.seq = 0;
220 continue;
221 }
222 let (seq, remainder) = read_u16(data)?;
223 data = remainder;
224
225 if seq < self.seq {
226 decoder.reset_state()?;
227 } else {
228 let lost = seq - self.seq;
229 for _ in 0..lost {
230 let count = decoder.decode(&[], &mut output_buffer[total..], false)?;
231 total += count;
232 if total >= output_buffer.len() {
233 return Err(SteamAudioError::InsufficientOutputBuffer);
234 }
235 }
236 }
237 let len = len as usize;
238
239 self.seq = seq + 1;
240
241 if data.len() < len {
242 return Err(SteamAudioError::InsufficientData);
243 }
244
245 let count = decoder.decode(&data[0..len], &mut output_buffer[total..], false)?;
246 data = &data[len..];
247 total += count;
248 if total >= output_buffer.len() {
249 return Err(SteamAudioError::InsufficientOutputBuffer);
250 }
251 }
252
253 Ok(total)
254 }
255}