integrate webvtt subtitles

This commit is contained in:
jb-alvarado 2024-09-25 17:18:50 +02:00
parent 1d4cdfaca6
commit 5061f21376
7 changed files with 133 additions and 12 deletions

View File

@ -51,11 +51,13 @@
"canonicalize",
"ffpengine",
"flexi",
"httpauth",
"lettre",
"libc",
"neli",
"nuxt",
"paris",
"Referer",
"reqwest",
"rsplit",
"rustls",

View File

@ -72,6 +72,10 @@ pub fn ingest_server(
dummy_media.add_filter(&config, &None);
let is_terminated = channel_mgr.is_terminated.clone();
let ingest_is_running = channel_mgr.ingest_is_running.clone();
let vtt_dummy = config
.channel
.storage_path
.join(&config.processing.vtt_dummy.clone().unwrap_or_default());
if let Some(ingest_input_cmd) = config.advanced.ingest.input_cmd {
server_cmd.append(&mut ingest_input_cmd.clone());
@ -79,11 +83,19 @@ pub fn ingest_server(
server_cmd.append(&mut stream_input.clone());
if config.processing.vtt_enable && vtt_dummy.is_file() {
server_cmd.append(&mut vec_strings!["-i", vtt_dummy.to_string_lossy()]);
}
if let Some(mut filter) = dummy_media.filter {
server_cmd.append(&mut filter.cmd());
server_cmd.append(&mut filter.map());
}
if config.processing.vtt_enable && vtt_dummy.is_file() {
server_cmd.append(&mut vec_strings!("-map", "1:s"));
}
if let Some(mut cmd) = config.processing.cmd {
server_cmd.append(&mut cmd);
}

View File

@ -640,14 +640,14 @@ pub fn gen_source(
.filter(|c| IMAGE_FORMAT.contains(&c.as_str()))
.is_some()
{
node.cmd = Some(loop_image(&node));
node.cmd = Some(loop_image(&config, &node));
} else {
if node.seek > 0.0 && node.out > node.duration {
warn!(target: Target::file_mail(), channel = config.general.channel_id; "Clip loops and has seek value: duplicate clip to separate loop and seek.");
duplicate_for_seek_and_loop(&mut node, &manager.current_list);
}
node.cmd = Some(seek_and_length(&mut node));
node.cmd = Some(seek_and_length(&config, &mut node));
}
} else {
trace!("clip index: {node_index} | last index: {last_index}");
@ -694,7 +694,7 @@ pub fn gen_source(
node.seek = 0.0;
node.out = filler_media.out;
node.duration = filler_media.duration;
node.cmd = Some(loop_filler(&node));
node.cmd = Some(loop_filler(&config, &node));
node.probe = filler_media.probe;
} else {
match MediaProbe::new(&config.storage.filler_path.to_string_lossy()) {
@ -715,7 +715,7 @@ pub fn gen_source(
.clone()
.to_string_lossy()
.to_string();
node.cmd = Some(loop_image(&node));
node.cmd = Some(loop_image(&config, &node));
node.probe = Some(probe);
} else if let Some(filler_duration) = probe
.clone()
@ -739,7 +739,7 @@ pub fn gen_source(
node.seek = 0.0;
node.out = filler_out;
node.duration = filler_duration;
node.cmd = Some(loop_filler(&node));
node.cmd = Some(loop_filler(&config, &node));
node.probe = Some(probe);
} else {
// Create colored placeholder.

View File

@ -62,6 +62,17 @@ fn ingest_to_hls_server(manager: ChannelManager) -> Result<(), ProcessError> {
server_prefix.append(&mut stream_input.clone());
if config.processing.vtt_enable {
let vtt_dummy = config
.channel
.storage_path
.join(&config.processing.vtt_dummy.clone().unwrap_or_default());
if vtt_dummy.is_file() {
server_prefix.append(&mut vec_strings!["-i", vtt_dummy.to_string_lossy()]);
}
}
let mut is_running;
if let Some(url) = stream_input.iter().find(|s| s.contains("://")) {

View File

@ -150,6 +150,14 @@ pub fn player(manager: ChannelManager) -> Result<(), ProcessError> {
dec_cmd.append(&mut filter.map());
}
if config.processing.vtt_enable && dec_cmd.iter().any(|s| s.ends_with(".vtt")) {
let mut i = dec_cmd.iter().filter(|&n| &*n == "-i").count();
if i > 0 {
i -= 1
}
dec_cmd.append(&mut vec_strings!("-map", format!("{i}:s"), "-c:s", "copy"));
}
if let Some(mut cmd) = config.processing.cmd.clone() {
dec_cmd.append(&mut cmd);
}

View File

@ -65,9 +65,9 @@ fn check_media(
.filter(|c| IMAGE_FORMAT.contains(&c.as_str()))
.is_some()
{
node.cmd = Some(loop_image(&node));
node.cmd = Some(loop_image(&config, &node));
} else {
node.cmd = Some(seek_and_length(&mut node));
node.cmd = Some(seek_and_length(&config, &mut node));
}
node.add_filter(&config, &None);

View File

@ -67,6 +67,10 @@ pub fn prepare_output_cmd(
let mut new_params = vec![];
let mut count = 0;
let re_v = Regex::new(r"\[?0:v(:0)?\]?").unwrap();
let vtt_dummy = config
.channel
.storage_path
.join(&config.processing.vtt_dummy.clone().unwrap_or_default());
if let Some(mut filter) = filters.clone() {
for (i, param) in output_params.iter().enumerate() {
@ -119,6 +123,15 @@ pub fn prepare_output_cmd(
}
}
if config.processing.vtt_enable && vtt_dummy.is_file() {
let mut i = cmd.iter().filter(|&n| &*n == "-i").count();
if i > 0 {
i -= 1
}
cmd.append(&mut vec_strings!("-map", format!("{i}:s?")));
}
cmd.append(&mut output_params);
cmd
@ -589,7 +602,7 @@ pub fn get_delta(config: &PlayoutConfig, begin: &f64) -> (f64, f64) {
}
/// Loop image until target duration is reached.
pub fn loop_image(node: &Media) -> Vec<String> {
pub fn loop_image(config: &PlayoutConfig, node: &Media) -> Vec<String> {
let duration = node.out - node.seek;
let mut source_cmd: Vec<String> = vec_strings!["-loop", "1", "-i", node.source.clone()];
@ -608,11 +621,30 @@ pub fn loop_image(node: &Media) -> Vec<String> {
source_cmd.append(&mut vec_strings!["-t", duration]);
if config.processing.vtt_enable {
let vtt_file = Path::new(&node.source).with_extension("vtt");
let vtt_dummy = config
.channel
.storage_path
.join(&config.processing.vtt_dummy.clone().unwrap_or_default());
if vtt_file.is_file() {
source_cmd.append(&mut vec_strings![
"-i",
vtt_file.to_string_lossy(),
"-t",
node.out
]);
} else if vtt_dummy.is_file() {
source_cmd.append(&mut vec_strings!["-i", vtt_dummy.to_string_lossy()]);
}
}
source_cmd
}
/// Loop filler until target duration is reached.
pub fn loop_filler(node: &Media) -> Vec<String> {
pub fn loop_filler(config: &PlayoutConfig, node: &Media) -> Vec<String> {
let loop_count = (node.out / node.duration).ceil() as i32;
let mut source_cmd = vec![];
@ -624,11 +656,34 @@ pub fn loop_filler(node: &Media) -> Vec<String> {
source_cmd.append(&mut vec_strings!["-i", node.source, "-t", node.out]);
if config.processing.vtt_enable {
let vtt_file = Path::new(&node.source).with_extension("vtt");
let vtt_dummy = config
.channel
.storage_path
.join(&config.processing.vtt_dummy.clone().unwrap_or_default());
if vtt_file.is_file() {
if loop_count > 1 {
source_cmd.append(&mut vec_strings!["-stream_loop", loop_count]);
}
source_cmd.append(&mut vec_strings![
"-i",
vtt_file.to_string_lossy(),
"-t",
node.out
]);
} else if vtt_dummy.is_file() {
source_cmd.append(&mut vec_strings!["-i", vtt_dummy.to_string_lossy()]);
}
}
source_cmd
}
/// Set clip seek in and length value.
pub fn seek_and_length(node: &mut Media) -> Vec<String> {
pub fn seek_and_length(config: &PlayoutConfig, node: &mut Media) -> Vec<String> {
let loop_count = (node.out / node.duration).ceil() as i32;
let mut source_cmd = vec![];
let mut cut_audio = false;
@ -673,6 +728,28 @@ pub fn seek_and_length(node: &mut Media) -> Vec<String> {
}
}
if config.processing.vtt_enable {
let vtt_file = Path::new(&node.source).with_extension("vtt");
let vtt_dummy = config
.channel
.storage_path
.join(&config.processing.vtt_dummy.clone().unwrap_or_default());
if vtt_file.is_file() {
if loop_count > 1 {
source_cmd.append(&mut vec_strings!["-stream_loop", loop_count]);
}
source_cmd.append(&mut vec_strings!["-i", vtt_file.to_string_lossy()]);
if node.duration > node.out || remote_source || loop_count > 1 {
source_cmd.append(&mut vec_strings!["-t", node.out - node.seek]);
}
} else if vtt_dummy.is_file() {
source_cmd.append(&mut vec_strings!["-i", vtt_dummy.to_string_lossy()]);
}
}
source_cmd
}
@ -683,7 +760,7 @@ pub fn gen_dummy(config: &PlayoutConfig, duration: f64) -> (String, Vec<String>)
"color=c={color}:s={}x{}:d={duration}",
config.processing.width, config.processing.height
);
let cmd: Vec<String> = vec_strings![
let mut source_cmd: Vec<String> = vec_strings![
"-f",
"lavfi",
"-i",
@ -697,7 +774,18 @@ pub fn gen_dummy(config: &PlayoutConfig, duration: f64) -> (String, Vec<String>)
format!("anoisesrc=d={duration}:c=pink:r=48000:a=0.3")
];
(source, cmd)
if config.processing.vtt_enable {
let vtt_dummy = config
.channel
.storage_path
.join(&config.processing.vtt_dummy.clone().unwrap_or_default());
if vtt_dummy.is_file() {
source_cmd.append(&mut vec_strings!["-i", vtt_dummy.to_string_lossy()]);
}
}
(source, source_cmd)
}
// fn get_output_count(cmd: &[String]) -> i32 {