ffmpeg subprocess creation, remove byte handled checking

This commit is contained in:
Muaz Ahmad 2024-12-06 16:03:33 +05:00
parent 5aa12757a0
commit 58d33b8868
7 changed files with 56 additions and 23 deletions

View file

@ -1,5 +1,35 @@
use std::{
io::{Read, Write},
process::{Child, ChildStdin, ChildStdout, Command, Stdio},
};
use crate::utils::Error; use crate::utils::Error;
pub fn init() -> Result<(), Error> { pub struct DecoderContext {
unimplemented!() process: Child,
encoded_in: ChildStdin,
frames_out: ChildStdout,
}
pub fn init() -> Result<DecoderContext, Error> {
let mut decoder = Command::new("ffmpeg")
.args(["-hide_banner", "-i", "-", "-f", "s16le", "-"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()?;
let encoded_in = decoder.stdin.take().unwrap();
let frames_out = decoder.stdout.take().unwrap();
Ok(DecoderContext {
process: decoder,
encoded_in,
frames_out,
})
}
impl DecoderContext {
pub fn append_chunk(&mut self, chunk: Vec<u8>) -> Result<(), Error> {
self.encoded_in.write_all(chunk.as_slice())?;
Ok(())
}
} }

View file

@ -8,7 +8,7 @@ use std::{
use std::sync::mpsc::Receiver; use std::sync::mpsc::Receiver;
use pipewire::spa::pod::Pod; use codec::DecoderContext;
use crate::{config::Settings, player::PlayerEvent, utils::Error}; use crate::{config::Settings, player::PlayerEvent, utils::Error};
@ -23,6 +23,8 @@ pub struct SoundManager {
player_chan: Sender<PlayerEvent>, player_chan: Sender<PlayerEvent>,
sample_in: Sender<Vec<u8>>, sample_in: Sender<Vec<u8>>,
pw_signal_in: pipewire::channel::Sender<Vec<u8>>, pw_signal_in: pipewire::channel::Sender<Vec<u8>>,
decoder_context: DecoderContext,
playing: bool,
} }
pub fn init( pub fn init(

View file

@ -1,6 +1,6 @@
use std::{ use std::{
sync::{ sync::{
mpsc::{channel, Receiver, Sender}, mpsc::{channel, Receiver, Sender, TryRecvError},
Arc, Arc,
}, },
thread, thread,
@ -31,6 +31,7 @@ impl SoundManager {
}; };
}); });
let decoder_context = codec::init()?; let decoder_context = codec::init()?;
Ok(SoundManager { Ok(SoundManager {
settings, settings,
error_chan, error_chan,
@ -38,14 +39,26 @@ impl SoundManager {
player_chan, player_chan,
sample_in, sample_in,
pw_signal_in, pw_signal_in,
decoder_context,
playing: true,
}) })
} }
pub fn begin(&mut self) -> Result<(), Error> { pub fn begin(&mut self) -> Result<(), Error> {
loop { loop {
match self.audio_controls.recv()? { self.handle_events()?;
AudioEvent::DecodeChunk(chunk) => (),
_ => unimplemented!(),
}
} }
} }
fn handle_events(&mut self) -> Result<(), Error> {
let event = match self.audio_controls.try_recv() {
Err(err) if err == TryRecvError::Empty => return Ok(()),
Ok(e) => e,
Err(err) => return Err(Box::new(err)),
};
match event {
AudioEvent::DecodeChunk(chunk) => self.decoder_context.append_chunk(chunk)?,
_ => unimplemented!(),
}
Ok(())
}
} }

View file

@ -12,7 +12,6 @@ pub struct Metadata {
pub cover: DynamicImage, pub cover: DynamicImage,
pub size: u32, pub size: u32,
pub bytes_received: u32, pub bytes_received: u32,
pub bytes_handled: u32,
} }
impl Metadata { impl Metadata {
@ -27,7 +26,6 @@ impl Metadata {
cover: default_cover(), cover: default_cover(),
size: 0, size: 0,
bytes_received: 0, bytes_received: 0,
bytes_handled: 0,
} }
} }

View file

@ -24,6 +24,7 @@ pub enum PlayerEvent {
UserQuit, UserQuit,
PlayNext, PlayNext,
AddAudioChunk(Vec<u8>), AddAudioChunk(Vec<u8>),
AudioChunkQueued(u32),
} }
pub struct Player { pub struct Player {

View file

@ -46,6 +46,7 @@ impl Player {
PlayerEvent::PlayNext => self.play_next()?, PlayerEvent::PlayNext => self.play_next()?,
PlayerEvent::UpdateCover(cover) => self.tui_root.update_cover(cover), PlayerEvent::UpdateCover(cover) => self.tui_root.update_cover(cover),
PlayerEvent::AddAudioChunk(chunk) => self.recv_chunk(chunk)?, PlayerEvent::AddAudioChunk(chunk) => self.recv_chunk(chunk)?,
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
@ -100,19 +101,8 @@ impl Player {
} }
fn recv_chunk(&mut self, chunk: Vec<u8>) -> Result<(), Error> { fn recv_chunk(&mut self, chunk: Vec<u8>) -> Result<(), Error> {
if self.tui_root.metadata.bytes_received - self.tui_root.metadata.bytes_handled
> AUDIO_FILE_BUFFER
{
// requeue the new chunk since we don't need it decoded just yet. Mostly for massive hr+ files
self.player_chan_in
.send(PlayerEvent::AddAudioChunk(chunk))?;
return Ok(());
}
self.tui_root.metadata.bytes_received += chunk.len() as u32; self.tui_root.metadata.bytes_received += chunk.len() as u32;
self.audio_chan.send(AudioEvent::DecodeChunk(chunk))?; self.audio_chan.send(AudioEvent::DecodeChunk(chunk))?;
if self.tui_root.metadata.bytes_pending() {
self.fetch_audio_chunk()?;
}
Ok(()) Ok(())
} }
} }

View file

@ -1,5 +1,3 @@
use std::io::Cursor;
use image::DynamicImage; use image::DynamicImage;
use pipewire::spa::{ use pipewire::spa::{
param::audio::AudioInfoRaw, param::audio::AudioInfoRaw,
@ -7,6 +5,7 @@ use pipewire::spa::{
sys::{SPA_PARAM_EnumFormat, SPA_TYPE_OBJECT_Format}, sys::{SPA_PARAM_EnumFormat, SPA_TYPE_OBJECT_Format},
}; };
use rand::{distributions::Alphanumeric, thread_rng, Rng}; use rand::{distributions::Alphanumeric, thread_rng, Rng};
use std::io::Cursor;
pub type Error = Box<dyn std::error::Error + Send + Sync>; pub type Error = Box<dyn std::error::Error + Send + Sync>;