simplify prepare_output_cmd, use filters struct for stream encoder (#209)
This commit is contained in:
parent
5e923d4e06
commit
23aedf3600
28
Cargo.lock
generated
28
Cargo.lock
generated
@ -817,9 +817,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.78"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4"
|
||||
checksum = "3f83d0ebf42c6eafb8d7c52f7e5f2d3003b89c7aa4fd2b79229209459a849af8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@ -829,9 +829,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.78"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199"
|
||||
checksum = "07d050484b55975889284352b0ffc2ecbda25c0c55978017c132b29ba0818a86"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@ -844,15 +844,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.78"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c"
|
||||
checksum = "99d2199b00553eda8012dfec8d3b1c75fce747cf27c169a270b3b99e3448ab78"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.78"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea"
|
||||
checksum = "dcb67a6de1f602736dd7eaead0080cf3435df806c61b24b13328db128c58868f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1070,9 +1070,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "file-rotate"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9135a7f0c84014f3e46849b952f24725ce809ab8ab273ba67089650992ede7"
|
||||
checksum = "f549a61332cfe588f485923f46bfb2c273b0a3634b379cec00acb1d2236a3acb"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"flate2",
|
||||
@ -1503,9 +1503,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa"
|
||||
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
"cxx-build",
|
||||
@ -2264,9 +2264,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.46"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -2,16 +2,15 @@ use std::{
|
||||
io::{BufRead, BufReader, Error, Read},
|
||||
process::{exit, ChildStderr, Command, Stdio},
|
||||
sync::atomic::Ordering,
|
||||
sync::{Arc, Mutex},
|
||||
thread,
|
||||
};
|
||||
|
||||
use crossbeam_channel::Sender;
|
||||
use simplelog::*;
|
||||
|
||||
use ffplayout_lib::filter::filter_chains;
|
||||
use ffplayout_lib::utils::{
|
||||
format_log_line, test_tcp_port, Ingest, Media, PlayoutConfig, ProcessControl,
|
||||
controller::ProcessUnit::*, format_log_line, test_tcp_port, Media, PlayoutConfig,
|
||||
ProcessControl,
|
||||
};
|
||||
use ffplayout_lib::vec_strings;
|
||||
|
||||
@ -85,11 +84,15 @@ pub fn ingest_server(
|
||||
let mut server_cmd = vec_strings!["-hide_banner", "-nostats", "-v", "level+info"];
|
||||
let stream_input = config.ingest.input_cmd.clone().unwrap();
|
||||
let mut dummy_media = Media::new(0, "Live Stream", false);
|
||||
dummy_media.is_live = Some(true);
|
||||
let mut filters = filter_chains(&config, &mut dummy_media, &Arc::new(Mutex::new(vec![])));
|
||||
dummy_media.unit = Ingest;
|
||||
dummy_media.add_filter(&config, &None);
|
||||
|
||||
server_cmd.append(&mut stream_input.clone());
|
||||
server_cmd.append(&mut filters);
|
||||
|
||||
if let Some(mut filter) = dummy_media.filter {
|
||||
server_cmd.append(&mut filter.cmd);
|
||||
}
|
||||
|
||||
server_cmd.append(&mut config.processing.settings.unwrap());
|
||||
|
||||
let mut is_running;
|
||||
|
@ -457,7 +457,7 @@ fn timed_source(
|
||||
pub fn gen_source(
|
||||
config: &PlayoutConfig,
|
||||
mut node: Media,
|
||||
filter_chain: &Arc<Mutex<Vec<String>>>,
|
||||
filter_chain: &Option<Arc<Mutex<Vec<String>>>>,
|
||||
) -> Media {
|
||||
let duration = node.out - node.seek;
|
||||
|
||||
@ -534,7 +534,7 @@ pub fn gen_source(
|
||||
fn handle_list_init(
|
||||
config: &PlayoutConfig,
|
||||
mut node: Media,
|
||||
filter_chain: &Arc<Mutex<Vec<String>>>,
|
||||
filter_chain: &Option<Arc<Mutex<Vec<String>>>>,
|
||||
) -> Media {
|
||||
debug!("Playlist init");
|
||||
let (_, total_delta) = get_delta(config, &node.begin.unwrap());
|
||||
@ -555,7 +555,7 @@ fn handle_list_end(
|
||||
config: &PlayoutConfig,
|
||||
mut node: Media,
|
||||
total_delta: f64,
|
||||
filter_chain: &Arc<Mutex<Vec<String>>>,
|
||||
filter_chain: &Option<Arc<Mutex<Vec<String>>>>,
|
||||
) -> Media {
|
||||
debug!("Playlist end");
|
||||
|
||||
|
@ -1,7 +1,4 @@
|
||||
use std::{
|
||||
process::{self, Command, Stdio},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::process::{self, Command, Stdio};
|
||||
|
||||
use simplelog::*;
|
||||
|
||||
@ -25,9 +22,7 @@ pub fn output(config: &PlayoutConfig, log_format: &str) -> process::Child {
|
||||
);
|
||||
|
||||
let mut filter: String = "null,".to_string();
|
||||
filter.push_str(
|
||||
v_drawtext::filter_node(config, None, &Arc::new(Mutex::new(vec![]))).as_str(),
|
||||
);
|
||||
filter.push_str(v_drawtext::filter_node(config, None, &None).as_str());
|
||||
enc_filter = vec!["-vf".to_string(), filter];
|
||||
}
|
||||
}
|
||||
|
@ -29,12 +29,13 @@ 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::{
|
||||
sec_to_time, stderr_reader, test_tcp_port, Encoder, Ingest, Media, PlayerControl,
|
||||
PlayoutConfig, PlayoutStatus, ProcessControl,
|
||||
use ffplayout_lib::{
|
||||
utils::{
|
||||
controller::ProcessUnit::*, sec_to_time, stderr_reader, test_tcp_port, Media,
|
||||
PlayerControl, PlayoutConfig, PlayoutStatus, ProcessControl,
|
||||
},
|
||||
vec_strings,
|
||||
};
|
||||
use ffplayout_lib::vec_strings;
|
||||
|
||||
/// Ingest Server for HLS
|
||||
fn ingest_to_hls_server(
|
||||
@ -49,7 +50,7 @@ fn ingest_to_hls_server(
|
||||
let stream_input = config.ingest.input_cmd.clone().unwrap();
|
||||
server_prefix.append(&mut stream_input.clone());
|
||||
let mut dummy_media = Media::new(0, "Live Stream", false);
|
||||
dummy_media.is_live = Some(true);
|
||||
dummy_media.unit = Ingest;
|
||||
|
||||
let mut is_running;
|
||||
|
||||
@ -63,8 +64,8 @@ fn ingest_to_hls_server(
|
||||
};
|
||||
|
||||
loop {
|
||||
let filters = filter_chains(&config, &mut dummy_media, &playout_stat.chain);
|
||||
let server_cmd = prepare_output_cmd(server_prefix.clone(), filters, &config);
|
||||
dummy_media.add_filter(&config, &playout_stat.chain);
|
||||
let server_cmd = prepare_output_cmd(server_prefix.clone(), &dummy_media.filter, &config);
|
||||
|
||||
debug!(
|
||||
"Server CMD: <bright-blue>\"ffmpeg {}\"</>",
|
||||
@ -179,8 +180,7 @@ pub fn write_hls(
|
||||
|
||||
let mut enc_prefix = vec_strings!["-hide_banner", "-nostats", "-v", &ff_log_format];
|
||||
enc_prefix.append(&mut cmd);
|
||||
let enc_filter = node.filter.unwrap();
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, config);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, &node.filter, config);
|
||||
|
||||
debug!(
|
||||
"HLS writer CMD: <bright-blue>\"ffmpeg {}\"</>",
|
||||
|
@ -99,12 +99,11 @@ pub fn player(
|
||||
node.audio
|
||||
);
|
||||
|
||||
let mut filter = node.filter.unwrap();
|
||||
let mut dec_cmd = vec_strings!["-hide_banner", "-nostats", "-v", &ff_log_format];
|
||||
dec_cmd.append(&mut cmd);
|
||||
|
||||
if filter.len() > 1 {
|
||||
dec_cmd.append(&mut filter);
|
||||
if let Some(mut filter) = node.filter {
|
||||
dec_cmd.append(&mut filter.cmd);
|
||||
}
|
||||
|
||||
dec_cmd.append(&mut config.processing.clone().settings.unwrap());
|
||||
|
@ -1,47 +1,32 @@
|
||||
use std::{
|
||||
process::{self, Command, Stdio},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::process::{self, Command, Stdio};
|
||||
|
||||
use simplelog::*;
|
||||
|
||||
use ffplayout_lib::{filter::v_drawtext, utils::PlayoutConfig, vec_strings};
|
||||
use crate::utils::prepare_output_cmd;
|
||||
use ffplayout_lib::{
|
||||
utils::{Media, PlayoutConfig, ProcessUnit::*},
|
||||
vec_strings,
|
||||
};
|
||||
|
||||
/// Desktop Output
|
||||
///
|
||||
/// Instead of streaming, we run a ffplay instance and play on desktop.
|
||||
pub fn output(config: &PlayoutConfig, log_format: &str) -> process::Child {
|
||||
let mut enc_filter: Vec<String> = vec![];
|
||||
let mut media = Media::new(0, "", false);
|
||||
media.unit = Encoder;
|
||||
media.add_filter(config, &None);
|
||||
|
||||
let mut enc_cmd = vec_strings![
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
log_format,
|
||||
"-re",
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-f",
|
||||
"null",
|
||||
"-"
|
||||
"pipe:0"
|
||||
];
|
||||
|
||||
if config.text.add_text && !config.text.text_from_filename {
|
||||
if let Some(socket) = config.text.zmq_stream_socket.clone() {
|
||||
debug!(
|
||||
"Using drawtext filter, listening on address: <yellow>{}</>",
|
||||
socket
|
||||
);
|
||||
|
||||
let mut filter: String = "null,".to_string();
|
||||
filter.push_str(
|
||||
v_drawtext::filter_node(config, None, &Arc::new(Mutex::new(vec![]))).as_str(),
|
||||
);
|
||||
enc_filter = vec!["-vf".to_string(), filter];
|
||||
}
|
||||
}
|
||||
|
||||
enc_cmd.splice(7..7, enc_filter);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, &media.filter, config);
|
||||
|
||||
debug!(
|
||||
"Encoder CMD: <bright-blue>\"ffmpeg {}\"</>",
|
||||
|
@ -1,22 +1,20 @@
|
||||
use std::{
|
||||
process::{self, Command, Stdio},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::process::{self, Command, Stdio};
|
||||
|
||||
use simplelog::*;
|
||||
|
||||
use crate::utils::prepare_output_cmd;
|
||||
use ffplayout_lib::filter::v_drawtext;
|
||||
use ffplayout_lib::utils::PlayoutConfig;
|
||||
use ffplayout_lib::vec_strings;
|
||||
use ffplayout_lib::{
|
||||
utils::{Media, PlayoutConfig, ProcessUnit::*},
|
||||
vec_strings,
|
||||
};
|
||||
|
||||
/// Streaming Output
|
||||
///
|
||||
/// Prepare the ffmpeg command for streaming output
|
||||
pub fn output(config: &PlayoutConfig, log_format: &str) -> process::Child {
|
||||
let mut enc_cmd = vec![];
|
||||
let mut enc_filter = vec![];
|
||||
let mut output_cmd = config.out.output_cmd.as_ref().unwrap().clone();
|
||||
let mut media = Media::new(0, "", false);
|
||||
media.unit = Encoder;
|
||||
media.add_filter(config, &None);
|
||||
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -28,26 +26,7 @@ pub fn output(config: &PlayoutConfig, log_format: &str) -> process::Child {
|
||||
"pipe:0"
|
||||
];
|
||||
|
||||
if config.text.add_text && !config.text.text_from_filename {
|
||||
if let Some(socket) = config.text.zmq_stream_socket.clone() {
|
||||
debug!(
|
||||
"Using drawtext filter, listening on address: <yellow>{}</>",
|
||||
socket
|
||||
);
|
||||
|
||||
let mut filter = "[0:v]null,".to_string();
|
||||
|
||||
filter.push_str(
|
||||
v_drawtext::filter_node(config, None, &Arc::new(Mutex::new(vec![]))).as_str(),
|
||||
);
|
||||
|
||||
enc_filter = vec!["-filter_complex".to_string(), filter];
|
||||
}
|
||||
}
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, config);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, &media.filter, config);
|
||||
|
||||
debug!(
|
||||
"Encoder CMD: <bright-blue>\"ffmpeg {}\"</>",
|
||||
|
@ -87,8 +87,9 @@ pub fn json_rpc_server(
|
||||
|
||||
// TODO: in Rust 1.65 use let_chains instead
|
||||
if !filter.is_empty() && config.text.zmq_stream_socket.is_some() {
|
||||
let mut clips_filter = playout_stat.chain.lock().unwrap();
|
||||
*clips_filter = vec![filter.clone()];
|
||||
if let Some(clips_filter) = playout_stat.chain.clone() {
|
||||
*clips_filter.lock().unwrap() = vec![filter.clone()];
|
||||
}
|
||||
|
||||
if config.out.mode == HLS {
|
||||
if proc.server_is_running.load(Ordering::SeqCst) {
|
||||
|
@ -9,8 +9,8 @@ pub mod arg_parse;
|
||||
|
||||
pub use arg_parse::Args;
|
||||
use ffplayout_lib::{
|
||||
filter::Filters,
|
||||
utils::{time_to_sec, OutputMode::*, PlayoutConfig, ProcessMode::*},
|
||||
vec_strings,
|
||||
};
|
||||
|
||||
/// Read command line arguments, and override the config with them.
|
||||
@ -92,39 +92,32 @@ pub fn get_config(args: Args) -> PlayoutConfig {
|
||||
/// seek for multiple outputs and add mapping for it
|
||||
pub fn prepare_output_cmd(
|
||||
mut cmd: Vec<String>,
|
||||
mut filter: Vec<String>,
|
||||
filters: &Option<Filters>,
|
||||
config: &PlayoutConfig,
|
||||
) -> Vec<String> {
|
||||
let mut output_params = config.out.clone().output_cmd.unwrap();
|
||||
let mut new_params = vec![];
|
||||
let params_len = output_params.len();
|
||||
let mut output_a_map = "[a_out1]".to_string();
|
||||
let mut output_v_map = "[v_out1]".to_string();
|
||||
let mut out_count = 1;
|
||||
let mut new_params = vec![];
|
||||
let mut output_filter = String::new();
|
||||
let mut next_is_filter = false;
|
||||
let re_audio_map = Regex::new(r"\[0:a:(?P<num>[0-9]+)\]").unwrap();
|
||||
let re_map = Regex::new(r"(\[?[0-9]:[av](:[0-9]+)?\]?|-map|\[[a-z_0-9]+\])").unwrap();
|
||||
|
||||
if let Some(mut filter) = filters.clone() {
|
||||
// Loop over output parameters
|
||||
//
|
||||
// Check if it contains a filtergraph, count its outputs and set correct mapping values.
|
||||
for (i, p) in output_params.iter().enumerate() {
|
||||
let mut param = p.clone();
|
||||
|
||||
param = param.replace("[0:v]", "[vout0]");
|
||||
param = param.replace("[0:a]", "[aout0]");
|
||||
param = re_audio_map.replace_all(¶m, "[aout$num]").to_string();
|
||||
|
||||
for (i, param) in output_params.iter().enumerate() {
|
||||
// Skip filter command, to concat existing filters with new ones.
|
||||
if param != "-filter_complex" {
|
||||
if next_is_filter {
|
||||
if i > 0 && output_params[i - 1] == "-filter_complex" {
|
||||
output_filter = param.clone();
|
||||
next_is_filter = false;
|
||||
} else {
|
||||
} else if !output_filter.contains("split") {
|
||||
if !re_map.is_match(param) {
|
||||
// Skip mapping parameters, when no split filter is set
|
||||
new_params.push(param.clone());
|
||||
}
|
||||
} else {
|
||||
next_is_filter = true;
|
||||
new_params.push(param.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Check if parameter is a output
|
||||
@ -133,79 +126,33 @@ pub fn prepare_output_cmd(
|
||||
&& !output_params[i - 1].starts_with('-')
|
||||
&& i < params_len - 1
|
||||
{
|
||||
out_count += 1;
|
||||
let mut a_map = "0:a".to_string();
|
||||
let v_map = format!("[v_out{out_count}]");
|
||||
output_v_map.push_str(&v_map);
|
||||
|
||||
if config.out.mode == HLS {
|
||||
a_map = format!("[a_out{out_count}]");
|
||||
}
|
||||
|
||||
output_a_map.push_str(&a_map);
|
||||
|
||||
if !output_params.contains(&"-map".to_string()) {
|
||||
let mut map = vec_strings!["-map", v_map, "-map", a_map];
|
||||
new_params.append(&mut map);
|
||||
// add mapping to following outputs
|
||||
new_params.append(&mut filter.output_map.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !filter.is_empty() {
|
||||
if filter.cmd.contains(&"-filter_complex".to_string()) {
|
||||
output_params = new_params;
|
||||
|
||||
// Process A/V mapping
|
||||
//
|
||||
// Check if there is multiple outputs, and/or multiple audio tracks
|
||||
// and add the correct mapping for it.
|
||||
if out_count > 1 && config.processing.audio_tracks == 1 && config.out.mode == HLS {
|
||||
filter[1].push_str(&format!(";[vout0]split={out_count}{output_v_map}"));
|
||||
filter[1].push_str(&format!(";[aout0]asplit={out_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_filter.is_empty() && config.out.mode == HLS {
|
||||
filter[1].push_str(&format!(";{output_filter}"));
|
||||
filter.drain(2..);
|
||||
cmd.append(&mut filter);
|
||||
} else if out_count == 1
|
||||
&& config.processing.audio_tracks == 1
|
||||
&& config.out.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 out_count > 1 && config.processing.audio_tracks == 1 && config.out.mode == Stream
|
||||
{
|
||||
filter[1].push_str(&format!(",split={out_count}{output_v_map}"));
|
||||
cmd.append(&mut filter);
|
||||
cmd.append(&mut vec_strings!["-map", "[v_out1]", "-map", "0:a"]);
|
||||
} else if config.processing.audio_tracks > 1 && config.out.mode == Stream {
|
||||
filter[1].push_str("[v_out1]");
|
||||
cmd.append(&mut filter);
|
||||
// when filter in output_param are found and we are in HLS mode, fix the mapping
|
||||
if !output_filter.is_empty() && config.out.mode == HLS {
|
||||
let re = Regex::new(r"0:a:(?P<num>[0-9]+)").unwrap();
|
||||
output_filter = re
|
||||
.replace_all(&output_filter, "aout${num}")
|
||||
.to_string()
|
||||
.replace("[0:a]", &filter.audio_map[0])
|
||||
.replace("[0:v]", &filter.video_map[0])
|
||||
.replace("[0:v:0]", &filter.video_map[0]);
|
||||
|
||||
output_params = output_params
|
||||
.iter()
|
||||
.map(|p| p.replace("0:v", "[v_out1]"))
|
||||
.collect();
|
||||
|
||||
if out_count == 1 {
|
||||
cmd.append(&mut vec_strings!["-map", "[v_out1]"]);
|
||||
|
||||
for i in 0..config.processing.audio_tracks {
|
||||
cmd.append(&mut vec_strings!["-map", format!("0:a:{i}")]);
|
||||
}
|
||||
filter.cmd[1].push_str(&format!(";{output_filter}"));
|
||||
filter.cmd.drain(2..);
|
||||
cmd.append(&mut filter.cmd);
|
||||
} else {
|
||||
cmd.append(&mut filter.cmd);
|
||||
}
|
||||
} else {
|
||||
cmd.append(&mut filter);
|
||||
}
|
||||
} else if out_count == 1 && config.processing.audio_tracks > 1 && config.out.mode == Stream {
|
||||
cmd.append(&mut vec_strings!["-map", "0:v"]);
|
||||
|
||||
for i in 0..config.processing.audio_tracks {
|
||||
cmd.append(&mut vec_strings!["-map", format!("0:a:{i}")]);
|
||||
cmd.append(&mut filter.cmd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,11 +13,12 @@ pub mod v_drawtext;
|
||||
// get_delta
|
||||
use self::custom_filter::custom_filter;
|
||||
use crate::utils::{
|
||||
fps_calc, get_delta, is_close, Media, MediaProbe, OutputMode::*, PlayoutConfig,
|
||||
controller::ProcessUnit::*, fps_calc, get_delta, is_close, Media, MediaProbe, OutputMode::*,
|
||||
PlayoutConfig,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Copy, PartialEq)]
|
||||
enum FilterType {
|
||||
#[derive(Clone, Debug, Copy, Eq, PartialEq)]
|
||||
pub enum FilterType {
|
||||
Audio,
|
||||
Video,
|
||||
}
|
||||
@ -34,22 +35,23 @@ impl fmt::Display for FilterType {
|
||||
use FilterType::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Filters {
|
||||
pub struct Filters {
|
||||
audio_chain: String,
|
||||
video_chain: String,
|
||||
final_chain: String,
|
||||
audio_map: Vec<String>,
|
||||
video_map: Vec<String>,
|
||||
output_map: Vec<String>,
|
||||
pub final_chain: String,
|
||||
pub audio_map: Vec<String>,
|
||||
pub video_map: Vec<String>,
|
||||
pub output_map: Vec<String>,
|
||||
audio_track_count: i32,
|
||||
audio_position: i32,
|
||||
video_position: i32,
|
||||
audio_last: i32,
|
||||
video_last: i32,
|
||||
cmd: Vec<String>,
|
||||
pub cmd: Vec<String>,
|
||||
}
|
||||
|
||||
impl Filters {
|
||||
fn new(position: i32) -> Self {
|
||||
pub fn new(audio_track_count: i32, audio_position: i32) -> Self {
|
||||
Self {
|
||||
audio_chain: String::new(),
|
||||
video_chain: String::new(),
|
||||
@ -57,15 +59,16 @@ impl Filters {
|
||||
audio_map: vec![],
|
||||
video_map: vec![],
|
||||
output_map: vec![],
|
||||
audio_position: position,
|
||||
video_position: position,
|
||||
audio_track_count,
|
||||
audio_position,
|
||||
video_position: 0,
|
||||
audio_last: -1,
|
||||
video_last: -1,
|
||||
cmd: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn add_filter(&mut self, filter: &str, track_nr: i32, filter_type: FilterType) {
|
||||
pub fn add_filter(&mut self, filter: &str, track_nr: i32, filter_type: FilterType) {
|
||||
let (map, chain, position, last) = match filter_type {
|
||||
Audio => (
|
||||
&mut self.audio_map,
|
||||
@ -114,20 +117,47 @@ impl Filters {
|
||||
|
||||
fn close_chains(&mut self) {
|
||||
// add final output selector
|
||||
self.audio_chain
|
||||
.push_str(&format!("[aout{}]", self.audio_last));
|
||||
if self.video_last >= 0 {
|
||||
self.video_chain
|
||||
.push_str(&format!("[vout{}]", self.video_last));
|
||||
} else {
|
||||
self.output_map
|
||||
.append(&mut vec!["-map".to_string(), "0:v".to_string()]);
|
||||
}
|
||||
|
||||
if self.audio_last >= 0 {
|
||||
self.audio_chain
|
||||
.push_str(&format!("[aout{}]", self.audio_last));
|
||||
} else {
|
||||
for i in 0..self.audio_track_count {
|
||||
self.output_map.append(&mut vec![
|
||||
"-map".to_string(),
|
||||
format!("{}:a:{i}", self.audio_position),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_final_chain(&mut self) {
|
||||
self.final_chain.push_str(&self.video_chain);
|
||||
|
||||
if !self.audio_chain.is_empty() {
|
||||
self.final_chain.push(';');
|
||||
self.final_chain.push_str(&self.audio_chain);
|
||||
}
|
||||
|
||||
if !self.final_chain.is_empty() {
|
||||
self.cmd.push("-filter_complex".to_string());
|
||||
self.cmd.push(self.final_chain.clone());
|
||||
self.cmd.append(&mut self.output_map);
|
||||
}
|
||||
|
||||
self.cmd.append(&mut self.output_map.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Filters {
|
||||
fn default() -> Self {
|
||||
Self::new(1, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,7 +250,7 @@ fn fade(node: &mut Media, chain: &mut Filters, nr: i32, filter_type: FilterType)
|
||||
t = "a"
|
||||
}
|
||||
|
||||
if node.seek > 0.0 || node.is_live == Some(true) {
|
||||
if node.seek > 0.0 || node.unit == Ingest {
|
||||
chain.add_filter(&format!("{t}fade=in:st=0:d=0.5"), nr, filter_type)
|
||||
}
|
||||
|
||||
@ -283,9 +313,11 @@ fn add_text(
|
||||
node: &mut Media,
|
||||
chain: &mut Filters,
|
||||
config: &PlayoutConfig,
|
||||
filter_chain: &Arc<Mutex<Vec<String>>>,
|
||||
filter_chain: &Option<Arc<Mutex<Vec<String>>>>,
|
||||
) {
|
||||
if config.text.add_text && (config.text.text_from_filename || config.out.mode == HLS) {
|
||||
if config.text.add_text
|
||||
&& (config.text.text_from_filename || config.out.mode == HLS || node.unit == Encoder)
|
||||
{
|
||||
let filter = v_drawtext::filter_node(config, Some(node), filter_chain);
|
||||
|
||||
chain.add_filter(&filter, 0, Video);
|
||||
@ -325,8 +357,7 @@ fn extend_audio(node: &mut Media, chain: &mut Filters, nr: i32) {
|
||||
|
||||
/// Add single pass loudnorm filter to audio line.
|
||||
fn add_loudnorm(node: &Media, chain: &mut Filters, config: &PlayoutConfig, nr: i32) {
|
||||
if config.processing.add_loudnorm
|
||||
|| (node.is_live.unwrap_or_default() && config.processing.loudnorm_ingest)
|
||||
if config.processing.add_loudnorm || (node.unit == Ingest && config.processing.loudnorm_ingest)
|
||||
{
|
||||
let loud_filter = a_loudnorm::filter_node(config);
|
||||
chain.add_filter(&loud_filter, nr, Audio);
|
||||
@ -383,9 +414,18 @@ fn custom(filter: &str, chain: &mut Filters, nr: i32, filter_type: FilterType) {
|
||||
pub fn filter_chains(
|
||||
config: &PlayoutConfig,
|
||||
node: &mut Media,
|
||||
filter_chain: &Arc<Mutex<Vec<String>>>,
|
||||
) -> Vec<String> {
|
||||
let mut filters = Filters::new(0);
|
||||
filter_chain: &Option<Arc<Mutex<Vec<String>>>>,
|
||||
) -> Filters {
|
||||
let mut filters = Filters::new(config.processing.audio_tracks, 0);
|
||||
|
||||
if node.unit == Encoder {
|
||||
add_text(node, &mut filters, config, filter_chain);
|
||||
|
||||
filters.close_chains();
|
||||
filters.build_final_chain();
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
if let Some(probe) = node.probe.as_ref() {
|
||||
if Path::new(&node.audio).is_file() {
|
||||
@ -434,7 +474,7 @@ pub fn filter_chains(
|
||||
|| Path::new(&node.audio).is_file()
|
||||
{
|
||||
extend_audio(node, &mut filters, i);
|
||||
} else if !node.is_live.unwrap_or(false) {
|
||||
} else if node.unit == Decoder {
|
||||
warn!(
|
||||
"Missing audio track (id {i}) from <b><magenta>{}</></b>",
|
||||
node.source
|
||||
@ -456,5 +496,5 @@ pub fn filter_chains(
|
||||
filters.close_chains();
|
||||
filters.build_final_chain();
|
||||
|
||||
filters.cmd
|
||||
filters
|
||||
}
|
||||
|
@ -5,25 +5,23 @@ use std::{
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
use crate::utils::{Media, PlayoutConfig};
|
||||
use crate::utils::{controller::ProcessUnit::*, Media, PlayoutConfig};
|
||||
|
||||
pub fn filter_node(
|
||||
config: &PlayoutConfig,
|
||||
node: Option<&Media>,
|
||||
filter_chain: &Arc<Mutex<Vec<String>>>,
|
||||
filter_chain: &Option<Arc<Mutex<Vec<String>>>>,
|
||||
) -> String {
|
||||
let mut filter = String::new();
|
||||
let mut font = String::new();
|
||||
|
||||
if config.text.add_text {
|
||||
if Path::new(&config.text.fontfile).is_file() {
|
||||
font = format!(":fontfile='{}'", config.text.fontfile)
|
||||
}
|
||||
|
||||
let zmq_socket = match node.and_then(|n| n.is_live) {
|
||||
Some(true) => config.text.zmq_server_socket.clone(),
|
||||
Some(false) => config.text.zmq_stream_socket.clone(),
|
||||
None => config.text.zmq_stream_socket.clone(),
|
||||
let zmq_socket = match node.map(|n| n.unit) {
|
||||
Some(Ingest) => config.text.zmq_server_socket.clone(),
|
||||
_ => config.text.zmq_stream_socket.clone(),
|
||||
};
|
||||
|
||||
// TODO: in Rust 1.65 use let_chains instead
|
||||
@ -42,11 +40,10 @@ pub fn filter_node(
|
||||
.replace(':', "\\:");
|
||||
filter = format!("drawtext=text='{escape}':{}{font}", config.text.style)
|
||||
} else if let Some(socket) = zmq_socket {
|
||||
let chain = filter_chain.lock().unwrap();
|
||||
let mut filter_cmd = format!("text=''{font}");
|
||||
|
||||
if !chain.is_empty() {
|
||||
if let Some(link) = chain.iter().find(|&l| l.contains("text")) {
|
||||
if let Some(chain) = filter_chain {
|
||||
if let Some(link) = chain.lock().unwrap().iter().find(|&l| l.contains("text")) {
|
||||
filter_cmd = link.to_string();
|
||||
}
|
||||
}
|
||||
@ -56,7 +53,6 @@ pub fn filter_node(
|
||||
socket.replace(':', "\\:")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
filter
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ use std::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use shlex::split;
|
||||
|
||||
use crate::utils::{free_tcp_socket, home_dir, time_to_sec};
|
||||
use crate::vec_strings;
|
||||
use super::vec_strings;
|
||||
use crate::utils::{free_tcp_socket, home_dir, time_to_sec, OutputMode::*};
|
||||
|
||||
pub const DUMMY_LEN: f64 = 60.0;
|
||||
pub const IMAGE_FORMAT: [&str; 21] = [
|
||||
@ -43,7 +43,7 @@ impl FromStr for OutputMode {
|
||||
"hls" => Ok(Self::HLS),
|
||||
"null" => Ok(Self::Null),
|
||||
"stream" => Ok(Self::Stream),
|
||||
_ => Err(String::new()),
|
||||
_ => Err("Use 'desktop', 'hls', 'null' or 'stream'".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,7 +71,7 @@ impl FromStr for ProcessMode {
|
||||
match input {
|
||||
"folder" => Ok(Self::Folder),
|
||||
"playlist" => Ok(Self::Playlist),
|
||||
_ => Err(String::new()),
|
||||
_ => Err("Use 'folder' or 'playlist'".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -267,14 +267,6 @@ impl PlayoutConfig {
|
||||
.join(".ffp_status")
|
||||
.display()
|
||||
.to_string();
|
||||
let bitrate = format!(
|
||||
"{}k",
|
||||
config.processing.width * config.processing.height / 10
|
||||
);
|
||||
let buf_size = format!(
|
||||
"{}k",
|
||||
(config.processing.width * config.processing.height / 10) / 2
|
||||
);
|
||||
|
||||
config.playlist.start_sec = Some(time_to_sec(&config.playlist.day_start));
|
||||
|
||||
@ -297,19 +289,13 @@ impl PlayoutConfig {
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
"-r",
|
||||
&config.processing.fps.to_string(),
|
||||
&config.processing.fps,
|
||||
"-c:v",
|
||||
"mpeg2video",
|
||||
"-g",
|
||||
"1",
|
||||
"-b:v",
|
||||
&bitrate,
|
||||
"-minrate",
|
||||
&bitrate,
|
||||
"-maxrate",
|
||||
&bitrate,
|
||||
"-bufsize",
|
||||
&buf_size
|
||||
"-qscale:v",
|
||||
"2"
|
||||
];
|
||||
|
||||
settings.append(&mut pre_audio_codec(config.processing.add_loudnorm));
|
||||
@ -320,7 +306,12 @@ impl PlayoutConfig {
|
||||
config.processing.settings = Some(settings);
|
||||
|
||||
config.ingest.input_cmd = split(config.ingest.input_param.as_str());
|
||||
|
||||
if config.out.mode == Null {
|
||||
config.out.output_cmd = Some(vec_strings!["-f", "null", "-"]);
|
||||
} else {
|
||||
config.out.output_cmd = split(config.out.output_param.as_str());
|
||||
}
|
||||
|
||||
// when text overlay without text_from_filename is on, turn also the RPC server on,
|
||||
// to get text messages from it
|
||||
|
@ -8,11 +8,13 @@ use std::{
|
||||
};
|
||||
|
||||
use jsonrpc_http_server::CloseHandle;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use simplelog::*;
|
||||
|
||||
use crate::utils::Media;
|
||||
|
||||
/// Defined process units.
|
||||
#[derive(Clone, Debug, Copy, Eq, Serialize, Deserialize, PartialEq)]
|
||||
pub enum ProcessUnit {
|
||||
Decoder,
|
||||
Encoder,
|
||||
@ -29,6 +31,12 @@ impl fmt::Display for ProcessUnit {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ProcessUnit {
|
||||
fn default() -> Self {
|
||||
ProcessUnit::Decoder
|
||||
}
|
||||
}
|
||||
|
||||
use ProcessUnit::*;
|
||||
|
||||
/// Process Controller
|
||||
@ -182,7 +190,7 @@ impl Default for PlayerControl {
|
||||
/// Global playout control, for move forward/backward clip, or resetting playlist/state.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PlayoutStatus {
|
||||
pub chain: Arc<Mutex<Vec<String>>>,
|
||||
pub chain: Option<Arc<Mutex<Vec<String>>>>,
|
||||
pub current_date: Arc<Mutex<String>>,
|
||||
pub date: Arc<Mutex<String>>,
|
||||
pub list_init: Arc<AtomicBool>,
|
||||
@ -192,7 +200,7 @@ pub struct PlayoutStatus {
|
||||
impl PlayoutStatus {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
chain: Arc::new(Mutex::new(vec![])),
|
||||
chain: None,
|
||||
current_date: Arc::new(Mutex::new(String::new())),
|
||||
date: Arc::new(Mutex::new(String::new())),
|
||||
list_init: Arc::new(AtomicBool::new(true)),
|
||||
|
@ -19,7 +19,7 @@ use crate::utils::{get_sec, include_file, Media, PlayoutConfig};
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FolderSource {
|
||||
config: PlayoutConfig,
|
||||
filter_chain: Arc<Mutex<Vec<String>>>,
|
||||
filter_chain: Option<Arc<Mutex<Vec<String>>>>,
|
||||
pub nodes: Arc<Mutex<Vec<Media>>>,
|
||||
current_node: Media,
|
||||
index: Arc<AtomicUsize>,
|
||||
@ -28,7 +28,7 @@ pub struct FolderSource {
|
||||
impl FolderSource {
|
||||
pub fn new(
|
||||
config: &PlayoutConfig,
|
||||
filter_chain: Arc<Mutex<Vec<String>>>,
|
||||
filter_chain: Option<Arc<Mutex<Vec<String>>>>,
|
||||
current_list: Arc<Mutex<Vec<Media>>>,
|
||||
global_index: Arc<AtomicUsize>,
|
||||
) -> Self {
|
||||
|
@ -93,7 +93,7 @@ pub fn generate_playlist(
|
||||
date_range = get_date_range(&date_range)
|
||||
}
|
||||
|
||||
let media_list = FolderSource::new(config, Arc::new(Mutex::new(vec![])), current_list, index);
|
||||
let media_list = FolderSource::new(config, None, current_list, index);
|
||||
let list_length = media_list.nodes.lock().unwrap().len();
|
||||
|
||||
for date in date_range {
|
||||
|
@ -9,8 +9,8 @@ use std::{
|
||||
use simplelog::*;
|
||||
|
||||
use crate::utils::{
|
||||
get_date, is_remote, modified_time, time_from_header, validate_playlist, Media, PlayoutConfig,
|
||||
DUMMY_LEN,
|
||||
controller::ProcessUnit::*, get_date, is_remote, modified_time, time_from_header,
|
||||
validate_playlist, Media, PlayoutConfig, DUMMY_LEN,
|
||||
};
|
||||
|
||||
/// This is our main playlist object, it holds all necessary information for the current day.
|
||||
@ -76,7 +76,7 @@ fn set_defaults(
|
||||
item.last_ad = Some(false);
|
||||
item.next_ad = Some(false);
|
||||
item.process = Some(true);
|
||||
item.filter = Some(vec![]);
|
||||
item.filter = None;
|
||||
|
||||
start_sec += item.out - item.seek;
|
||||
}
|
||||
@ -112,10 +112,10 @@ fn loop_playlist(
|
||||
cmd: item.cmd.clone(),
|
||||
probe: item.probe.clone(),
|
||||
process: Some(true),
|
||||
is_live: Some(false),
|
||||
unit: Decoder,
|
||||
last_ad: Some(false),
|
||||
next_ad: Some(false),
|
||||
filter: Some(vec![]),
|
||||
filter: None,
|
||||
custom_filter: String::new(),
|
||||
};
|
||||
|
||||
|
@ -3,7 +3,7 @@ use std::{
|
||||
process::{Command, Stdio},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Mutex,
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
@ -53,16 +53,16 @@ fn check_media(
|
||||
node.cmd = Some(seek_and_length(&node));
|
||||
}
|
||||
|
||||
node.add_filter(config, &Arc::new(Mutex::new(vec![])));
|
||||
node.add_filter(config, &None);
|
||||
|
||||
let mut filter = node.filter.unwrap_or_default();
|
||||
|
||||
if filter.len() > 1 {
|
||||
filter[1] = filter[1].replace("realtime=speed=1", "null")
|
||||
if filter.cmd.len() > 1 {
|
||||
filter.cmd[1] = filter.cmd[1].replace("realtime=speed=1", "null")
|
||||
}
|
||||
|
||||
enc_cmd.append(&mut node.cmd.unwrap_or_default());
|
||||
enc_cmd.append(&mut filter);
|
||||
enc_cmd.append(&mut filter.cmd);
|
||||
enc_cmd.append(&mut vec_strings!["-t", "0.1", "-f", "null", "-"]);
|
||||
|
||||
let mut enc_proc = match Command::new("ffmpeg")
|
||||
|
@ -41,13 +41,19 @@ pub use config::{
|
||||
ProcessMode::{self, *},
|
||||
DUMMY_LEN, FFMPEG_IGNORE_ERRORS, IMAGE_FORMAT,
|
||||
};
|
||||
pub use controller::{PlayerControl, PlayoutStatus, ProcessControl, ProcessUnit::*};
|
||||
pub use controller::{
|
||||
PlayerControl, PlayoutStatus, ProcessControl,
|
||||
ProcessUnit::{self, *},
|
||||
};
|
||||
pub use generator::generate_playlist;
|
||||
pub use json_serializer::{read_json, JsonPlaylist};
|
||||
pub use json_validate::validate_playlist;
|
||||
pub use logging::{init_logging, send_mail};
|
||||
|
||||
use crate::{filter::filter_chains, vec_strings};
|
||||
use crate::{
|
||||
filter::{filter_chains, Filters},
|
||||
vec_strings,
|
||||
};
|
||||
|
||||
/// Video clip struct to hold some important states and comments for current media.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
@ -82,7 +88,7 @@ pub struct Media {
|
||||
pub cmd: Option<Vec<String>>,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub filter: Option<Vec<String>>,
|
||||
pub filter: Option<Filters>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "is_empty_string")]
|
||||
pub custom_filter: String,
|
||||
@ -99,8 +105,8 @@ pub struct Media {
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub process: Option<bool>,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub is_live: Option<bool>,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub unit: ProcessUnit,
|
||||
}
|
||||
|
||||
impl Media {
|
||||
@ -130,13 +136,13 @@ impl Media {
|
||||
source: src.to_string(),
|
||||
audio: String::new(),
|
||||
cmd: Some(vec_strings!["-i", src]),
|
||||
filter: Some(vec![]),
|
||||
filter: None,
|
||||
custom_filter: String::new(),
|
||||
probe,
|
||||
last_ad: Some(false),
|
||||
next_ad: Some(false),
|
||||
process: Some(true),
|
||||
is_live: Some(false),
|
||||
unit: Decoder,
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,7 +166,11 @@ impl Media {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_filter(&mut self, config: &PlayoutConfig, filter_chain: &Arc<Mutex<Vec<String>>>) {
|
||||
pub fn add_filter(
|
||||
&mut self,
|
||||
config: &PlayoutConfig,
|
||||
filter_chain: &Option<Arc<Mutex<Vec<String>>>>,
|
||||
) {
|
||||
let mut node = self.clone();
|
||||
self.filter = Some(filter_chains(config, &mut node, filter_chain))
|
||||
}
|
||||
|
0
tests/assets/playlist_full.json
Executable file → Normal file
0
tests/assets/playlist_full.json
Executable file → Normal file
@ -1,12 +1,8 @@
|
||||
use std::{
|
||||
fs,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::fs;
|
||||
|
||||
use ffplayout::{input::playlist::gen_source, utils::prepare_output_cmd};
|
||||
use ffplayout_lib::{
|
||||
filter::v_drawtext,
|
||||
utils::{Media, OutputMode::*, PlayoutConfig},
|
||||
utils::{Media, OutputMode::*, PlayoutConfig, ProcessUnit::*},
|
||||
vec_strings,
|
||||
};
|
||||
|
||||
@ -19,9 +15,9 @@ fn video_audio_input() {
|
||||
config.processing.logo = logo_path.to_string_lossy().to_string();
|
||||
|
||||
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
|
||||
let media = gen_source(&config, media_obj, &Arc::new(Mutex::new(vec![])));
|
||||
let media = gen_source(&config, media_obj, &None);
|
||||
|
||||
let test_filter_cmd = Some(
|
||||
let test_filter_cmd =
|
||||
vec_strings![
|
||||
"-filter_complex",
|
||||
format!("[0:v:0]scale=1024:576,null[v];movie={}:loop=0,setpts=N/(FRAME_RATE*TB),format=rgba,colorchannelmixer=aa=0.7[l];[v][l]overlay=W-w-12:12:shortest=1[vout0];[0:a:0]anull[aout0]", config.processing.logo),
|
||||
@ -29,14 +25,13 @@ fn video_audio_input() {
|
||||
"[vout0]",
|
||||
"-map",
|
||||
"[aout0]"
|
||||
],
|
||||
);
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
media.cmd,
|
||||
Some(vec_strings!["-i", "./assets/with_audio.mp4"])
|
||||
);
|
||||
assert_eq!(media.filter, test_filter_cmd);
|
||||
assert_eq!(media.filter.unwrap().cmd, test_filter_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -47,9 +42,9 @@ fn dual_audio_aevalsrc_input() {
|
||||
config.processing.add_logo = false;
|
||||
|
||||
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
|
||||
let media = gen_source(&config, media_obj, &Arc::new(Mutex::new(vec![])));
|
||||
let media = gen_source(&config, media_obj, &None);
|
||||
|
||||
let test_filter_cmd = Some(
|
||||
let test_filter_cmd =
|
||||
vec_strings![
|
||||
"-filter_complex",
|
||||
"[0:v:0]scale=1024:576[vout0];[0:a:0]anull[aout0];aevalsrc=0:channel_layout=stereo:duration=30:sample_rate=48000,anull[aout1]",
|
||||
@ -59,14 +54,13 @@ fn dual_audio_aevalsrc_input() {
|
||||
"[aout0]",
|
||||
"-map",
|
||||
"[aout1]"
|
||||
],
|
||||
);
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
media.cmd,
|
||||
Some(vec_strings!["-i", "./assets/with_audio.mp4"])
|
||||
);
|
||||
assert_eq!(media.filter, test_filter_cmd);
|
||||
assert_eq!(media.filter.unwrap().cmd, test_filter_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -77,9 +71,9 @@ fn dual_audio_input() {
|
||||
config.processing.add_logo = false;
|
||||
|
||||
let media_obj = Media::new(0, "./assets/dual_audio.mp4", true);
|
||||
let media = gen_source(&config, media_obj, &Arc::new(Mutex::new(vec![])));
|
||||
let media = gen_source(&config, media_obj, &None);
|
||||
|
||||
let test_filter_cmd = Some(vec_strings![
|
||||
let test_filter_cmd = vec_strings![
|
||||
"-filter_complex",
|
||||
"[0:v:0]scale=1024:576[vout0];[0:a:0]anull[aout0];[0:a:1]anull[aout1]",
|
||||
"-map",
|
||||
@ -88,13 +82,13 @@ fn dual_audio_input() {
|
||||
"[aout0]",
|
||||
"-map",
|
||||
"[aout1]"
|
||||
]);
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
media.cmd,
|
||||
Some(vec_strings!["-i", "./assets/dual_audio.mp4"])
|
||||
);
|
||||
assert_eq!(media.filter, test_filter_cmd);
|
||||
assert_eq!(media.filter.unwrap().cmd, test_filter_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -106,16 +100,16 @@ fn video_separate_audio_input() {
|
||||
|
||||
let mut media_obj = Media::new(0, "./assets/no_audio.mp4", true);
|
||||
media_obj.audio = "./assets/audio.mp3".to_string();
|
||||
let media = gen_source(&config, media_obj, &Arc::new(Mutex::new(vec![])));
|
||||
let media = gen_source(&config, media_obj, &None);
|
||||
|
||||
let test_filter_cmd = Some(vec_strings![
|
||||
let test_filter_cmd = vec_strings![
|
||||
"-filter_complex",
|
||||
"[0:v:0]scale=1024:576[vout0];[1:a:0]anull[aout0]",
|
||||
"-map",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
"[aout0]"
|
||||
]);
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
media.cmd,
|
||||
@ -128,7 +122,7 @@ fn video_separate_audio_input() {
|
||||
"30"
|
||||
])
|
||||
);
|
||||
assert_eq!(media.filter, test_filter_cmd);
|
||||
assert_eq!(media.filter.unwrap().cmd, test_filter_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -153,7 +147,6 @@ fn video_audio_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![
|
||||
@ -168,7 +161,7 @@ fn video_audio_stream() {
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, &None, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -202,6 +195,7 @@ fn video_dual_audio_stream() {
|
||||
config.out.mode = Stream;
|
||||
config.processing.add_logo = false;
|
||||
config.processing.audio_tracks = 2;
|
||||
config.text.add_text = false;
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-c:v",
|
||||
"libx264",
|
||||
@ -218,8 +212,12 @@ fn video_dual_audio_stream() {
|
||||
"srt://127.0.0.1:40051"
|
||||
]);
|
||||
|
||||
let mut media = Media::new(0, "", false);
|
||||
media.unit = Encoder;
|
||||
media.add_filter(&config, &None);
|
||||
|
||||
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![
|
||||
@ -234,7 +232,7 @@ fn video_dual_audio_stream() {
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, &media.filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -308,17 +306,16 @@ fn video_dual_audio_filter_stream() {
|
||||
.clone()
|
||||
.unwrap()
|
||||
.replace(':', "\\:");
|
||||
let mut filter = "[0:v]null,".to_string();
|
||||
|
||||
filter.push_str(v_drawtext::filter_node(&config, None, &Arc::new(Mutex::new(vec![]))).as_str());
|
||||
|
||||
let enc_filter = vec!["-filter_complex".to_string(), filter];
|
||||
let mut media = Media::new(0, "", false);
|
||||
media.unit = Encoder;
|
||||
media.add_filter(&config, &None);
|
||||
|
||||
let mut output_cmd = config.out.output_cmd.as_ref().unwrap().clone();
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, &media.filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -329,9 +326,9 @@ fn video_dual_audio_filter_stream() {
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-filter_complex",
|
||||
format!("[0:v]null,zmq=b=tcp\\\\://'{socket}',drawtext@dyntext=text=''[v_out1]"),
|
||||
format!("[0:v:0]zmq=b=tcp\\\\://'{socket}',drawtext@dyntext=text=''[vout0]"),
|
||||
"-map",
|
||||
"[v_out1]",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
@ -391,7 +388,6 @@ fn video_audio_multi_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![
|
||||
@ -406,7 +402,7 @@ fn video_audio_multi_stream() {
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, &None, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -499,7 +495,6 @@ fn video_dual_audio_multi_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![
|
||||
@ -514,7 +509,7 @@ fn video_dual_audio_multi_stream() {
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, &None, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -636,17 +631,16 @@ fn video_dual_audio_multi_filter_stream() {
|
||||
.clone()
|
||||
.unwrap()
|
||||
.replace(':', "\\:");
|
||||
let mut filter = "[0:v]null,".to_string();
|
||||
|
||||
filter.push_str(v_drawtext::filter_node(&config, None, &Arc::new(Mutex::new(vec![]))).as_str());
|
||||
|
||||
let enc_filter = vec!["-filter_complex".to_string(), filter];
|
||||
let mut media = Media::new(0, "", false);
|
||||
media.unit = Encoder;
|
||||
media.add_filter(&config, &None);
|
||||
|
||||
let mut output_cmd = config.out.output_cmd.as_ref().unwrap().clone();
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, &media.filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -657,9 +651,9 @@ fn video_dual_audio_multi_filter_stream() {
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-filter_complex",
|
||||
format!("[0:v]null,zmq=b=tcp\\\\://'{socket}',drawtext@dyntext=text=''[v_out1]"),
|
||||
format!("[0:v:0]zmq=b=tcp\\\\://'{socket}',drawtext@dyntext=text=''[vout0]"),
|
||||
"-map",
|
||||
"[v_out1]",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
@ -678,7 +672,7 @@ fn video_dual_audio_multi_filter_stream() {
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40051",
|
||||
"-map",
|
||||
"[v_out1]",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
@ -736,8 +730,7 @@ fn video_audio_hls() {
|
||||
]);
|
||||
|
||||
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
|
||||
let media = gen_source(&config, media_obj, &Arc::new(Mutex::new(vec![])));
|
||||
let enc_filter = media.filter.unwrap();
|
||||
let media = gen_source(&config, media_obj, &None);
|
||||
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -749,7 +742,7 @@ fn video_audio_hls() {
|
||||
"./assets/with_audio.mp4"
|
||||
];
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, &media.filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -823,8 +816,7 @@ fn video_multi_audio_hls() {
|
||||
]);
|
||||
|
||||
let media_obj = Media::new(0, "./assets/dual_audio.mp4", true);
|
||||
let media = gen_source(&config, media_obj, &Arc::new(Mutex::new(vec![])));
|
||||
let enc_filter = media.filter.unwrap();
|
||||
let media = gen_source(&config, media_obj, &None);
|
||||
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -836,7 +828,7 @@ fn video_multi_audio_hls() {
|
||||
"./assets/dual_audio.mp4"
|
||||
];
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, &media.filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -927,8 +919,7 @@ fn multi_video_audio_hls() {
|
||||
]);
|
||||
|
||||
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
|
||||
let media = gen_source(&config, media_obj, &Arc::new(Mutex::new(vec![])));
|
||||
let enc_filter = media.filter.unwrap();
|
||||
let media = gen_source(&config, media_obj, &None);
|
||||
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -940,7 +931,7 @@ fn multi_video_audio_hls() {
|
||||
"./assets/with_audio.mp4"
|
||||
];
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, &media.filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -1044,8 +1035,7 @@ fn multi_video_multi_audio_hls() {
|
||||
]);
|
||||
|
||||
let media_obj = Media::new(0, "./assets/dual_audio.mp4", true);
|
||||
let media = gen_source(&config, media_obj, &Arc::new(Mutex::new(vec![])));
|
||||
let enc_filter = media.filter.unwrap();
|
||||
let media = gen_source(&config, media_obj, &None);
|
||||
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -1057,7 +1047,7 @@ fn multi_video_multi_audio_hls() {
|
||||
"./assets/dual_audio.mp4"
|
||||
];
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, &media.filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
|
Loading…
Reference in New Issue
Block a user