From 72b390aba9a5d9a125fbf8f35b010d4282486188 Mon Sep 17 00:00:00 2001 From: jb-alvarado Date: Mon, 24 Jul 2023 16:57:07 +0200 Subject: [PATCH] play longer filler clips --- ffplayout-engine/src/input/playlist.rs | 108 ++++++++++++++++++------ ffplayout-engine/src/main.rs | 10 +-- ffplayout-engine/src/output/mod.rs | 4 + ffplayout-engine/src/utils/arg_parse.rs | 3 + ffplayout-engine/src/utils/mod.rs | 5 ++ lib/src/filter/mod.rs | 11 ++- lib/src/utils/folder.rs | 34 +++++--- lib/src/utils/mod.rs | 10 ++- 8 files changed, 130 insertions(+), 55 deletions(-) diff --git a/ffplayout-engine/src/input/playlist.rs b/ffplayout-engine/src/input/playlist.rs index ec9a142d..3f0fd424 100644 --- a/ffplayout-engine/src/input/playlist.rs +++ b/ffplayout-engine/src/input/playlist.rs @@ -141,6 +141,7 @@ impl CurrentProgram { duration = self.current_node.duration } + // TODO: add unwrap_or_default() let mut next_start = self.current_node.begin.unwrap() - start_sec + duration + delta; if self.player_control.current_index.load(Ordering::SeqCst) @@ -313,11 +314,30 @@ impl Iterator for CurrentProgram { // fill missing length from playlist let mut current_time = get_sec(); let (_, total_delta) = get_delta(&self.config, ¤t_time); - let mut duration = DUMMY_LEN; + let mut out = total_delta.abs(); + let mut duration = out + 0.001; + self.playout_stat.list_init.store(false, Ordering::SeqCst); - if DUMMY_LEN > total_delta { + trace!("Total delta on list init: {total_delta}"); + + let filler_index = self.player_control.filler_index.load(Ordering::SeqCst); + let mut filler = + self.player_control.filler_list.lock().unwrap()[filler_index].clone(); + + filler.add_probe(); + + if filler.duration > 0.0 { + if duration > filler.duration { + out = filler.duration; + } + + duration = filler.duration; + } else if DUMMY_LEN > total_delta { duration = total_delta; - self.playout_stat.list_init.store(false, Ordering::SeqCst); + out = total_delta; + } else { + duration = DUMMY_LEN; + out = DUMMY_LEN; } if self.config.playlist.start_sec.unwrap() > current_time { @@ -330,7 +350,7 @@ impl Iterator for CurrentProgram { let mut media = Media::new(index, "", false); media.begin = Some(current_time); media.duration = duration; - media.out = duration; + media.out = out; self.current_node = gen_source( &self.config, @@ -392,18 +412,32 @@ impl Iterator for CurrentProgram { && last_playlist == self.json_path && total_delta.abs() > 1.0 { - // Test if playlist is to early finish, + trace!("Total delta on list end: {total_delta}"); + + // Playlist is to early finish, // and if we have to fill it with a placeholder. let index = self.player_control.current_index.load(Ordering::SeqCst); self.current_node = Media::new(index, "", false); self.current_node.begin = Some(get_sec()); - let mut duration = total_delta.abs(); + let mut out = total_delta.abs(); + let mut duration = out + 0.001; - if duration > DUMMY_LEN { - duration = DUMMY_LEN; + let filler_index = self.player_control.filler_index.load(Ordering::SeqCst); + let mut filler = + self.player_control.filler_list.lock().unwrap()[filler_index].clone(); + + filler.add_probe(); + + if filler.duration > 0.0 { + if duration > filler.duration { + out = filler.duration; + } + + duration = filler.duration; } + self.current_node.duration = duration; - self.current_node.out = duration; + self.current_node.out = out; self.current_node = gen_source( &self.config, self.current_node.clone(), @@ -526,7 +560,9 @@ pub fn gen_source( player_control: &PlayerControl, last_index: usize, ) -> Media { - let duration = node.out - node.seek; + let mut duration = node.out - node.seek; + + trace!("Clip out: {duration}, duration: {}", node.duration); if valid_source(&node.source) { node.add_probe(); @@ -553,9 +589,9 @@ pub fn gen_source( error!("Source not found: \"{}\"", node.source); } - if Path::new(&config.storage.filler).is_dir() - && !player_control.filler_list.lock().unwrap().is_empty() - { + let filler_source = Path::new(&config.storage.filler); + + if filler_source.is_dir() && !player_control.filler_list.lock().unwrap().is_empty() { let filler_index = player_control.filler_index.fetch_add(1, Ordering::SeqCst); let mut filler_media = player_control.filler_list.lock().unwrap()[filler_index].clone(); @@ -563,22 +599,20 @@ pub fn gen_source( player_control.filler_index.store(0, Ordering::SeqCst) } - filler_media.add_probe(); + if filler_media.probe.is_none() { + filler_media.add_probe(); + } - if filler_media.duration > duration { + if node.duration > duration && filler_media.duration > duration { filler_media.out = duration; } - warn!( - "Generate filler with {:.2} seconds length!", - filler_media.out - ); - - node = filler_media; + node.source = filler_media.source; + node.duration = filler_media.duration; + node.out = filler_media.out; node.cmd = Some(loop_filler(&node)); - } else { - warn!("Generate filler with {duration:.2} seconds length!"); - + node.probe = filler_media.probe; + } else if filler_source.is_file() { let probe = MediaProbe::new(&config.storage.filler); if config @@ -592,7 +626,7 @@ pub fn gen_source( node.source = config.storage.filler.clone(); node.cmd = Some(loop_image(&node)); node.probe = Some(probe); - } else if let Some(length) = probe + } else if let Some(filler_duration) = probe .clone() .format .and_then(|f| f.duration) @@ -600,8 +634,14 @@ pub fn gen_source( { // Create placeholder from config filler. node.source = config.storage.filler.clone(); - node.duration = length; - node.out = duration; + + node.out = if node.duration > duration && filler_duration > duration { + duration + } else { + filler_duration + }; + + node.duration = filler_duration; node.cmd = Some(loop_filler(&node)); node.probe = Some(probe); } else { @@ -610,7 +650,21 @@ pub fn gen_source( node.source = source; node.cmd = Some(cmd); } + } else { + if duration > DUMMY_LEN { + duration = DUMMY_LEN; + node.duration = duration; + node.out = duration; + } + let (source, cmd) = gen_dummy(config, duration); + node.source = source; + node.cmd = Some(cmd); } + + warn!( + "Generate filler with {:.2} seconds length!", + node.out + ); } node.add_filter(config, filter_chain); diff --git a/ffplayout-engine/src/main.rs b/ffplayout-engine/src/main.rs index 3d1cd5c4..624066f8 100644 --- a/ffplayout-engine/src/main.rs +++ b/ffplayout-engine/src/main.rs @@ -212,14 +212,8 @@ fn main() { config.general.config_path ); - if Path::new(&config.storage.filler).is_dir() { - debug!( - "Fill filler list from: {}", - config.storage.filler - ); - - thread::spawn(move || fill_filler_list(config_clone2, play_ctl2)); - } + // Fill filler list, can also be a single file. + thread::spawn(move || fill_filler_list(config_clone2, play_ctl2)); match config.out.mode { // write files/playlist to HLS m3u8 playlist diff --git a/ffplayout-engine/src/output/mod.rs b/ffplayout-engine/src/output/mod.rs index 2f3f2488..1b17a247 100644 --- a/ffplayout-engine/src/output/mod.rs +++ b/ffplayout-engine/src/output/mod.rs @@ -111,6 +111,8 @@ pub fn player( } } + trace!("Decoder CMD: {:?}", node.cmd); + let mut cmd = match node.cmd { Some(cmd) => cmd, None => break, @@ -223,6 +225,8 @@ pub fn player( }; } + trace!("Out of source loop"); + sleep(Duration::from_secs(1)); proc_control.stop_all(); diff --git a/ffplayout-engine/src/utils/arg_parse.rs b/ffplayout-engine/src/utils/arg_parse.rs index c4ea1e28..9d4f7f22 100644 --- a/ffplayout-engine/src/utils/arg_parse.rs +++ b/ffplayout-engine/src/utils/arg_parse.rs @@ -66,6 +66,9 @@ pub struct Args { )] pub length: Option, + #[clap(long, help = "Override logging level")] + pub level: Option, + #[clap(short, long, help = "Loop playlist infinitely")] pub infinit: bool, diff --git a/ffplayout-engine/src/utils/mod.rs b/ffplayout-engine/src/utils/mod.rs index 2affad7e..3d77739a 100644 --- a/ffplayout-engine/src/utils/mod.rs +++ b/ffplayout-engine/src/utils/mod.rs @@ -86,6 +86,11 @@ pub fn get_config(args: Args) -> PlayoutConfig { } } + // TODO: implement this + // if let Some(level) = args.level { + // config.logging.level = LevelFilter::from(level); + // } + if args.infinit { config.playlist.infinit = args.infinit; } diff --git a/lib/src/filter/mod.rs b/lib/src/filter/mod.rs index 867cb4a8..554e0bed 100644 --- a/lib/src/filter/mod.rs +++ b/lib/src/filter/mod.rs @@ -577,10 +577,13 @@ pub fn filter_chains( { extend_audio(node, &mut filters, i); } else if node.unit == Decoder { - warn!( - "Missing audio track (id {i}) from {}", - node.source - ); + if !node.source.contains("color=c=") { + warn!( + "Missing audio track (id {i}) from {}", + node.source + ); + } + add_audio(node, &mut filters, i); } diff --git a/lib/src/utils/folder.rs b/lib/src/utils/folder.rs index 6abbace2..5bb841e3 100644 --- a/lib/src/utils/folder.rs +++ b/lib/src/utils/folder.rs @@ -163,21 +163,29 @@ impl Iterator for FolderSource { pub fn fill_filler_list(config: PlayoutConfig, player_control: PlayerControl) { let mut filler_list = vec![]; - for (index, entry) in WalkDir::new(&config.storage.filler) - .into_iter() - .flat_map(|e| e.ok()) - .filter(|f| f.path().is_file()) - .filter(|f| include_file_extension(&config, f.path())) - .enumerate() - { - let media = Media::new(index, &entry.path().to_string_lossy(), false); - filler_list.push(media); - } + if Path::new(&config.storage.filler).is_dir() { + debug!( + "Fill filler list from: {}", + config.storage.filler + ); - if config.storage.shuffle { - let mut rng = thread_rng(); + for (index, entry) in WalkDir::new(&config.storage.filler) + .into_iter() + .flat_map(|e| e.ok()) + .filter(|f| f.path().is_file()) + .filter(|f| include_file_extension(&config, f.path())) + .enumerate() + { + filler_list.push(Media::new(index, &entry.path().to_string_lossy(), false)); + } - filler_list.shuffle(&mut rng); + if config.storage.shuffle { + let mut rng = thread_rng(); + + filler_list.shuffle(&mut rng); + } + } else { + filler_list.push(Media::new(0, &config.storage.filler, false)); } *player_control.filler_list.lock().unwrap() = filler_list; diff --git a/lib/src/utils/mod.rs b/lib/src/utils/mod.rs index 4265755a..c0c4007e 100644 --- a/lib/src/utils/mod.rs +++ b/lib/src/utils/mod.rs @@ -238,9 +238,13 @@ impl MediaProbe { } } Err(e) => { - error!( - "Can't read source {input} with ffprobe, source not exists or damaged! Error in: {e:?}" - ); + if Path::new(input).is_file() { + error!( + "Can't read source {input} with ffprobe! Error: {e:?}" + ); + } else if !input.is_empty() { + error!("File not exists: {input}"); + } MediaProbe { format: None,