#[cfg(feature = "named_pipes")]
fn main() -> anyhow::Result<()> {
use anyhow::Result;
use ffmpeg_sidecar::command::FfmpegCommand;
use ffmpeg_sidecar::event::{FfmpegEvent, LogLevel};
use ffmpeg_sidecar::named_pipes::NamedPipe;
use ffmpeg_sidecar::pipe_name;
use std::io::Read;
use std::sync::mpsc;
use std::thread;
const VIDEO_PIPE_NAME: &str = pipe_name!("ffmpeg_video");
const AUDIO_PIPE_NAME: &str = pipe_name!("ffmpeg_audio");
const SUBTITLES_PIPE_NAME: &str = pipe_name!("ffmpeg_subtitles");
let mut command = FfmpegCommand::new();
command
.hide_banner()
.overwrite() .format("lavfi")
.input("testsrc=size=1920x1080:rate=60:duration=10")
.format("lavfi")
.input("sine=frequency=1000:duration=10")
.format("srt")
.input(
"data:text/plain;base64,MQ0KMDA6MDA6MDAsMDAwIC0tPiAwMDowMDoxMCw1MDANCkhlbGxvIFdvcmxkIQ==",
)
.map("0:v")
.format("rawvideo")
.pix_fmt("rgb24")
.output(VIDEO_PIPE_NAME)
.map("1:a")
.format("s16le")
.output(AUDIO_PIPE_NAME)
.map("2:s")
.format("srt")
.output(SUBTITLES_PIPE_NAME);
let threads = [VIDEO_PIPE_NAME, AUDIO_PIPE_NAME, SUBTITLES_PIPE_NAME]
.iter()
.cloned()
.map(|pipe_name| {
let mut pipe = NamedPipe::new(pipe_name)?;
println!("[{pipe_name}] pipe created");
let (ready_sender, ready_receiver) = mpsc::channel::<()>();
let thread = thread::spawn(move || -> Result<()> {
println!("[{pipe_name}] waiting for ready signal");
ready_receiver.recv()?;
println!("[{pipe_name}] reading from pipe");
let mut buf = vec![0; 1920 * 1080 * 3];
let mut total_bytes_read = 0;
let mut text_content = if pipe_name == SUBTITLES_PIPE_NAME {
Some("".to_string())
} else {
None
};
loop {
match pipe.read(&mut buf) {
Ok(bytes_read) => {
total_bytes_read += bytes_read;
if let Some(cur_str) = &mut text_content {
let s = std::str::from_utf8(&buf[..bytes_read]).unwrap();
text_content = Some(format!("{}{}", cur_str, s));
}
if bytes_read == 0 {
break;
}
}
Err(err) => {
if err.kind() != std::io::ErrorKind::BrokenPipe {
return Err(err.into());
} else {
break;
}
}
}
}
let size_str = if total_bytes_read < 1024 {
format!("{}B", total_bytes_read)
} else {
format!("{}KiB", total_bytes_read / 1024)
};
if let Some(text_content) = text_content {
println!("[{pipe_name}] subtitle text content: ");
println!("{}", text_content.trim());
}
println!("[{pipe_name}] done reading ({size_str} total)");
Ok(())
});
Ok((thread, ready_sender))
})
.collect::<Result<Vec<_>>>()?;
let mut ready_signal_sent = false;
command
.print_command()
.spawn()?
.iter()?
.for_each(|event| match event {
FfmpegEvent::Progress(_) if !ready_signal_sent => {
threads.iter().for_each(|(_, sender)| {
sender.send(()).ok();
});
ready_signal_sent = true;
}
FfmpegEvent::Log(LogLevel::Info, msg) if msg.starts_with("[out#") => {
println!("{msg}");
}
FfmpegEvent::Log(LogLevel::Warning | LogLevel::Error | LogLevel::Fatal, msg) => {
eprintln!("{msg}");
}
_ => {}
});
for (thread, _) in threads {
thread.join().unwrap()?;
}
Ok(())
}
#[cfg(not(feature = "named_pipes"))]
fn main() {
eprintln!(r#"Enable the "named_pipes" feature to run this example."#);
println!("cargo run --features named_pipes --example named_pipes")
}