mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 04:04:21 -04:00
Add audio transcode functions
This commit is contained in:
parent
0b2d8a7e2e
commit
a5fc5b3753
@ -3,6 +3,8 @@ use rand::distributions::Alphanumeric;
|
|||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::{Hasher, Hash};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::slice::Iter;
|
use std::slice::Iter;
|
||||||
@ -10,10 +12,12 @@ use std::str::FromStr;
|
|||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||||
use tokio::process::{Child, Command};
|
use tokio::process::{Child, Command};
|
||||||
use tokio::sync::watch::{self, Receiver};
|
use tokio::sync::watch;
|
||||||
|
|
||||||
use crate::utils::Signalable;
|
use crate::utils::Signalable;
|
||||||
|
|
||||||
|
const SEGMENT_TIME: u32 = 10;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Serialize, Display)]
|
#[derive(PartialEq, Eq, Serialize, Display)]
|
||||||
pub enum Quality {
|
pub enum Quality {
|
||||||
#[display(fmt = "240p")]
|
#[display(fmt = "240p")]
|
||||||
@ -119,6 +123,18 @@ impl FromStr for Quality {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_transcode_audio_args(audio_idx: u32) -> Vec<String> {
|
||||||
|
// TODO: Support multy audio qualities.
|
||||||
|
return vec![
|
||||||
|
"-map".to_string(),
|
||||||
|
format!("0:a:{}", audio_idx),
|
||||||
|
"-c:a".to_string(),
|
||||||
|
"aac".to_string(),
|
||||||
|
"-b:a".to_string(),
|
||||||
|
"128k".to_string(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
fn get_transcode_video_quality_args(quality: &Quality, segment_time: u32) -> Vec<String> {
|
fn get_transcode_video_quality_args(quality: &Quality, segment_time: u32) -> Vec<String> {
|
||||||
if *quality == Quality::Original {
|
if *quality == Quality::Original {
|
||||||
return vec!["-map", "0:v:0", "-c:v", "copy"]
|
return vec!["-map", "0:v:0", "-c:v", "copy"]
|
||||||
@ -156,8 +172,23 @@ fn get_transcode_video_quality_args(quality: &Quality, segment_time: u32) -> Vec
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add audios streams (and transcode them only when necesarry)
|
async fn transcode_audio(path: String, audio: u32) -> TranscodeInfo {
|
||||||
async fn start_transcode(path: String, quality: Quality, start_time: u32) -> TranscodeInfo {
|
let mut hasher = DefaultHasher::new();
|
||||||
|
path.hash(&mut hasher);
|
||||||
|
audio.hash(&mut hasher);
|
||||||
|
let hash = hasher.finish();
|
||||||
|
|
||||||
|
let child = start_transcode(
|
||||||
|
&path,
|
||||||
|
&format!("/cache/{hash}"),
|
||||||
|
get_transcode_audio_args(audio),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn transcode_video(path: String, quality: Quality, start_time: u32) -> TranscodeInfo {
|
||||||
// TODO: Use the out path below once cached segments can be reused.
|
// TODO: Use the out path below once cached segments can be reused.
|
||||||
// let out_dir = format!("/cache/{show_hash}/{quality}");
|
// let out_dir = format!("/cache/{show_hash}/{quality}");
|
||||||
let uuid: String = thread_rng()
|
let uuid: String = thread_rng()
|
||||||
@ -168,7 +199,26 @@ async fn start_transcode(path: String, quality: Quality, start_time: u32) -> Tra
|
|||||||
let out_dir = format!("/cache/{uuid}");
|
let out_dir = format!("/cache/{uuid}");
|
||||||
std::fs::create_dir(&out_dir).expect("Could not create cache directory");
|
std::fs::create_dir(&out_dir).expect("Could not create cache directory");
|
||||||
|
|
||||||
let segment_time: u32 = 10;
|
let child = start_transcode(
|
||||||
|
&path,
|
||||||
|
&out_dir,
|
||||||
|
get_transcode_video_quality_args(&quality, SEGMENT_TIME),
|
||||||
|
start_time,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
TranscodeInfo {
|
||||||
|
show: (path, quality),
|
||||||
|
job: child,
|
||||||
|
uuid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn start_transcode(
|
||||||
|
path: &String,
|
||||||
|
out_dir: &String,
|
||||||
|
encode_args: Vec<String>,
|
||||||
|
start_time: u32,
|
||||||
|
) -> Child {
|
||||||
let mut cmd = Command::new("ffmpeg");
|
let mut cmd = Command::new("ffmpeg");
|
||||||
cmd.args(&["-progress", "pipe:1"])
|
cmd.args(&["-progress", "pipe:1"])
|
||||||
.arg("-nostats")
|
.arg("-nostats")
|
||||||
@ -181,8 +231,8 @@ async fn start_transcode(path: String, quality: Quality, start_time: u32) -> Tra
|
|||||||
// .args(&["-hls_allow_cache", "1"])
|
// .args(&["-hls_allow_cache", "1"])
|
||||||
// Keep all segments in the list (else only last X are presents, useful for livestreams)
|
// Keep all segments in the list (else only last X are presents, useful for livestreams)
|
||||||
.args(&["-hls_list_size", "0"])
|
.args(&["-hls_list_size", "0"])
|
||||||
.args(&["-hls_time", segment_time.to_string().as_str()])
|
.args(&["-hls_time", SEGMENT_TIME.to_string().as_str()])
|
||||||
.args(get_transcode_video_quality_args(&quality, segment_time))
|
.args(&encode_args)
|
||||||
.args(&[
|
.args(&[
|
||||||
"-hls_segment_filename".to_string(),
|
"-hls_segment_filename".to_string(),
|
||||||
format!("{out_dir}/segments-%02d.ts"),
|
format!("{out_dir}/segments-%02d.ts"),
|
||||||
@ -213,18 +263,10 @@ async fn start_transcode(path: String, quality: Quality, start_time: u32) -> Tra
|
|||||||
loop {
|
loop {
|
||||||
rx.changed().await.unwrap();
|
rx.changed().await.unwrap();
|
||||||
let ready_time = *rx.borrow();
|
let ready_time = *rx.borrow();
|
||||||
if ready_time >= (1.5 * segment_time as f32) as u32 + start_time {
|
if ready_time >= (1.5 * SEGMENT_TIME as f32) as u32 + start_time {
|
||||||
break;
|
return child;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TranscodeInfo {
|
|
||||||
show: (path, quality),
|
|
||||||
job: child,
|
|
||||||
uuid,
|
|
||||||
start_time,
|
|
||||||
ready_time: rx,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cache_path(info: &TranscodeInfo) -> PathBuf {
|
fn get_cache_path(info: &TranscodeInfo) -> PathBuf {
|
||||||
@ -235,15 +277,10 @@ fn get_cache_path_from_uuid(uuid: &String) -> PathBuf {
|
|||||||
return PathBuf::from(format!("/cache/{uuid}/", uuid = &uuid));
|
return PathBuf::from(format!("/cache/{uuid}/", uuid = &uuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TranscodeInfo {
|
pub struct TranscodeInfo {
|
||||||
show: (String, Quality),
|
show: (String, Quality),
|
||||||
// TODO: Store if the process as ended (probably Option<Child> for the job)
|
|
||||||
job: Child,
|
job: Child,
|
||||||
uuid: String,
|
uuid: String,
|
||||||
#[allow(dead_code)]
|
|
||||||
start_time: u32,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
ready_time: Receiver<u32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Transcoder {
|
pub struct Transcoder {
|
||||||
@ -311,7 +348,7 @@ impl Transcoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let info = start_transcode(path, quality, start_time).await;
|
let info = transcode_video(path, quality, start_time).await;
|
||||||
let mut path = get_cache_path(&info);
|
let mut path = get_cache_path(&info);
|
||||||
path.push("stream.m3u8");
|
path.push("stream.m3u8");
|
||||||
self.running.write().unwrap().insert(client_id, info);
|
self.running.write().unwrap().insert(client_id, info);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user