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 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 pending: None,
208 eof: false,
209 }))
210}
211
212fn make_encoder(params: &CodecParameters) -> Result<Box<dyn Encoder>> {
213 let format = sample_format_for(¶ms.codec_id)
214 .ok_or_else(|| Error::CodecNotFound(params.codec_id.to_string()))?;
215 let channels = params
216 .channels
217 .ok_or_else(|| Error::invalid("PCM encoder requires channels"))?;
218 let sample_rate = params
219 .sample_rate
220 .ok_or_else(|| Error::invalid("PCM encoder requires sample_rate"))?;
221 let mut output = params.clone();
222 output.media_type = MediaType::Audio;
223 output.sample_format = Some(format);
224 Ok(Box::new(PcmEncoder {
225 format,
226 channels,
227 sample_rate,
228 output,
229 queue: std::collections::VecDeque::new(),
230 }))
231}
232
233struct PcmDecoder {
234 id: CodecId,
235 format: SampleFormat,
236 channels: u16,
237 pending: Option<Packet>,
238 eof: bool,
239}
240
241impl Decoder for PcmDecoder {
242 fn codec_id(&self) -> &CodecId {
243 &self.id
244 }
245
246 fn send_packet(&mut self, packet: &Packet) -> Result<()> {
247 if self.pending.is_some() {
248 return Err(Error::other(
249 "PCM decoder already has a buffered packet; call receive_frame first",
250 ));
251 }
252 self.pending = Some(packet.clone());
253 Ok(())
254 }
255
256 fn receive_frame(&mut self) -> Result<Frame> {
257 let Some(pkt) = self.pending.take() else {
258 return if self.eof {
259 Err(Error::Eof)
260 } else {
261 Err(Error::NeedMore)
262 };
263 };
264 let bps = self.format.bytes_per_sample();
265 let block = bps * self.channels as usize;
266 if block == 0 || pkt.data.len() % block != 0 {
267 return Err(Error::invalid("PCM packet size not a multiple of block"));
268 }
269 let samples = (pkt.data.len() / block) as u32;
270 Ok(Frame::Audio(AudioFrame {
271 samples,
272 pts: pkt.pts,
273 data: vec![pkt.data],
274 }))
275 }
276
277 fn flush(&mut self) -> Result<()> {
278 self.eof = true;
279 Ok(())
280 }
281}
282
283struct PcmEncoder {
284 format: SampleFormat,
285 channels: u16,
286 sample_rate: u32,
287 output: CodecParameters,
288 queue: std::collections::VecDeque<Packet>,
289}
290
291impl Encoder for PcmEncoder {
292 fn codec_id(&self) -> &CodecId {
293 &self.output.codec_id
294 }
295
296 fn output_params(&self) -> &CodecParameters {
297 &self.output
298 }
299
300 fn send_frame(&mut self, frame: &Frame) -> Result<()> {
301 let Frame::Audio(a) = frame else {
302 return Err(Error::invalid("PCM encoder requires audio frames"));
303 };
304 if self.format.is_planar() {
308 return Err(Error::unsupported(
309 "PCM encoder takes interleaved input; convert planar → interleaved first",
310 ));
311 }
312 let data = a
313 .data
314 .first()
315 .ok_or_else(|| Error::invalid("empty audio frame"))?
316 .clone();
317 let bps = self.format.bytes_per_sample() * self.channels as usize;
318 let expected = bps * a.samples as usize;
319 if data.len() != expected {
320 return Err(Error::invalid("audio frame data length mismatch"));
321 }
322 let mut pkt = Packet::new(
323 0,
324 oxideav_core::TimeBase::new(1, self.sample_rate as i64),
325 data,
326 );
327 pkt.pts = a.pts;
328 pkt.dts = a.pts;
329 pkt.duration = Some(a.samples as i64);
330 pkt.flags.keyframe = true;
331 self.queue.push_back(pkt);
332 Ok(())
333 }
334
335 fn receive_packet(&mut self) -> Result<Packet> {
336 self.queue.pop_front().ok_or(Error::NeedMore)
337 }
338
339 fn flush(&mut self) -> Result<()> {
340 Ok(())
341 }
342}
343
344pub fn params(format: SampleFormat, channels: u16, sample_rate: u32) -> Result<CodecParameters> {
346 let codec_id = codec_id_for(format)
347 .ok_or_else(|| Error::unsupported(format!("no PCM codec for {:?}", format)))?;
348 let mut p = CodecParameters::audio(codec_id);
349 p.sample_format = Some(format);
350 p.channels = Some(channels);
351 p.sample_rate = Some(sample_rate);
352 p.bit_rate =
353 Some((format.bytes_per_sample() as u64) * 8 * (channels as u64) * (sample_rate as u64));
354 Ok(p)
355}
356
357pub fn time_base_for(sample_rate: u32) -> TimeBase {
359 TimeBase::new(1, sample_rate as i64)
360}