diff options
Diffstat (limited to 'transcoder/src/fragment.rs')
-rw-r--r-- | transcoder/src/fragment.rs | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/transcoder/src/fragment.rs b/transcoder/src/fragment.rs new file mode 100644 index 0000000..90512c4 --- /dev/null +++ b/transcoder/src/fragment.rs @@ -0,0 +1,102 @@ +/* + This file is part of jellything (https://codeberg.org/metamuffin/jellything) + which is licensed under the GNU Affero General Public License (version 3); see /COPYING. + Copyright (C) 2024 metamuffin <metamuffin.org> +*/ + +use crate::LOCAL_VIDEO_TRANSCODING_TASKS; +use jellybase::cache::{async_cache_file, CachePath}; +use jellycommon::jhls::EncodingProfile; +use log::{debug, info}; +use std::process::Stdio; +use tokio::{ + io::copy, + process::{ChildStdin, Command}, +}; + +// TODO odd video resolutions can cause errors when transcoding to YUV42{0,2} +// TODO with an implementation that cant handle it (SVT-AV1 such an impl). + +pub async fn transcode( + key: &str, + enc: &EncodingProfile, + input: impl FnOnce(ChildStdin), +) -> anyhow::Result<CachePath> { + Ok(async_cache_file( + &["snip-tc", key, &format!("{enc:?}")], + move |mut output| async move { + let _permit = LOCAL_VIDEO_TRANSCODING_TASKS.acquire().await?; + debug!("transcoding snippet with {enc:?}"); + + let mut args = Vec::new(); + match enc { + EncodingProfile::Video { + codec, + preset, + bitrate, + width, + } => { + if let Some(width) = width { + args.push("-vf".to_string()); + args.push(format!("scale={width}:-1")); + } + args.push("-c:v".to_string()); + args.push(codec.to_string()); + if let Some(preset) = preset { + args.push("-preset".to_string()); + args.push(format!("{preset}")); + } + args.push("-b:v".to_string()); + args.push(format!("{bitrate}")); + } + EncodingProfile::Audio { + codec, + bitrate, + sample_rate, + channels, + } => { + if let Some(channels) = channels { + args.push("-ac".to_string()); + args.push(format!("{channels}")) + } + if let Some(sample_rate) = sample_rate { + args.push("-ar".to_string()); + args.push(format!("{sample_rate}")) + } + args.push("-c:a".to_string()); + args.push(codec.to_string()); + args.push("-b:a".to_string()); + args.push(format!("{bitrate}")); + } + EncodingProfile::Subtitles { codec } => { + args.push("-c:s".to_string()); + args.push(codec.to_string()); + } + }; + info!("encoding with {:?}", args.join(" ")); + + let mut proc = Command::new("ffmpeg") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .args(&["-f", "matroska", "-i", "pipe:0"]) + .args(args) + .args(&["-f", "webm", "pipe:1"]) + .spawn()?; + // let mut proc = Command::new("cat") + // .stdin(Stdio::piped()) + // .stdout(Stdio::piped()) + // .spawn()?; + + let stdin = proc.stdin.take().unwrap(); + let mut stdout = proc.stdout.take().unwrap(); + + input(stdin); + copy(&mut stdout, &mut output).await?; + + proc.wait().await.unwrap().exit_ok()?; + info!("done"); + Ok(()) + }, + ) + .await?) +} |