diff --git a/ffplayout-engine/src/output/hls.rs b/ffplayout-engine/src/output/hls.rs index cd6c755b..f1fb3975 100644 --- a/ffplayout-engine/src/output/hls.rs +++ b/ffplayout-engine/src/output/hls.rs @@ -28,10 +28,11 @@ use std::{ use simplelog::*; use crate::input::{ingest::log_line, source_generator}; +use crate::utils::prepare_output_cmd; use ffplayout_lib::filter::filter_chains; use ffplayout_lib::utils::{ - prepare_output_cmd, sec_to_time, stderr_reader, test_tcp_port, Encoder, Ingest, Media, - PlayerControl, PlayoutConfig, PlayoutStatus, ProcessControl, + sec_to_time, stderr_reader, test_tcp_port, Encoder, Ingest, Media, PlayerControl, + PlayoutConfig, PlayoutStatus, ProcessControl, }; use ffplayout_lib::vec_strings; diff --git a/ffplayout-engine/src/output/stream.rs b/ffplayout-engine/src/output/stream.rs index 4b9cd9b7..8a9639a3 100644 --- a/ffplayout-engine/src/output/stream.rs +++ b/ffplayout-engine/src/output/stream.rs @@ -5,8 +5,9 @@ use std::{ use simplelog::*; +use crate::utils::prepare_output_cmd; use ffplayout_lib::filter::v_drawtext; -use ffplayout_lib::utils::{prepare_output_cmd, PlayoutConfig}; +use ffplayout_lib::utils::PlayoutConfig; use ffplayout_lib::vec_strings; /// Streaming Output diff --git a/ffplayout-engine/src/utils/mod.rs b/ffplayout-engine/src/utils/mod.rs index 44a5baba..113bf119 100644 --- a/ffplayout-engine/src/utils/mod.rs +++ b/ffplayout-engine/src/utils/mod.rs @@ -6,8 +6,12 @@ use std::{ pub mod arg_parse; pub use arg_parse::Args; -use ffplayout_lib::utils::{time_to_sec, PlayoutConfig}; +use ffplayout_lib::{ + utils::{time_to_sec, PlayoutConfig}, + vec_strings, +}; +/// Read command line arguments, and override the config with them. pub fn get_config(args: Args) -> PlayoutConfig { let cfg_path = match args.channel { Some(c) => { @@ -80,4 +84,79 @@ pub fn get_config(args: Args) -> PlayoutConfig { config } -// Read command line arguments, and override the config with them. + +/// Prepare output parameters +/// +/// seek for multiple outputs and add mapping for it +pub fn prepare_output_cmd( + prefix: Vec, + mut filter: Vec, + params: Vec, + mode: &str, +) -> Vec { + let params_len = params.len(); + let mut output_params = params.clone(); + let mut output_a_map = "[a_out1]".to_string(); + let mut output_v_map = "[v_out1]".to_string(); + let mut output_count = 1; + let mut cmd = prefix; + + if !filter.is_empty() { + output_params.clear(); + + for (i, p) in params.iter().enumerate() { + let mut param = p.clone(); + + param = param.replace("[0:v]", "[vout0]"); + param = param.replace("[0:a]", "[aout0]"); + + if param != "-filter_complex" { + output_params.push(param.clone()); + } + + if i > 0 + && !param.starts_with('-') + && !params[i - 1].starts_with('-') + && i < params_len - 1 + { + output_count += 1; + let mut a_map = "0:a".to_string(); + let v_map = format!("[v_out{output_count}]"); + output_v_map.push_str(&v_map); + + if mode == "hls" { + a_map = format!("[a_out{output_count}]"); + } + + output_a_map.push_str(&a_map); + + let mut map = vec_strings!["-map", v_map, "-map", a_map]; + + output_params.append(&mut map); + } + } + + if output_count > 1 && mode == "hls" { + filter[1].push_str(&format!(";[vout0]split={output_count}{output_v_map}")); + filter[1].push_str(&format!(";[aout0]asplit={output_count}{output_a_map}")); + filter.drain(2..); + cmd.append(&mut filter); + cmd.append(&mut vec_strings!["-map", "[v_out1]", "-map", "[a_out1]"]); + } else if output_count == 1 && mode == "hls" && output_params[0].contains("split") { + let out_filter = output_params.remove(0); + filter[1].push_str(&format!(";{out_filter}")); + filter.drain(2..); + cmd.append(&mut filter); + } else if output_count > 1 && mode == "stream" { + filter[1].push_str(&format!(",split={output_count}{output_v_map}")); + cmd.append(&mut filter); + cmd.append(&mut vec_strings!["-map", "[v_out1]", "-map", "0:a"]); + } else { + cmd.append(&mut filter); + } + } + + cmd.append(&mut output_params); + + cmd +} diff --git a/lib/src/utils/mod.rs b/lib/src/utils/mod.rs index 7918c8cd..f00055e2 100644 --- a/lib/src/utils/mod.rs +++ b/lib/src/utils/mod.rs @@ -570,82 +570,6 @@ pub fn gen_dummy(config: &PlayoutConfig, duration: f64) -> (String, Vec) // count // } -/// Prepare output parameters -/// -/// seek for multiple outputs and add mapping for it -pub fn prepare_output_cmd( - prefix: Vec, - mut filter: Vec, - params: Vec, - mode: &str, -) -> Vec { - let params_len = params.len(); - let mut output_params = params.clone(); - let mut output_a_map = "[a_out1]".to_string(); - let mut output_v_map = "[v_out1]".to_string(); - let mut output_count = 1; - let mut cmd = prefix; - - if !filter.is_empty() { - output_params.clear(); - - for (i, p) in params.iter().enumerate() { - let mut param = p.clone(); - - param = param.replace("[0:v]", "[vout0]"); - param = param.replace("[0:a]", "[aout0]"); - - if param != "-filter_complex" { - output_params.push(param.clone()); - } - - if i > 0 - && !param.starts_with('-') - && !params[i - 1].starts_with('-') - && i < params_len - 1 - { - output_count += 1; - let mut a_map = "0:a".to_string(); - let v_map = format!("[v_out{output_count}]"); - output_v_map.push_str(&v_map); - - if mode == "hls" { - a_map = format!("[a_out{output_count}]"); - } - - output_a_map.push_str(&a_map); - - let mut map = vec_strings!["-map", v_map, "-map", a_map]; - - output_params.append(&mut map); - } - } - - if output_count > 1 && mode == "hls" { - filter[1].push_str(&format!(";[vout0]split={output_count}{output_v_map}")); - filter[1].push_str(&format!(";[aout0]asplit={output_count}{output_a_map}")); - filter.drain(2..); - cmd.append(&mut filter); - cmd.append(&mut vec_strings!["-map", "[v_out1]", "-map", "[a_out1]"]); - } else if output_count == 1 && mode == "hls" && output_params[0].contains("split") { - let out_filter = output_params.remove(0); - filter[1].push_str(&format!(";{out_filter}")); - filter.drain(2..); - cmd.append(&mut filter); - } else if output_count > 1 && mode == "stream" { - filter[1].push_str(&format!(",split={output_count}{output_v_map}")); - cmd.append(&mut filter); - cmd.append(&mut vec_strings!["-map", "[v_out1]", "-map", "0:a"]); - } else { - cmd.append(&mut filter); - } - } - - cmd.append(&mut output_params); - - cmd -} - pub fn is_remote(path: &str) -> bool { Regex::new(r"^https?://.*").unwrap().is_match(path) } diff --git a/tests/src/engine_cmd.rs b/tests/src/engine_cmd.rs index 6ff031cd..1adac7f3 100644 --- a/tests/src/engine_cmd.rs +++ b/tests/src/engine_cmd.rs @@ -1,4 +1,4 @@ -use ffplayout::input::playlist::gen_source; +use ffplayout::{input::playlist::gen_source, utils::prepare_output_cmd}; use ffplayout_lib::{ utils::{Media, PlayoutConfig}, vec_strings, @@ -80,3 +80,131 @@ fn dual_audio_input() { assert_eq!(media.cmd, Some(vec_strings!["-i", "assets/dual_audio.mp4"])); assert_eq!(media.filter, test_filter_cmd); } + +#[test] +fn test_prepare_output_cmd() { + let enc_prefix = vec_strings![ + "-hide_banner", + "-nostats", + "-v", + "level+error", + "-re", + "-i", + "pipe:0" + ]; + let filter = vec_strings![ + "-filter_complex", + "[0:v]null,zmq=b=tcp\\\\://'127.0.0.1\\:5555',drawtext=text=''" + ]; + let params = vec_strings![ + "-c:v", + "libx264", + "-flags", + "+global_header", + "-f", + "flv", + "rtmp://localhost/live/stream", + "-s", + "512x288", + "-c:v", + "libx264", + "-flags", + "+global_header", + "-f", + "flv", + "rtmp://localhost:1937/live/stream" + ]; + + let mut t1_params = enc_prefix.clone(); + t1_params.append(&mut params.clone()); + let cmd_two_outs = + prepare_output_cmd(enc_prefix.clone(), vec_strings![], params.clone(), "stream"); + + assert_eq!(cmd_two_outs, t1_params); + + let mut test_cmd = enc_prefix.clone(); + let mut test_params = params.clone(); + let mut t2_filter = filter.clone(); + t2_filter[1].push_str(",split=2[v_out1][v_out2]"); + test_cmd.append(&mut t2_filter); + + test_params.insert(0, "-map".to_string()); + test_params.insert(1, "[v_out1]".to_string()); + test_params.insert(2, "-map".to_string()); + test_params.insert(3, "0:a".to_string()); + + test_params.insert(11, "-map".to_string()); + test_params.insert(12, "[v_out2]".to_string()); + test_params.insert(13, "-map".to_string()); + test_params.insert(14, "0:a".to_string()); + + test_cmd.append(&mut test_params); + let cmd_two_outs_with_filter = prepare_output_cmd(enc_prefix, filter, params, "stream"); + + assert_eq!(cmd_two_outs_with_filter, test_cmd); +} + +#[test] +fn video_audio_output() { + let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string())); + config.out.mode = "stream".to_string(); + config.processing.add_logo = false; + config.out.output_cmd = Some(vec_strings![ + "-c:v", + "libx264", + "-c:a", + "aac", + "-ar", + "44100", + "-b:a", + "128k", + "-flags", + "+global_header", + "-f", + "flv", + "rtmp://localhost/live/stream" + ]); + + let mut enc_cmd = vec![]; + let enc_filter = vec![]; + let mut output_cmd = config.out.output_cmd.as_ref().unwrap().clone(); + + let enc_prefix = vec_strings![ + "-hide_banner", + "-nostats", + "-v", + "level+error", + "-re", + "-i", + "pipe:0" + ]; + + enc_cmd.append(&mut output_cmd); + + let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, enc_cmd, &config.out.mode); + + let test_cmd = vec_strings![ + "-hide_banner", + "-nostats", + "-v", + "level+error", + "-re", + "-i", + "pipe:0", + "-c:v", + "libx264", + "-c:a", + "aac", + "-ar", + "44100", + "-b:a", + "128k", + "-flags", + "+global_header", + "-f", + "flv", + "rtmp://localhost/live/stream" + ]; + + assert_eq!(enc_cmd, test_cmd); +} diff --git a/tests/src/lib_utils.rs b/tests/src/lib_utils.rs index b6b3a9fe..daccd51e 100644 --- a/tests/src/lib_utils.rs +++ b/tests/src/lib_utils.rs @@ -50,66 +50,3 @@ fn test_delta() { assert!(delta < 2.0); } - -#[test] -fn test_prepare_output_cmd() { - let enc_prefix = vec_strings![ - "-hide_banner", - "-nostats", - "-v", - "level+error", - "-re", - "-i", - "pipe:0" - ]; - let filter = vec_strings![ - "-filter_complex", - "[0:v]null,zmq=b=tcp\\\\://'127.0.0.1\\:5555',drawtext=text=''" - ]; - let params = vec_strings![ - "-c:v", - "libx264", - "-flags", - "+global_header", - "-f", - "flv", - "rtmp://localhost/live/stream", - "-s", - "512x288", - "-c:v", - "libx264", - "-flags", - "+global_header", - "-f", - "flv", - "rtmp://localhost:1937/live/stream" - ]; - - let mut t1_params = enc_prefix.clone(); - t1_params.append(&mut params.clone()); - let cmd_two_outs = - prepare_output_cmd(enc_prefix.clone(), vec_strings![], params.clone(), "stream"); - - assert_eq!(cmd_two_outs, t1_params); - - let mut test_cmd = enc_prefix.clone(); - let mut test_params = params.clone(); - let mut t2_filter = filter.clone(); - t2_filter[1].push_str(",split=2[v_out1][v_out2]"); - test_cmd.append(&mut t2_filter); - - test_params.insert(0, "-map".to_string()); - test_params.insert(1, "[v_out1]".to_string()); - test_params.insert(2, "-map".to_string()); - test_params.insert(3, "0:a".to_string()); - - test_params.insert(11, "-map".to_string()); - test_params.insert(12, "[v_out2]".to_string()); - test_params.insert(13, "-map".to_string()); - test_params.insert(14, "0:a".to_string()); - - test_cmd.append(&mut test_params); - let cmd_two_outs_with_filter = prepare_output_cmd(enc_prefix, filter, params, "stream"); - - assert_eq!(cmd_two_outs_with_filter, test_cmd); -}