👕 refactor: took apart the decoder and put it back together as an iterator

popping is still there
main
_ 2021-11-13 10:31:58 -06:00
parent 9292f0767e
commit e6c157b556
3 changed files with 110 additions and 74 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
/target /target
*.data
*.m4a *.m4a
*.ogg *.ogg
*.opus *.opus

View File

@ -24,6 +24,7 @@ use ffmpeg_next::{
self, self,
Sample, Sample,
}, },
frame::Audio as AudioFrame,
}, },
}; };
@ -86,7 +87,6 @@ pub struct Decoder {
best_stream_idx: usize, best_stream_idx: usize,
pub decoder: DecodeContext, pub decoder: DecodeContext,
pub resampler: ResamplingContext, pub resampler: ResamplingContext,
resampler_flushed: bool,
} }
impl Decoder { impl Decoder {
@ -110,46 +110,92 @@ impl Decoder {
best_stream_idx, best_stream_idx,
decoder, decoder,
resampler, resampler,
resampler_flushed: false,
}) })
} }
pub fn fill_buffer (&mut self, pcm_buffers: &mut PcmBuffers) -> Result <bool> { fn new_frame () -> AudioFrame {
if ! self.resampler_flushed && self.resampler.delay ().is_some () { let mut x = AudioFrame::empty ();
let mut frame_resampled = ffmpeg_next::util::frame::Audio::empty (); x.set_channel_layout (ChannelLayout::STEREO);
frame_resampled.set_channel_layout (ChannelLayout::STEREO); x.set_format (Sample::F32 (sample::Type::Packed));
frame_resampled.set_format (Sample::F32 (sample::Type::Packed)); x
frame_resampled.set_rate (48000); }
let rc = self.resampler.flush (&mut frame_resampled)?; pub fn next (&mut self) -> Result <Option <AudioFrame>> {
if rc.is_none () { loop {
self.resampler_flushed = true; match self.pump_resampler ()? {
Some (x) => {
// tracing::trace! ("Pumped resampler");
return Ok (Some (x));
},
None => (),
} }
if ! frame_resampled.data (0).is_empty () { match self.pump_decoder ()? {
tracing::trace! ("flushing resampler... {}", frame_resampled.data (0).len ()); Some (x) => {
pcm_buffers.produce_bytes (frame_resampled.data (0)); // tracing::trace! ("Pumped decoder");
return Ok (Some (x));
return Ok (true); },
None => (),
}
if self.pump_demuxer ()? {
// tracing::trace! ("Pumped demuxer");
continue;
}
else {
break;
} }
} }
Ok (None)
}
pub fn pump_resampler (&mut self) -> Result <Option <AudioFrame>> {
let mut frame_resampled = Self::new_frame ();
// dbg! (self.resampler.delay ());
if self.resampler.delay ().is_none () {
return Ok (None);
}
let _rc = self.resampler.flush (&mut frame_resampled)?;
// dbg! (&frame_resampled, rc);
Ok (if frame_resampled.samples () > 0 {
Some (frame_resampled)
}
else {
None
})
}
pub fn pump_decoder (&mut self) -> Result <Option <AudioFrame>> {
let mut frame_src = ffmpeg_next::util::frame::Audio::empty (); let mut frame_src = ffmpeg_next::util::frame::Audio::empty ();
if self.decoder.receive_frame (&mut frame_src).is_ok () { let rc = self.decoder.receive_frame (&mut frame_src);
// tracing::trace! ("decoded frame"); // dbg! (&rc);
let mut frame_resampled = ffmpeg_next::util::frame::Audio::empty (); Ok (if rc.is_ok () {
frame_resampled.set_channel_layout (ChannelLayout::STEREO); // dbg! (&frame_src);
frame_resampled.set_format (Sample::F32 (sample::Type::Packed));
frame_resampled.set_rate (48000); //let nb_output_samples = frame_src.samples () * SAMPLE_RATE as usize / frame_src.rate () as usize;
let nb_output_samples = frame_src.samples ();
let mut frame_resampled = AudioFrame::new (
Sample::F32 (sample::Type::Packed),
nb_output_samples,
ChannelLayout::STEREO
);
self.resampler.run (&frame_src, &mut frame_resampled)?; self.resampler.run (&frame_src, &mut frame_resampled)?;
self.resampler_flushed = false; Some (frame_resampled)
pcm_buffers.produce_bytes (frame_resampled.data (0));
return Ok (true);
} }
else {
//eprintln! ("Decoder is dry, fetching a new packet..."); None
})
}
pub fn pump_demuxer (&mut self) -> Result <bool> {
while let Some ((stream, packet)) = self.input_ctx.packets ().next () { while let Some ((stream, packet)) = self.input_ctx.packets ().next () {
if stream.index () == self.best_stream_idx { if stream.index () == self.best_stream_idx {
// tracing::trace! ("demuxed packet"); // tracing::trace! ("demuxed packet");
@ -158,27 +204,6 @@ impl Decoder {
} }
} }
if self.resampler_flushed {
return Ok (false);
}
let mut frame_resampled = ffmpeg_next::util::frame::Audio::empty ();
frame_resampled.set_channel_layout (ChannelLayout::STEREO);
frame_resampled.set_format (Sample::F32 (sample::Type::Packed));
frame_resampled.set_rate (48000);
let rc = self.resampler.flush (&mut frame_resampled)?;
if rc.is_none () {
self.resampler_flushed = true;
}
if ! frame_resampled.data (0).is_empty () {
tracing::trace! ("flushing resampler... {}", frame_resampled.data (0).len ());
pcm_buffers.produce_bytes (frame_resampled.data (0));
return Ok (true);
}
Ok (false) Ok (false)
} }
} }

