From 00ae975a815d8424facd042b0c009c269b3f5256 Mon Sep 17 00:00:00 2001 From: jb-alvarado Date: Wed, 20 Mar 2024 21:12:47 +0100 Subject: [PATCH] work on new infinit mode --- ffplayout-engine/src/input/playlist.rs | 31 +- ffplayout-engine/src/output/mod.rs | 12 +- lib/src/utils/json_serializer.rs | 88 ++---- lib/src/utils/json_validate.rs | 7 + lib/src/utils/mod.rs | 26 +- .../assets/playlists/2024/03/2024-03-19.json | 288 ++++++++++++++++++ 6 files changed, 369 insertions(+), 83 deletions(-) create mode 100644 tests/assets/playlists/2024/03/2024-03-19.json diff --git a/ffplayout-engine/src/input/playlist.rs b/ffplayout-engine/src/input/playlist.rs index a66af059..dfbef74a 100644 --- a/ffplayout-engine/src/input/playlist.rs +++ b/ffplayout-engine/src/input/playlist.rs @@ -80,7 +80,7 @@ impl CurrentProgram { if get_current { let json = read_json( - &self.config, + &mut self.config, &self.player_control, self.json_path.clone(), self.is_terminated.clone(), @@ -137,15 +137,16 @@ impl CurrentProgram { trace!("next_start: {next_start}, end_sec: {}", self.end_sec); // Check if we over the target length or we are close to it, if so we load the next playlist. - if next_start >= self.end_sec - || is_close(total_delta, 0.0, 2.0) - || is_close(total_delta, self.end_sec, 2.0) + if !self.config.playlist.infinit + && (next_start >= self.end_sec + || is_close(total_delta, 0.0, 2.0) + || is_close(total_delta, self.end_sec, 2.0)) { trace!("get next day"); next = true; let json = read_json( - &self.config, + &mut self.config, &self.player_control, None, self.is_terminated.clone(), @@ -212,7 +213,7 @@ impl CurrentProgram { let mut time_sec = time_in_seconds(); if time_sec < self.start_sec { - time_sec += self.config.playlist.length_sec.unwrap() + time_sec += 86400.0 // self.config.playlist.length_sec.unwrap(); } time_sec @@ -222,6 +223,7 @@ impl CurrentProgram { fn get_current_clip(&mut self) { let mut time_sec = self.get_current_time(); let shift = self.playout_stat.time_shift.lock().unwrap(); + let p_length = self.config.playlist.length_sec.unwrap(); if *self.playout_stat.current_date.lock().unwrap() == *self.playout_stat.date.lock().unwrap() @@ -231,6 +233,10 @@ impl CurrentProgram { time_sec += *shift; } + if self.config.playlist.infinit && p_length < 86400.0 { + time_sec -= p_length; + } + for (i, item) in self .player_control .current_list @@ -255,7 +261,8 @@ impl CurrentProgram { let mut is_filler = false; if !self.playout_stat.list_init.load(Ordering::SeqCst) { - let time_sec = self.get_current_time(); + let mut time_sec = self.get_current_time(); + let p_length = self.config.playlist.length_sec.unwrap(); let index = self .player_control .current_index @@ -268,6 +275,10 @@ impl CurrentProgram { trace!("Clip from init: {}", node_clone.source); + if self.config.playlist.infinit && p_length < 86400.0 { + time_sec -= p_length; + } + node_clone.seek += time_sec - (node_clone.begin.unwrap() - *self.playout_stat.time_shift.lock().unwrap()); @@ -488,7 +499,8 @@ fn timed_source( debug!("Delta: {shifted_delta:.3}"); } - if config.general.stop_threshold > 0.0 + if !config.playlist.infinit + && config.general.stop_threshold > 0.0 && shifted_delta.abs() > config.general.stop_threshold { error!("Clip begin out of sync for {delta:.3} seconds."); @@ -502,6 +514,7 @@ fn timed_source( if (total_delta > node.out - node.seek && !last) || node.index.unwrap() < 2 || !config.playlist.length.contains(':') + || config.playlist.infinit { // when we are in the 24 hour range, get the clip new_node.process = Some(true); @@ -742,7 +755,7 @@ fn handle_list_init( debug!("Playlist init"); let (_, total_delta) = get_delta(config, &node.begin.unwrap()); - if node.out - node.seek > total_delta { + if !config.playlist.infinit && node.out - node.seek > total_delta { node.out = total_delta + node.seek; } diff --git a/ffplayout-engine/src/output/mod.rs b/ffplayout-engine/src/output/mod.rs index fd1cbb36..6fe25df3 100644 --- a/ffplayout-engine/src/output/mod.rs +++ b/ffplayout-engine/src/output/mod.rs @@ -107,8 +107,18 @@ pub fn player( continue; } + let c_index = if cfg!(debug_assertions) { + format!( + " ({}/{})", + node.index.unwrap() + 1, + play_control.current_list.lock().unwrap().len() + ) + } else { + String::new() + }; + info!( - "Play for {}: {} {}", + "Play for {}{c_index}: {} {}", sec_to_time(node.out - node.seek), node.source, node.audio diff --git a/lib/src/utils/json_serializer.rs b/lib/src/utils/json_serializer.rs index 920e523a..6ee63ddd 100644 --- a/lib/src/utils/json_serializer.rs +++ b/lib/src/utils/json_serializer.rs @@ -9,8 +9,8 @@ use std::{ use simplelog::*; use crate::utils::{ - controller::ProcessUnit::*, get_date, is_remote, modified_time, time_from_header, - validate_playlist, Media, PlayerControl, PlayoutConfig, DUMMY_LEN, + get_date, is_remote, modified_time, sec_to_time, time_from_header, validate_playlist, Media, + PlayerControl, PlayoutConfig, DUMMY_LEN, }; /// This is our main playlist object, it holds all necessary information for the current day. @@ -65,9 +65,10 @@ fn set_defaults( mut playlist: JsonPlaylist, current_file: String, mut start_sec: f64, -) -> JsonPlaylist { +) -> (JsonPlaylist, f64) { playlist.current_file = Some(current_file); playlist.start_sec = Some(start_sec); + let mut length = 0.0; // Add extra values to every media clip for (i, item) in playlist.program.iter_mut().enumerate() { @@ -78,69 +79,18 @@ fn set_defaults( item.process = Some(true); item.filter = None; - start_sec += item.out - item.seek; + let dur = item.out - item.seek; + start_sec += dur; + length += dur; } - playlist -} - -fn loop_playlist( - config: &PlayoutConfig, - current_file: String, - mut playlist: JsonPlaylist, -) -> JsonPlaylist { - let start_sec = config.playlist.start_sec.unwrap(); - let mut begin = start_sec; - let length = config.playlist.length_sec.unwrap(); - let mut program_list = vec![]; - let mut index = 0; - - playlist.current_file = Some(current_file); - playlist.start_sec = Some(start_sec); - - 'program_looper: loop { - for item in playlist.program.iter() { - let media = Media { - index: Some(index), - begin: Some(begin), - seek: item.seek, - out: item.out, - duration: item.duration, - duration_audio: item.duration_audio, - category: item.category.clone(), - source: item.source.clone(), - audio: item.audio.clone(), - cmd: item.cmd.clone(), - probe: item.probe.clone(), - probe_audio: item.probe_audio.clone(), - process: Some(true), - unit: Decoder, - last_ad: false, - next_ad: false, - filter: None, - custom_filter: String::new(), - }; - - if begin < start_sec + length { - program_list.push(media); - } else { - break 'program_looper; - } - - begin += item.out - item.seek; - index += 1; - } - } - - playlist.program = program_list; - - playlist + (playlist, length) } /// Read json playlist file, fills JsonPlaylist struct and set some extra values, /// which we need to process. pub fn read_json( - config: &PlayoutConfig, + config: &mut PlayoutConfig, player_control: &PlayerControl, path: Option, is_terminated: Arc, @@ -197,10 +147,14 @@ pub fn read_json( }); } - match config.playlist.infinit { - true => return loop_playlist(config, current_file, playlist), - false => return set_defaults(playlist, current_file, start_sec), + let (playlist, duration) = set_defaults(playlist, current_file, start_sec); + + if config.playlist.infinit || config.playlist.length.is_empty() { + config.playlist.length = sec_to_time(duration); + config.playlist.length_sec = Some(duration); } + + return playlist; } } } @@ -235,10 +189,14 @@ pub fn read_json( }); } - match config.playlist.infinit { - true => return loop_playlist(config, current_file, playlist), - false => return set_defaults(playlist, current_file, start_sec), + let (playlist, duration) = set_defaults(playlist, current_file, start_sec); + + if config.playlist.infinit || config.playlist.length.is_empty() { + config.playlist.length = sec_to_time(duration); + config.playlist.length_sec = Some(duration); } + + return playlist; } error!("Playlist {current_file} not exist!"); diff --git a/lib/src/utils/json_validate.rs b/lib/src/utils/json_validate.rs index 38c38913..62f30bf1 100644 --- a/lib/src/utils/json_validate.rs +++ b/lib/src/utils/json_validate.rs @@ -236,5 +236,12 @@ pub fn validate_playlist( ); } + if config.general.validate { + info!( + "[Validation] Playlist length: {}", + sec_to_time(begin - config.playlist.start_sec.unwrap()) + ); + } + debug!("Validation done, in {:.3?} ...", timer.elapsed(),); } diff --git a/lib/src/utils/mod.rs b/lib/src/utils/mod.rs index 44652595..96105797 100644 --- a/lib/src/utils/mod.rs +++ b/lib/src/utils/mod.rs @@ -7,7 +7,6 @@ use std::{ path::{Path, PathBuf}, process::{exit, ChildStderr, Command, Stdio}, sync::{Arc, Mutex}, - time::{self, UNIX_EPOCH}, }; #[cfg(not(windows))] @@ -427,11 +426,12 @@ pub fn time_to_sec(time_str: &str) -> f64 { /// Convert floating number (seconds) to a formatted time string. pub fn sec_to_time(sec: f64) -> String { - let d = UNIX_EPOCH + time::Duration::from_millis((sec * 1000.0) as u64); - // Create DateTime from SystemTime - let date_time = DateTime::::from(d); - - date_time.format("%H:%M:%S%.3f").to_string() + format!( + "{:0>2}:{:0>2}:{:06.3}", + (sec / 60.0 / 60.0) as i32, + (sec / 60.0 % 60.0) as i32, + (sec % 60.0), + ) } /// get file extension @@ -461,18 +461,20 @@ pub fn sum_durations(clip_list: &Vec) -> f64 { /// /// We also get here the global delta between clip start and time when a new playlist should start. pub fn get_delta(config: &PlayoutConfig, begin: &f64) -> (f64, f64) { + let time_sec = time_in_seconds(); let mut current_time = time_in_seconds(); let start = config.playlist.start_sec.unwrap(); - let length = time_to_sec(&config.playlist.length); + let length = config.playlist.length_sec.unwrap_or(86400.0); let mut target_length = 86400.0; if length > 0.0 && length != target_length { target_length = length } + if begin == &start && start == 0.0 && 86400.0 - current_time < 4.0 { current_time -= target_length } else if start >= current_time && begin != &start { - current_time += target_length + current_time += 86400.0 } let mut current_delta = begin - current_time; @@ -487,6 +489,14 @@ pub fn get_delta(config: &PlayoutConfig, begin: &f64) -> (f64, f64) { target_length + start - current_time }; + if config.playlist.infinit && length < 86400.0 { + current_delta += length; + + if time_sec > start { + current_delta -= 86400.0; + } + } + (current_delta, total_delta) } diff --git a/tests/assets/playlists/2024/03/2024-03-19.json b/tests/assets/playlists/2024/03/2024-03-19.json new file mode 100644 index 00000000..2952577d --- /dev/null +++ b/tests/assets/playlists/2024/03/2024-03-19.json @@ -0,0 +1,288 @@ +{ + "channel": "Test 1", + "date": "2024-02-01", + "program": [ + { + "in": 0, + "out": 10.0, + "duration": 10.0, + "source": "tests/assets/media_sorted/DarkGray_00-00-10.mp4" + }, + { + "in": 0, + "out": 30.0, + "duration": 30.0, + "source": "tests/assets/media_sorted/Olive_00-00-30.mp4" + }, + { + "in": 0, + "out": 15.0, + "duration": 15.0, + "source": "tests/assets/media_sorted/Indigo_00-00-15.mp4" + }, + { + "in": 0, + "out": 25.0, + "duration": 25.0, + "source": "tests/assets/media_sorted/DarkOrchid_00-00-25.mp4" + }, + { + "in": 0, + "out": 45.0, + "duration": 45.0, + "source": "tests/assets/media_sorted/Orange_00-00-45.mp4" + }, + { + "in": 0, + "out": 20.0, + "duration": 20.0, + "source": "tests/assets/media_sorted/LightGoldenRodYellow_00-00-20.mp4" + }, + { + "in": 0, + "out": 30.0, + "duration": 30.0, + "source": "tests/assets/media_sorted/Cyan_00-00-30.mp4" + }, + { + "in": 0, + "out": 50.0, + "duration": 50.0, + "source": "tests/assets/media_sorted/Cornsilk_00-00-50.mp4" + }, + { + "in": 0, + "out": 15.0, + "duration": 15.0, + "source": "tests/assets/media_sorted/LightSeaGreen_00-00-15.mp4" + }, + { + "in": 0, + "out": 30.0, + "duration": 30.0, + "source": "tests/assets/media_sorted/Yellow_00-00-30.mp4" + }, + { + "in": 0, + "out": 30.0, + "duration": 30.0, + "source": "tests/assets/media_sorted/Aqua_00-00-30.mp4" + }, + { + "in": 0, + "out": 1500.0, + "duration": 1500.0, + "source": "tests/assets/media_sorted/MediumSeaGreen_00-25-00.mp4" + }, + { + "in": 0, + "out": 1800.0, + "duration": 1800.0, + "source": "tests/assets/media_sorted/MediumOrchid_00-30-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/Plum_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/Plum_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/Plum_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/Plum_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/Plum_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/Plum_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/ForestGreen_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/Plum_01-00-00.mp4" + }, + { + "in": 0, + "out": 3600.0, + "duration": 3600.0, + "source": "tests/assets/media_sorted/IndianRed_01-00-00.mp4" + }, + { + "in": 0, + "out": 1800.0, + "duration": 1800.0, + "source": "tests/assets/media_sorted/MediumOrchid_00-30-00.mp4" + }, + { + "in": 0, + "out": 1500.0, + "duration": 1500.0, + "source": "tests/assets/media_sorted/MediumSeaGreen_00-25-00.mp4" + }, + { + "in": 0, + "out": 10.0, + "duration": 10.0, + "source": "tests/assets/media_sorted/DarkGray_00-00-10.mp4" + }, + { + "in": 0, + "out": 30.0, + "duration": 30.0, + "source": "tests/assets/media_sorted/Olive_00-00-30.mp4" + }, + { + "in": 0, + "out": 15.0, + "duration": 15.0, + "source": "tests/assets/media_sorted/Indigo_00-00-15.mp4" + }, + { + "in": 0, + "out": 25.0, + "duration": 25.0, + "source": "tests/assets/media_sorted/DarkOrchid_00-00-25.mp4" + }, + { + "in": 0, + "out": 45.0, + "duration": 45.0, + "source": "tests/assets/media_sorted/Orange_00-00-45.mp4" + }, + { + "in": 0, + "out": 20.0, + "duration": 20.0, + "source": "tests/assets/media_sorted/LightGoldenRodYellow_00-00-20.mp4" + }, + { + "in": 0, + "out": 30.0, + "duration": 30.0, + "source": "tests/assets/media_sorted/Cyan_00-00-30.mp4" + }, + { + "in": 0, + "out": 50.0, + "duration": 50.0, + "source": "tests/assets/media_sorted/Cornsilk_00-00-50.mp4" + }, + { + "in": 0, + "out": 15.0, + "duration": 15.0, + "source": "tests/assets/media_sorted/LightSeaGreen_00-00-15.mp4" + }, + { + "in": 0, + "out": 30.0, + "duration": 30.0, + "source": "tests/assets/media_sorted/Yellow_00-00-30.mp4" + } + ] +}