diff --git a/Cargo.lock b/Cargo.lock index a4fd1535..fe2f135e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1210,7 +1210,7 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "ffplayout" -version = "0.20.4-beta1" +version = "0.20.4-beta2" dependencies = [ "chrono", "clap", @@ -1232,7 +1232,7 @@ dependencies = [ [[package]] name = "ffplayout-api" -version = "0.20.4-beta1" +version = "0.20.4-beta2" dependencies = [ "actix-files", "actix-multipart", @@ -1271,7 +1271,7 @@ dependencies = [ [[package]] name = "ffplayout-lib" -version = "0.20.4-beta1" +version = "0.20.4-beta2" dependencies = [ "chrono", "crossbeam-channel", @@ -3436,7 +3436,7 @@ dependencies = [ [[package]] name = "tests" -version = "0.20.4-beta1" +version = "0.20.4-beta2" dependencies = [ "chrono", "crossbeam-channel", diff --git a/Cargo.toml b/Cargo.toml index 7bfccb56..a9ff6459 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ default-members = ["ffplayout-api", "ffplayout-engine", "tests"] resolver = "2" [workspace.package] -version = "0.20.4-beta1" +version = "0.20.4-beta2" license = "GPL-3.0" repository = "https://github.com/ffplayout/ffplayout" authors = ["Jonathan Baecker "] diff --git a/ffplayout-engine/src/input/playlist.rs b/ffplayout-engine/src/input/playlist.rs index 117d0292..a66af059 100644 --- a/ffplayout-engine/src/input/playlist.rs +++ b/ffplayout-engine/src/input/playlist.rs @@ -59,7 +59,8 @@ impl CurrentProgram { } } - // Check if playlist file got updated, and when yes we reload it and setup everything in place. + // Check if there is no current playlist or file got updated, + // and when is so load/reload it. fn load_or_update_playlist(&mut self, seek: bool) { let mut get_current = false; let mut reload = false; @@ -235,7 +236,7 @@ impl CurrentProgram { .current_list .lock() .unwrap() - .iter_mut() + .iter() .enumerate() { if item.begin.unwrap() + item.out - item.seek > time_sec { @@ -267,12 +268,12 @@ impl CurrentProgram { trace!("Clip from init: {}", node_clone.source); - // Important! When no manual drop is happen here, lock is still active in handle_list_init - drop(nodes); - node_clone.seek += time_sec - (node_clone.begin.unwrap() - *self.playout_stat.time_shift.lock().unwrap()); + // Important! When no manual drop is happen here, lock is still active in handle_list_init + drop(nodes); + self.current_node = handle_list_init( &self.config, node_clone, @@ -285,6 +286,7 @@ impl CurrentProgram { .current_node .source .contains(&self.config.storage.path.to_string_lossy().to_string()) + || self.current_node.source.contains("color=c=#121212") { is_filler = true; } @@ -392,6 +394,7 @@ impl Iterator for CurrentProgram { let node_list = self.player_control.current_list.lock().unwrap(); let node = node_list[index].clone(); let last_index = node_list.len() - 1; + drop(node_list); if index == last_index { @@ -519,6 +522,39 @@ fn timed_source( new_node } +fn duplicate_for_seek_and_loop(node: &mut Media, player_control: &PlayerControl) { + warn!("Clip loops and has seek value: duplicate clip to separate loop and seek."); + let mut nodes = player_control.current_list.lock().unwrap(); + let index = node.index.unwrap_or_default(); + + let mut node_duplicate = node.clone(); + node_duplicate.seek = 0.0; + let orig_seek = node.seek; + node.out = node.duration; + + if node.seek > node.duration { + node.seek %= node.duration; + + node_duplicate.out = node_duplicate.out - orig_seek - (node.out - node.seek); + } else { + node_duplicate.out -= node_duplicate.duration; + } + + if node.seek == node.out { + node.seek = node_duplicate.seek; + node.out = node_duplicate.out; + } else if node_duplicate.out - node_duplicate.seek > 1.2 { + node_duplicate.begin = + Some(node_duplicate.begin.unwrap_or_default() + (node.out - node.seek)); + + nodes.insert(index + 1, node_duplicate); + + for (i, item) in nodes.iter_mut().enumerate() { + item.index = Some(i); + } + } +} + /// Generate the source CMD, or when clip not exist, get a dummy. pub fn gen_source( config: &PlayoutConfig, @@ -536,9 +572,9 @@ pub fn gen_source( duration = 1.2; if node.seek > 1.0 { - node.seek -= 1.0; + node.seek -= 1.2; } else { - node.out += 1.0; + node.out = 1.2; } } @@ -563,6 +599,10 @@ pub fn gen_source( { node.cmd = Some(loop_image(&node)); } else { + if node.seek > 0.0 && node.out > node.duration { + duplicate_for_seek_and_loop(&mut node, player_control); + } + node.cmd = Some(seek_and_length(&mut node)); } } else { @@ -580,16 +620,17 @@ pub fn gen_source( Err(e) => error!("Lock filler list error: {e}"), } + // Set list_init to true, to stay in sync. + playout_stat.list_init.store(true, Ordering::SeqCst); + if config.storage.filler.is_dir() && !filler_list.is_empty() { let filler_index = player_control.filler_index.fetch_add(1, Ordering::SeqCst); let mut filler_media = filler_list[filler_index].clone(); trace!("take filler: {}", filler_media.source); - // Set list_init to true, to stay in sync. - playout_stat.list_init.store(true, Ordering::SeqCst); - if filler_index == filler_list.len() - 1 { + // reset index for next round player_control.filler_index.store(0, Ordering::SeqCst) } @@ -700,14 +741,11 @@ fn handle_list_init( ) -> Media { debug!("Playlist init"); let (_, total_delta) = get_delta(config, &node.begin.unwrap()); - let mut out = node.out; if node.out - node.seek > total_delta { - out = total_delta + node.seek; + node.out = total_delta + node.seek; } - node.out = out; - gen_source(config, node, playout_stat, player_control, last_index) } diff --git a/ffplayout-engine/src/output/desktop.rs b/ffplayout-engine/src/output/desktop.rs index 369007e1..ea739057 100644 --- a/ffplayout-engine/src/output/desktop.rs +++ b/ffplayout-engine/src/output/desktop.rs @@ -40,7 +40,7 @@ pub fn output(config: &PlayoutConfig, log_format: &str) -> process::Child { }) { enc_cmd.append(&mut cmd); } else { - warn!("ffplay does not support given output parameters, they are skipped!"); + warn!("ffplay doesn't support given output parameters, they will be skipped!"); } } diff --git a/lib/src/utils/mod.rs b/lib/src/utils/mod.rs index 40614da8..159efcb6 100644 --- a/lib/src/utils/mod.rs +++ b/lib/src/utils/mod.rs @@ -529,6 +529,7 @@ pub fn loop_filler(node: &Media) -> Vec { /// Set clip seek in and length value. pub fn seek_and_length(node: &mut Media) -> Vec { + let loop_count = (node.out / node.duration).ceil() as i32; let mut source_cmd = vec![]; let mut cut_audio = false; let mut loop_audio = false; @@ -541,9 +542,15 @@ pub fn seek_and_length(node: &mut Media) -> Vec { source_cmd.append(&mut vec_strings!["-ss", node.seek]) } + if loop_count > 1 { + info!("Loop {} {loop_count} times, total duration: {:.2}", node.source, node.out); + + source_cmd.append(&mut vec_strings!["-stream_loop", loop_count]); + } + source_cmd.append(&mut vec_strings!["-i", node.source.clone()]); - if node.duration > node.out || remote_source { + if node.duration > node.out || remote_source || loop_count > 1 { source_cmd.append(&mut vec_strings!["-t", node.out - node.seek]); } @@ -552,9 +559,9 @@ pub fn seek_and_length(node: &mut Media) -> Vec { source_cmd.append(&mut vec_strings!["-ss", node.seek]); } - if node.duration_audio > node.duration { + if node.duration_audio > node.out { cut_audio = true; - } else if node.duration_audio < node.duration { + } else if node.duration_audio < node.out { source_cmd.append(&mut vec_strings!["-stream_loop", -1]); loop_audio = true; }