aboutsummaryrefslogtreecommitdiff
path: root/transcoder/src/fragment.rs
diff options
context:
space:
mode:
Diffstat (limited to 'transcoder/src/fragment.rs')
-rw-r--r--transcoder/src/fragment.rs102
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?)
+}