diff options
author | metamuffin <metamuffin@disroot.org> | 2023-10-02 19:46:08 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2023-10-02 19:46:08 +0200 |
commit | 839c1e1490e7cd856e6ada1dcfd82f3d4505c89c (patch) | |
tree | 2e3e929a8154f1239641da564ee05abf84bce19c | |
parent | 15d1f6516e31d20ab69569b7d6b6589f4d8f4f7b (diff) | |
download | jellything-839c1e1490e7cd856e6ada1dcfd82f3d4505c89c.tar jellything-839c1e1490e7cd856e6ada1dcfd82f3d4505c89c.tar.bz2 jellything-839c1e1490e7cd856e6ada1dcfd82f3d4505c89c.tar.zst |
transcoding profiles
-rw-r--r-- | common/src/config.rs | 66 | ||||
-rw-r--r-- | common/src/lib.rs | 19 | ||||
-rw-r--r-- | common/src/stream.rs | 13 | ||||
-rw-r--r-- | stream/src/segment.rs | 22 | ||||
-rw-r--r-- | transcoder/src/snippet.rs | 33 |
5 files changed, 103 insertions, 50 deletions
diff --git a/common/src/config.rs b/common/src/config.rs index aded4ff..e30e70b 100644 --- a/common/src/config.rs +++ b/common/src/config.rs @@ -4,7 +4,7 @@ Copyright (C) 2023 metamuffin <metamuffin.org> */ -use crate::user::PermissionSet; +use crate::{user::PermissionSet, EncodingProfile}; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, path::PathBuf}; @@ -19,6 +19,7 @@ pub struct GlobalConfig { #[serde(default = "default::temp_path")] pub temp_path: PathBuf, #[serde(default = "default::cache_path")] pub cache_path: PathBuf, #[serde(default = "default::admin_username")] pub admin_username: String, + #[serde(default = "default::transcoding_profiles")] pub transcoding_profiles: Vec<EncodingProfile>, #[serde(default = "default::max_in_memory_cache_size")] pub max_in_memory_cache_size: usize, pub admin_password: String, #[serde(default)] pub cookie_key: Option<String>, @@ -28,16 +29,61 @@ pub struct GlobalConfig { #[serde(default)] pub default_permission_set: PermissionSet, } -#[rustfmt::skip] mod default { + use crate::EncodingProfile; use std::path::PathBuf; - pub fn admin_username() -> String { "admin".into() } - pub fn login_expire() -> i64 { 60*60*24 } - pub fn asset_path() -> PathBuf { "data/assets".into() } - pub fn database_path() -> PathBuf { "data/database".into() } - pub fn library_path() -> PathBuf { "data/library".into() } - pub fn cache_path() -> PathBuf { "data/cache".into() } - pub fn temp_path() -> PathBuf { "/tmp".into() } - pub fn max_in_memory_cache_size() -> usize { 50_000_000 } + pub fn admin_username() -> String { + "admin".into() + } + pub fn login_expire() -> i64 { + 60 * 60 * 24 + } + pub fn asset_path() -> PathBuf { + "data/assets".into() + } + pub fn database_path() -> PathBuf { + "data/database".into() + } + pub fn library_path() -> PathBuf { + "data/library".into() + } + pub fn cache_path() -> PathBuf { + "data/cache".into() + } + pub fn temp_path() -> PathBuf { + "/tmp".into() + } + pub fn max_in_memory_cache_size() -> usize { + 50_000_000 + } + pub fn transcoding_profiles() -> Vec<EncodingProfile> { + vec![ + EncodingProfile::Video { + codec: "libsvtav1".to_string(), + preset: 8, + bitrate: 2_000_000, + width: 1920, + }, + EncodingProfile::Video { + codec: "libsvtav1".to_string(), + preset: 8, + bitrate: 1_500_000, + width: 1280, + }, + EncodingProfile::Audio { + codec: "libopus".to_string(), + bitrate: 128_000, + sample_rate: None, + }, + EncodingProfile::Audio { + codec: "libopus".to_string(), + bitrate: 64_000, + sample_rate: None, + }, + EncodingProfile::Subtitles { + codec: "webvtt".to_string(), + }, + ] + } } diff --git a/common/src/lib.rs b/common/src/lib.rs index c425c21..6d1a78c 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -142,3 +142,22 @@ pub enum SourceTrackKind { }, Subtitles, } + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum EncodingProfile { + Video { + codec: String, + preset: u8, + bitrate: usize, + width: usize, + }, + Audio { + codec: String, + bitrate: usize, + sample_rate: Option<f64>, + }, + Subtitles { + codec: String, + }, +} diff --git a/common/src/stream.rs b/common/src/stream.rs index 1fb3761..8a7ff36 100644 --- a/common/src/stream.rs +++ b/common/src/stream.rs @@ -13,8 +13,7 @@ pub struct StreamSpec { pub tracks: Vec<usize>, pub format: StreamFormat, pub webm: Option<bool>, - pub bitrate: Option<usize>, - pub width: Option<usize>, + pub profile: Option<usize>, pub index: Option<usize>, } @@ -37,8 +36,7 @@ impl Default for StreamSpec { tracks: Vec::new(), format: StreamFormat::Matroska, webm: Some(true), - width: None, - bitrate: None, + profile: None, index: None, } } @@ -62,8 +60,8 @@ impl StreamSpec { ) .unwrap(); } - if let Some(bitrate) = self.bitrate { - write!(u, "&bitrate={bitrate}").unwrap(); + if let Some(profile) = self.profile { + write!(u, "&profile={profile}").unwrap(); } if let Some(index) = self.index { write!(u, "&index={index}").unwrap(); @@ -71,9 +69,6 @@ impl StreamSpec { if let Some(webm) = self.webm { write!(u, "&webm={webm}").unwrap(); } - if let Some(width) = self.width { - write!(u, "&width={width}").unwrap(); - } u } } diff --git a/stream/src/segment.rs b/stream/src/segment.rs index fdef01c..51674d7 100644 --- a/stream/src/segment.rs +++ b/stream/src/segment.rs @@ -6,7 +6,7 @@ use anyhow::{anyhow, bail, Result}; use jellybase::{AssetLocationExt, CONF}; use jellycommon::{stream::StreamSpec, LocalTrack, Node}; -use jellytranscoder::snippet::{transcode, Encoding}; +use jellytranscoder::snippet::transcode; use log::warn; use tokio::{fs::File, io::DuplexStream}; use tokio_util::io::SyncIoBridge; @@ -23,16 +23,12 @@ pub async fn segment_stream( let track = spec.tracks[0]; let n = spec.index.ok_or(anyhow!("segment index missing"))?; - if let Some(width) = spec.width { - let width = width_steps(width); + if let Some(profile) = spec.profile { let location = transcode( "", - Encoding::Video { - codec: "libsvtav1", - preset: 8, - bitrate: spec.bitrate.unwrap_or(2_000_000), - width, - }, + CONF.transcoding_profiles + .get(profile) + .ok_or(anyhow!("profile out of range"))?, move |b| { tokio::task::spawn_blocking(move || { if let Err(err) = jellyremuxer::write_snippet_into( @@ -75,11 +71,3 @@ pub async fn segment_stream( Ok(()) } - -fn width_steps(width: usize) -> usize { - match width { - x if x >= 3840 => 3840, - x if x >= 1920 => 1920, - _ => 720, - } -} diff --git a/transcoder/src/snippet.rs b/transcoder/src/snippet.rs index 3cc9736..9253c2c 100644 --- a/transcoder/src/snippet.rs +++ b/transcoder/src/snippet.rs @@ -5,7 +5,7 @@ */ use jellybase::cache::async_cache_file; -use jellycommon::AssetLocation; +use jellycommon::{AssetLocation, EncodingProfile}; use log::info; use std::process::Stdio; use tokio::{ @@ -13,19 +13,9 @@ use tokio::{ process::{ChildStdin, Command}, }; -#[derive(Debug)] -pub enum Encoding { - Video { - codec: &'static str, - preset: u8, - bitrate: usize, - width: usize, - }, -} - pub async fn transcode( key: &str, - enc: Encoding, + enc: &EncodingProfile, input: impl FnOnce(ChildStdin), ) -> anyhow::Result<AssetLocation> { Ok(async_cache_file( @@ -33,7 +23,7 @@ pub async fn transcode( move |mut output| async move { info!("transcoding snippet {key}"); let args = match enc { - Encoding::Video { + EncodingProfile::Video { codec, preset, bitrate, @@ -49,6 +39,21 @@ pub async fn transcode( format!("{bitrate}"), ] .to_vec(), + EncodingProfile::Audio { + codec, + bitrate, + sample_rate: _, + } => [ + // TODO resample? + "-c:a".to_string(), + codec.to_string(), + "-b:a".to_string(), + format!("{bitrate}"), + ] + .to_vec(), + EncodingProfile::Subtitles { codec } => { + ["-c:s".to_string(), codec.to_string()].to_vec() + } }; info!("encoding with {:?}", args.join(" ")); @@ -65,7 +70,7 @@ pub async fn transcode( input(stdin); copy(&mut stdout, &mut output).await?; - + proc.wait().await.unwrap().exit_ok()?; info!("done"); Ok(()) |