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.rs140
1 files changed, 87 insertions, 53 deletions
diff --git a/transcoder/src/fragment.rs b/transcoder/src/fragment.rs
index 7c5cadd..027e80f 100644
--- a/transcoder/src/fragment.rs
+++ b/transcoder/src/fragment.rs
@@ -4,12 +4,18 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use crate::LOCAL_VIDEO_TRANSCODING_TASKS;
+use anyhow::Result;
use jellybase::{
cache::{async_cache_file, CachePath},
- common::stream::{StreamFormatInfo, TrackKind},
+ common::{
+ config::TranscoderConfig,
+ stream::{StreamFormatInfo, TrackKind},
+ },
CONF,
};
-use log::{debug, info};
+use jellyremuxer::metadata::MatroskaTrackEntry;
+use log::info;
+use std::fmt::Write;
use std::process::Stdio;
use tokio::{
io::copy,
@@ -21,62 +27,16 @@ use tokio::{
pub async fn transcode(
kind: TrackKind,
+ orig_metadata: &MatroskaTrackEntry,
format: &StreamFormatInfo,
input_key: &str,
input: impl FnOnce(ChildStdin),
) -> anyhow::Result<CachePath> {
- let template = match format.codec.as_str() {
- "V_MPEG4/ISO/AVC" => CONF.encoders.avc.as_ref(),
- "V_MPEGH/ISO/HEVC" => CONF.encoders.hevc.as_ref(),
- "V_VP8" => CONF.encoders.vp8.as_ref(),
- "V_VP9" => CONF.encoders.vp9.as_ref(),
- "V_AV1" => CONF.encoders.av1.as_ref(),
- _ => None,
- }
- .or(CONF.encoders.generic.as_ref())
- .cloned()
- .unwrap_or("ffmpeg %a".to_owned());
-
- let filter = match kind {
- TrackKind::Video => "-vf scale=%w:%h",
- TrackKind::Audio => "",
- TrackKind::Subtitle => "",
- };
- let typechar = match kind {
- TrackKind::Video => "v",
- TrackKind::Audio => "a",
- TrackKind::Subtitle => "s",
- };
- let fallback_encoder = match format.codec.as_str() {
- "A_OPUS" => "libopus",
- "V_VP8" => "libvpx",
- "V_VP9" => "libvpx-vp9",
- "V_AV1" => "libaom", // svtav1 is x86 only :(
- "V_MPEG4/ISO/AVC" => "libx264",
- "V_MPEGH/ISO/HEVC" => "libx265",
- _ => "",
- };
-
- let args = template
- .replace("%a", "-hide_banner %i %f %e %o")
- .replace("%i", "-f matroska -i pipe:0 -copyts")
- .replace("%o", "-f matroska pipe:1")
- .replace("%f", &filter)
- .replace("%w", &format.width.unwrap_or_default().to_string())
- .replace("%h", &format.height.unwrap_or_default().to_string())
- .replace("%e", "-c:%t %c -b:%t %r")
- .replace("%t", typechar)
- .replace("%c", fallback_encoder)
- .replace("%r", &(format.bitrate as i64).to_string())
- .replace(" ", " ");
-
- async_cache_file("frag-tc", (input_key, &args), async |mut output| {
+ let command = transcode_command(kind, orig_metadata, format, &CONF.transcoder).unwrap();
+ async_cache_file("frag-tc", (input_key, &command), async |mut output| {
let _permit = LOCAL_VIDEO_TRANSCODING_TASKS.acquire().await?;
- debug!("transcoding fragment with {format:?}");
-
- info!("encoding with {:?}", args);
-
- let mut args = args.split(" ");
+ info!("encoding with {command:?}");
+ let mut args = command.split(" ");
let mut proc = Command::new(args.next().unwrap())
.stdin(Stdio::piped())
.stdout(Stdio::piped())
@@ -95,3 +55,77 @@ pub async fn transcode(
})
.await
}
+
+fn transcode_command(
+ kind: TrackKind,
+ orig_metadata: &MatroskaTrackEntry,
+ format: &StreamFormatInfo,
+ config: &TranscoderConfig,
+) -> Result<String> {
+ let br = format.bitrate as u64;
+ let w = format.width.unwrap_or(0);
+ let h = format.height.unwrap_or(0);
+ let mut o = String::new();
+ write!(o, "ffmpeg -hide_banner ")?;
+
+ if kind == TrackKind::Video {
+ if config.enable_rkmpp {
+ write!(o, "-hwaccel rkmpp -hwaccel_output_format drm_prime ")?;
+ }
+ if config.enable_rkrga {
+ write!(o, "-afbc rga ")?;
+ }
+
+ write!(o, "-f matroska -i pipe:0 -copyts ")?;
+
+ if config.enable_rkrga {
+ write!(o, "-vf scale_rkrga=w={w}:h={h}:format=nv12:afbc=1 ")?;
+ } else {
+ write!(o, "-vf scale={w}:{h} ")?;
+ }
+
+ match format.codec.as_str() {
+ "V_MPEG4/ISO/AVC" if config.enable_rkmpp => {
+ write!(o, "-c:v h264_rkmpp -profile:v high -b:v {br} ")?
+ }
+ "V_MPEGH/ISO/HEVC" if config.enable_rkmpp => {
+ write!(o, "-c:v h265_rkmpp -profile:v high -b:v {br} ")?
+ }
+ "A_AV1" if config.use_svtav1 => {
+ let p = config.svtav1_preset.unwrap_or(8);
+ write!(o, "-c:v libsvtav1 -preset {p} -b:v {br} ")?
+ }
+ "A_AV1" if config.use_rav1e => {
+ let p = config.rav1e_preset.unwrap_or(8);
+ write!(o, "-c:v librav1e -speed {p} -b:v {br} ")?
+ }
+ "V_MPEG4/ISO/AVC" => {
+ let p = config.x264_preset.clone().unwrap_or("fast".to_string());
+ write!(o, "-c:v libx264 -preset {p} -b:v {br} ")?
+ }
+ "V_MPEGH/ISO/HEVC" => write!(o, "-c:v libx265 -b:v {br} ")?,
+ "V_VP8" => write!(o, "-c:v libvpx -b:v {br} ")?,
+ "V_VP9" => write!(o, "-c:v libvpx-vp9 -b:v {br} ")?,
+ "V_AV1" => {
+ let p = config.aom_preset.unwrap_or(1);
+ write!(o, "-c:v libaom -cpu-used {p} -row-mt 1 -b:v {br} ")?
+ }
+ _ => todo!(),
+ };
+ } else if kind == TrackKind::Audio {
+ write!(o, "-f matroska -i pipe:0 -copyts ")?;
+ if format.codec == "A_OPUS" && orig_metadata.audio.as_ref().unwrap().channels > 2 {
+ write!(o, "-ac 2 ")?;
+ }
+ match format.codec.as_str() {
+ "A_OPUS" => write!(o, "-c:a libopus -b:a {br} ")?,
+ _ => todo!(),
+ }
+ } else {
+ write!(o, "-f matroska -i pipe:0 -copyts ")?;
+ todo!()
+ }
+
+ write!(o, "-f matroska pipe:1")?;
+ Ok(o)
+}