Add transcode cmd paramters

This commit is contained in:
Zoe Roux 2023-04-09 01:14:55 +09:00
parent 5543bc4c9d
commit 33d212bd84
No known key found for this signature in database
4 changed files with 154 additions and 9 deletions

34
transcoder/Cargo.lock generated
View File

@ -88,7 +88,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6"
dependencies = [
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@ -203,7 +203,7 @@ dependencies = [
"actix-router",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@ -398,7 +398,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version",
"syn",
"syn 1.0.109",
]
[[package]]
@ -867,6 +867,20 @@ name = "serde"
version = "1.0.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.13",
]
[[package]]
name = "serde_json"
@ -947,6 +961,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "time"
version = "0.3.20"
@ -1047,6 +1072,9 @@ version = "0.1.0"
dependencies = [
"actix-files",
"actix-web",
"rand",
"serde",
"tokio",
]
[[package]]

View File

@ -6,3 +6,6 @@ edition = "2021"
[dependencies]
actix-web = "4"
actix-files = "0.6.2"
tokio = { version = "1.27.0", features = ["process"] }
serde = { version = "1.0.159", features = ["derive"] }
rand = "0.8.5"

View File

@ -1,20 +1,40 @@
use actix_files::NamedFile;
use actix_web::{get, web, App, HttpServer, Result};
use crate::transcode::{Quality, TranscoderState};
mod paths;
mod transcode;
#[get("/movie/direct/{slug}")]
async fn index(query: web::Path<String>) -> Result<NamedFile> {
async fn get_movie_direct(query: web::Path<String>) -> Result<NamedFile> {
let slug = query.into_inner();
let path = paths::get_movie_path(slug);
Ok(NamedFile::open_async(path).await?)
// .map(|f| (infer_content_type(f), f))
}
#[get("/movie/{quality}/{slug}")]
async fn get_movie_auto(
query: web::Path<(String, String)>,
state: web::Data<TranscoderState>,
) -> Result<NamedFile> {
let (quality, slug) = query.into_inner();
let path = paths::get_movie_path(slug);
Ok(NamedFile::open_async(path).await?)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(index))
.bind(("0.0.0.0", 7666))?
.run()
.await
let state = web::Data::new(TranscoderState::new());
HttpServer::new(move || {
App::new()
.app_data(state.clone())
.service(get_movie_direct)
.service(get_movie_auto)
})
.bind(("0.0.0.0", 7666))?
.run()
.await
}

View File

@ -0,0 +1,94 @@
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use serde::{Deserialize, Serialize};
use std::process::{Command, Child};
use std::{collections::HashMap, sync::Mutex};
pub struct TranscoderState {
running: Mutex<HashMap<u32, Child>>,
}
impl TranscoderState {
pub fn new() -> TranscoderState {
Self {
running: Mutex::new(HashMap::new()),
}
}
}
pub enum Quality {
P240,
P360,
P480,
P720,
P1080,
P1440,
P4k,
P8k,
Original,
}
imhttps://sgsot3a.sic.shibaura-it.ac.jp/
fn get_transcode_video_quality_args(quality: Quality) -> Vec<&'static str> {
// superfast or ultrafast would produce a file extremly big so we prever veryfast.
let enc_base: Vec<&str> = vec![
"-map", "0:v:0", "-c:v", "libx264", "-crf", "21", "-preset", "veryfast",
];
match quality {
Quality::Original => vec![],
Quality::P240 => [enc_base, vec!["-vf", "scale=-1:240"]].concat(),
Quality::P360 => [enc_base, vec!["-vf", "scale=-1:360"]].concat(),
Quality::P480 => [enc_base, vec!["-vf", "scale=-1:480"]].concat(),
Quality::P720 => [enc_base, vec!["-vf", "scale=-1:720"]].concat(),
Quality::P1080 => [enc_base, vec!["-vf", "scale=-1:1080"]].concat(),
Quality::P1440 => [enc_base, vec!["-vf", "scale=-1:1440"]].concat(),
Quality::P4k => [enc_base, vec!["-vf", "scale=-1:2160"]].concat(),
Quality::P8k => [enc_base, vec!["-vf", "scale=-1:4320"]].concat(),
}
}
// TODO: Add audios streams (and transcode them only when necesarry)
async fn start_transcode(path: &str, quality: Quality, start_time_sec: f32) -> (String, Child) {
// TODO: Use the out path below once cached segments can be reused.
// let out_dir = format!("/cache/{show_hash}/{quality}");
let uuid: String = thread_rng()
.sample_iter(&Alphanumeric)
.take(30)
.map(char::from)
.collect();
let out_dir = format!("/cache/{uuid}");
let segment_time = "10";
let child = Command::new("ffmpeg")
.args(&["-ss", start_time_sec.to_string().as_str()])
.args(&["-i", path])
.args(&["-f", "segment"])
.args(&["-segment_list_type", "m3u8"])
// Keep all segments in the list (else only last X are presents, useful for livestreams)
.args(&["--segment_list_size", "0"])
.args(&["-segment_time", segment_time])
// Force segments to be exactly segment_time (only works when transcoding)
.args(&[
"-force_key_frames",
format!("expr:gte(t,n_forced*{segment_time})").as_str(),
"-strict",
"-2",
"-segment_time_delta",
"0.1",
])
.args(get_transcode_video_quality_args(quality))
.args(&[
"-segment_list".to_string(),
format!("{out_dir}/stream.m3u8"),
format!("{out_dir}/segments-%02d.ts"),
])
.spawn()
.expect("ffmpeg failed to start");
(uuid, child)
}
pub async fn transcode(user_id: u32, path: &str, quality: Quality, start_time_sec: f32) {
}