1use oxideav_core::{
23 AudioFrame, CodecCapabilities, CodecId, CodecParameters, CodecTag, Error, Frame, MediaType,
24 Packet, ProbeContext, Result, SampleFormat, TimeBase,
25};
26use oxideav_core::{CodecInfo, CodecRegistry, Decoder, Encoder};
27
28pub fn register(reg: &mut CodecRegistry) {
29 let wf_int = CodecTag::wave_format(0x0001);
34 let wf_flt = CodecTag::wave_format(0x0003);
35 for (id, bits, tag, probe) in [
36 (
37 "pcm_u8",
38 8u16,
39 Some(&wf_int),
40 probe_pcm_u8 as oxideav_core::ProbeFn,
41 ),
42 ("pcm_s8", 8, None, probe_pcm_s8 as oxideav_core::ProbeFn),
43 (
44 "pcm_s16le",
45 16,
46 Some(&wf_int),
47 probe_pcm_s16le as oxideav_core::ProbeFn,
48 ),
49 (
50 "pcm_s24le",
51 24,
52 Some(&wf_int),
53 probe_pcm_s24le as oxideav_core::ProbeFn,
54 ),
55 (
56 "pcm_s32le",
57 32,
58 Some(&wf_int),
59 probe_pcm_s32le as oxideav_core::ProbeFn,
60 ),
61 (
62 "pcm_f32le",
63 32,
64 Some(&wf_flt),
65 probe_pcm_f32le as oxideav_core::ProbeFn,
66 ),
67 (
68 "pcm_f64le",
69 64,
70 Some(&wf_flt),
71 probe_pcm_f64le as oxideav_core::ProbeFn,
72 ),
73 ] {
74 let _ = bits;
75 let caps = CodecCapabilities::audio(format!("{id}_sw"))
76 .with_lossless(true)
77 .with_intra_only(true);
78 let mut info = CodecInfo::new(CodecId::new(id))
79 .capabilities(caps)
80 .decoder(make_decoder)
81 .encoder(make_encoder);
82 if let Some(t) = tag {
83 info = info.probe(probe).tag(t.clone());
84 }
85 reg.register(info);
86 }
87
88 for id in SLIN_ALIASES {
89 let caps = CodecCapabilities::audio(format!("{id}_sw"))
90 .with_lossless(true)
91 .with_intra_only(true);
92 reg.register(
95 CodecInfo::new(CodecId::new(*id))
96 .capabilities(caps)
97 .decoder(make_decoder)
98 .encoder(make_encoder),
99 );
100 }
101}
102
103fn match_bits(ctx: &ProbeContext, expected: u16) -> f32 {
110 match ctx.bits_per_sample {
111 Some(b) if b == expected => 1.0,
112 _ => 0.0,
113 }
114}
115
116fn probe_pcm_u8(ctx: &ProbeContext) -> f32 {
117 match_bits(ctx, 8)
118}
119fn probe_pcm_s8(_ctx: &ProbeContext) -> f32 {
120 0.0
123}
124fn probe_pcm_s16le(ctx: &ProbeContext) -> f32 {
125 match ctx.bits_per_sample {
126 Some(0) => 0.5,
132 Some(16) => 1.0,
133 _ => 0.0,
134 }
135}
136fn probe_pcm_s24le(ctx: &ProbeContext) -> f32 {
137 match_bits(ctx, 24)
138}
139fn probe_pcm_s32le(ctx: &ProbeContext) -> f32 {
140 match_bits(ctx, 32)
141}
142fn probe_pcm_f32le(ctx: &ProbeContext) -> f32 {
143 match ctx.bits_per_sample {
144 None | Some(0) => 0.5,
146 Some(32) => 1.0,
147 _ => 0.0,
148 }
149}
150fn probe_pcm_f64le(ctx: &ProbeContext) -> f32 {
151 match_bits(ctx, 64)
152}
153
154pub(crate) const SLIN_ALIASES: &[&str] = &[
157 "slin", "slin8", "slin16", "slin24", "slin32", "slin44", "slin48", "slin96", "slin192",
158];
159
160pub fn sample_format_for(id: &CodecId) -> Option<SampleFormat> {
165 let s = id.as_str();
166 Some(match s {
167 "pcm_u8" => SampleFormat::U8,
168 "pcm_s8" => SampleFormat::S8,
169 "pcm_s16le" => SampleFormat::S16,
170 "pcm_s24le" => SampleFormat::S24,
171 "pcm_s32le" => SampleFormat::S32,
172 "pcm_f32le" => SampleFormat::F32,
173 "pcm_f64le" => SampleFormat::F64,
174 _ if SLIN_ALIASES.contains(&s) => SampleFormat::S16,
175 _ => return None,
176 })
177}
178
179pub fn codec_id_for(fmt: SampleFormat) -> Option<CodecId> {
182 Some(CodecId::new(match fmt {
183 SampleFormat::U8 => "pcm_u8",
184 SampleFormat::S8 => "pcm_s8",
185 SampleFormat::S16 => "pcm_s16le",
186 SampleFormat::S24 => "pcm_s24le",
187 SampleFormat::S32 => "pcm_s32le",
188 SampleFormat::F32 => "pcm_f32le",
189 SampleFormat::F64 => "pcm_f64le",
190 _ => return None,
191 }))
192}
193
194fn make_decoder(params: &CodecParameters) -> Result<Box<dyn Decoder>> {
195 let format = sample_format_for(¶ms.codec_id)
196 .ok_or_else(|| Error::CodecNotFound(params.codec_id.to_string()))?;
197 let channels = params
198 .channels
199 .ok_or_else(|| Error::invalid("PCM decoder requires channels"))?;
200 let sample_rate = params
201 .sample_rate
202 .ok_or_else(|| Error::invalid("PCM decoder requires sample_rate"))?;
203 Ok(Box::new(PcmDecoder {
204 id: params.codec_id.clone(),
205 format,
206 channels,
207 sample_rate,
208 pending: None,
209 eof: false,
210 }))
211}
212
213fn make_encoder(params: &CodecParameters) -> Result<Box<dyn Encoder>> {
214 let format = sample_format_for(¶ms.codec_id)
215 .ok_or_else(|| Error::CodecNotFound(params.codec_id.to_string()))?;
216 let channels = params
217 .channels
218 .ok_or_else(|| Error::invalid("PCM encoder requires channels"))?;
219 let sample_rate = params
220 .sample_rate
221 .ok_or_else(|| Error::invalid("PCM encoder requires sample_rate"))?;
222 let mut output = params.clone();
223 output.media_type = MediaType::Audio;
224 output.sample_format = Some(format);
225 Ok(Box::new(PcmEncoder {
226 format,
227 channels,
228 sample_rate,
229 output,
230 queue: std::collections::VecDeque::new(),
231 }))
232}
233
234struct PcmDecoder {
235 id: CodecId,
236 format: SampleFormat,
237 channels: u16,
238 sample_rate: u32,
239 pending: Option<Packet>,
240 eof: bool,
241}
242
243impl Decoder for PcmDecoder {
244 fn codec_id(&self) -> &CodecId {
245 &self.id
246 }
247
248 fn send_packet(&mut self, packet: &Packet) -> Result<()> {
249 if self.pending.is_some() {
250 return Err(Error::other(
251 "PCM decoder already has a buffered packet; call receive_frame first",
252 ));
253 }
254 self.pending = Some(packet.clone());
255 Ok(())
256 }
257
258 fn receive_frame(&mut self) -> Result<Frame> {
259 let Some(pkt) = self.pending.take() else {
260 return if self.eof {
261 Err(Error::Eof)
262 } else {
263 Err(Error::NeedMore)
264 };
265 };
266 let bps = self.format.bytes_per_sample();
267 let block = bps * self.channels as usize;
268 if block == 0 || pkt.data.len() % block != 0 {
269 return Err(Error::invalid("PCM packet size not a multiple of block"));
270 }
271 let samples = (pkt.data.len() / block) as u32;
272 Ok(Frame::Audio(AudioFrame {
273 format: self.format,
274 channels: self.channels,
275 sample_rate: self.sample_rate,
276 samples,
277 pts: pkt.pts,
278 time_base: pkt.time_base,
279 data: vec![pkt.data],
280 }))
281 }
282
283 fn flush(&mut self) -> Result<()> {
284 self.eof = true;
285 Ok(())
286 }
287}
288
289struct PcmEncoder {
290 format: SampleFormat,
291 channels: u16,
292 sample_rate: u32,
293 output: CodecParameters,
294 queue: std::collections::VecDeque<Packet>,
295}
296
297impl Encoder for PcmEncoder {
298 fn codec_id(&self) -> &CodecId {
299 &self.output.codec_id
300 }
301
302 fn output_params(&self) -> &CodecParameters {
303 &self.output
304 }
305
306 fn send_frame(&mut self, frame: &Frame) -> Result<()> {
307 let Frame::Audio(a) = frame else {
308 return Err(Error::invalid("PCM encoder requires audio frames"));
309 };
310 if a.format != self.format
311 || a.channels != self.channels
312 || a.sample_rate != self.sample_rate
313 {
314 return Err(Error::invalid(
315 "PCM encoder frame parameters do not match encoder configuration",
316 ));
317 }
318 if a.format.is_planar() {
319 return Err(Error::unsupported(
320 "PCM encoder takes interleaved input; convert planar → interleaved first",
321 ));
322 }
323 let data = a
324 .data
325 .first()
326 .ok_or_else(|| Error::invalid("empty audio frame"))?
327 .clone();
328 let bps = a.format.bytes_per_sample() * a.channels as usize;
329 let expected = bps * a.samples as usize;
330 if data.len() != expected {
331 return Err(Error::invalid("audio frame data length mismatch"));
332 }
333 let mut pkt = Packet::new(0, a.time_base, data);
334 pkt.pts = a.pts;
335 pkt.dts = a.pts;
336 pkt.duration = Some(a.samples as i64);
337 pkt.flags.keyframe = true;
338 self.queue.push_back(pkt);
339 Ok(())
340 }
341
342 fn receive_packet(&mut self) -> Result<Packet> {
343 self.queue.pop_front().ok_or(Error::NeedMore)
344 }
345
346 fn flush(&mut self) -> Result<()> {
347 Ok(())
348 }
349}
350
351pub fn params(format: SampleFormat, channels: u16, sample_rate: u32) -> Result<CodecParameters> {
353 let codec_id = codec_id_for(format)
354 .ok_or_else(|| Error::unsupported(format!("no PCM codec for {:?}", format)))?;
355 let mut p = CodecParameters::audio(codec_id);
356 p.sample_format = Some(format);
357 p.channels = Some(channels);
358 p.sample_rate = Some(sample_rate);
359 p.bit_rate =
360 Some((format.bytes_per_sample() as u64) * 8 * (channels as u64) * (sample_rate as u64));
361 Ok(p)
362}
363
364pub fn time_base_for(sample_rate: u32) -> TimeBase {
366 TimeBase::new(1, sample_rate as i64)
367}