View File

@ -36,13 +36,14 @@ fn main () -> Result <()> {
match args.get (1).map (|s| &s[..]) { match args.get (1).map (|s| &s[..]) {
None => bail! ("First argument must be a subcommand like `play`"), None => bail! ("First argument must be a subcommand like `play`"),
Some ("debug") => cmd_debug (&args [1..]), Some ("debug-dump") => cmd_debug_dump (&args [1..]),
Some ("debug-pipe") => cmd_debug_pipe (&args [1..]),
Some ("play") => cmd_play (&args [1..]), Some ("play") => cmd_play (&args [1..]),
Some (_) => bail! ("Unrecognized subcommand"), Some (_) => bail! ("Unrecognized subcommand"),
} }
} }
fn cmd_debug (args: &[String]) -> Result <()> { fn cmd_debug_dump (args: &[String]) -> Result <()> {
tracing_subscriber::fmt::init (); tracing_subscriber::fmt::init ();
let filename = args.get (1) let filename = args.get (1)
@ -50,33 +51,39 @@ fn cmd_debug (args: &[String]) -> Result <()> {
.unwrap_or_else (|| "test-short.m4a".to_string ()); .unwrap_or_else (|| "test-short.m4a".to_string ());
let mut decoder = decoder::Decoder::new (&filename)?; let mut decoder = decoder::Decoder::new (&filename)?;
let mut pcm_buffers = decoder::PcmBuffers::default ();
tracing::debug! ("Decoding..."); tracing::debug! ("Decoding...");
let mut f = File::create ("pcm-dump.data")?; let mut f = File::create ("pcm-dump.data")?;
'decoding: loop { while let Some (frame) = decoder.next ()? {
while pcm_buffers.samples_available () < 12_000 { f.write_all (frame.data (0))?;
if ! decoder.fill_buffer (&mut pcm_buffers)? {
tracing::info! ("Decoder finished");
break 'decoding;
}
}
while pcm_buffers.samples_available () > 0 {
let mut src = vec! [0.0f32; pcm_buffers.samples_available ()];
// dbg! (src.len ());
pcm_buffers.consume_exact (&mut src);
let mut dest = vec! [0; src.len () * 4];
LittleEndian::write_f32_into (&src, &mut dest);
f.write_all (&dest)?;
}
} }
Ok (()) Ok (())
} }
fn cmd_debug_pipe (args: &[String]) -> Result <()> {
tracing_subscriber::fmt::init ();
let filename = args.get (1)
.map (|s| s.to_string ())
.unwrap_or_else (|| "test-short.m4a".to_string ());
let mut decoder = decoder::Decoder::new (&filename)?;
let mut sample_count = 0;
while let Some (frame) = decoder.next ()? {
sample_count += frame.samples ();
assert_eq! (frame.rate (), 48000);
assert! (frame.samples () > 0);
// dbg! (frame, sample_count);
}
dbg! (sample_count);
Ok (())
}
fn cmd_play (args: &[String]) -> Result <()> { fn cmd_play (args: &[String]) -> Result <()> {
tracing_subscriber::fmt::init (); tracing_subscriber::fmt::init ();
@ -114,9 +121,12 @@ fn cmd_play (args: &[String]) -> Result <()> {
while pcm_buffers.samples_available () < 12_000 { while pcm_buffers.samples_available () < 12_000 {
// tracing::trace! ("Decoder is trying to work..."); // tracing::trace! ("Decoder is trying to work...");
if ! decoder.fill_buffer (pcm_buffers)? { match decoder.next ()? {
tracing::info! ("Finished decoding file"); Some (frame) => pcm_buffers.produce_bytes (frame.data (0)),
break 'one_file; None => {
tracing::info! ("Finished decoding file");
break 'one_file;
}
} }
} }
} }
@ -151,7 +161,7 @@ fn cmd_play (args: &[String]) -> Result <()> {
}; };
let time_stop = Instant::now (); let time_stop = Instant::now ();
let dur = time_stop - time_start; let dur = time_stop - time_start;
if dur.as_micros () > 1 { if dur.as_micros () > 2 {
dbg! (dur.as_micros ()); dbg! (dur.as_micros ());
} }