rearrange custom filters
fix missing output mapping on multiple outputs
This commit is contained in:
parent
72a1cb9e5a
commit
9cb3a62069
@ -91,7 +91,7 @@ pub fn ingest_server(
|
|||||||
|
|
||||||
if let Some(mut filter) = dummy_media.filter {
|
if let Some(mut filter) = dummy_media.filter {
|
||||||
server_cmd.append(&mut filter.cmd());
|
server_cmd.append(&mut filter.cmd());
|
||||||
server_cmd.append(&mut filter.map(None));
|
server_cmd.append(&mut filter.map());
|
||||||
}
|
}
|
||||||
|
|
||||||
server_cmd.append(&mut config.processing.settings.unwrap());
|
server_cmd.append(&mut config.processing.settings.unwrap());
|
||||||
|
@ -104,7 +104,7 @@ pub fn player(
|
|||||||
|
|
||||||
if let Some(mut filter) = node.filter {
|
if let Some(mut filter) = node.filter {
|
||||||
dec_cmd.append(&mut filter.cmd());
|
dec_cmd.append(&mut filter.cmd());
|
||||||
dec_cmd.append(&mut filter.map(None));
|
dec_cmd.append(&mut filter.map());
|
||||||
}
|
}
|
||||||
|
|
||||||
dec_cmd.append(&mut config.processing.clone().settings.unwrap());
|
dec_cmd.append(&mut config.processing.clone().settings.unwrap());
|
||||||
|
@ -11,6 +11,7 @@ pub use arg_parse::Args;
|
|||||||
use ffplayout_lib::{
|
use ffplayout_lib::{
|
||||||
filter::Filters,
|
filter::Filters,
|
||||||
utils::{time_to_sec, PlayoutConfig, ProcessMode::*},
|
utils::{time_to_sec, PlayoutConfig, ProcessMode::*},
|
||||||
|
vec_strings,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Read command line arguments, and override the config with them.
|
/// Read command line arguments, and override the config with them.
|
||||||
@ -97,18 +98,14 @@ pub fn prepare_output_cmd(
|
|||||||
) -> Vec<String> {
|
) -> Vec<String> {
|
||||||
let mut output_params = config.out.clone().output_cmd.unwrap();
|
let mut output_params = config.out.clone().output_cmd.unwrap();
|
||||||
let mut new_params = vec![];
|
let mut new_params = vec![];
|
||||||
let mut count = 1;
|
let mut count = 0;
|
||||||
let re_map = Regex::new(r"(\[?[0-9]:[av](:[0-9]+)?\]?|-map$|\[[a-z_0-9]+\])").unwrap(); // match a/v filter links and mapping
|
let re_v = Regex::new(r"\[?0:v(:0)?\]?").unwrap();
|
||||||
|
|
||||||
if let Some(mut filter) = filters.clone() {
|
if let Some(mut filter) = filters.clone() {
|
||||||
println!("filter: {filter:#?}\n");
|
|
||||||
for (i, param) in output_params.iter().enumerate() {
|
for (i, param) in output_params.iter().enumerate() {
|
||||||
if !re_map.is_match(param)
|
if filter.video_out_link.len() > count && re_v.is_match(param) {
|
||||||
|| (i < output_params.len() - 2
|
new_params.push(filter.video_out_link[count].clone());
|
||||||
&& (output_params[i + 1].contains("0:s") || param.contains("0:s")))
|
} else {
|
||||||
{
|
|
||||||
// Skip mapping parameters, when no multi in/out filter is set.
|
|
||||||
// Only add subtitle mapping.
|
|
||||||
new_params.push(param.clone());
|
new_params.push(param.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,10 +115,20 @@ pub fn prepare_output_cmd(
|
|||||||
&& !output_params[i - 1].starts_with('-')
|
&& !output_params[i - 1].starts_with('-')
|
||||||
&& i < output_params.len() - 1
|
&& i < output_params.len() - 1
|
||||||
{
|
{
|
||||||
// add mapping to following outputs
|
count += 1;
|
||||||
new_params.append(&mut filter.map(Some(count)));
|
|
||||||
|
|
||||||
count += 1
|
if filter.video_out_link.len() > count
|
||||||
|
&& !output_params.contains(&"-map".to_string())
|
||||||
|
{
|
||||||
|
new_params.append(&mut vec_strings![
|
||||||
|
"-map",
|
||||||
|
filter.video_out_link[count].clone()
|
||||||
|
]);
|
||||||
|
|
||||||
|
for i in 0..config.processing.audio_tracks {
|
||||||
|
new_params.append(&mut vec_strings!["-map", format!("0:a:{i}")]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,10 +136,17 @@ pub fn prepare_output_cmd(
|
|||||||
|
|
||||||
cmd.append(&mut filter.cmd());
|
cmd.append(&mut filter.cmd());
|
||||||
|
|
||||||
if config.out.output_count > 1 {
|
if !filter.map().iter().all(|item| output_params.contains(item))
|
||||||
cmd.append(&mut filter.map(Some(0)));
|
&& filter.output_chain.is_empty()
|
||||||
} else {
|
&& filter.video_out_link.is_empty()
|
||||||
cmd.append(&mut filter.map(None));
|
{
|
||||||
|
cmd.append(&mut filter.map())
|
||||||
|
} else if &output_params[0] != "-map" && !filter.video_out_link.is_empty() {
|
||||||
|
cmd.append(&mut vec_strings!["-map", filter.video_out_link[0].clone()]);
|
||||||
|
|
||||||
|
for i in 0..config.processing.audio_tracks {
|
||||||
|
cmd.append(&mut vec_strings!["-map", format!("0:a:{i}")]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,43 +1,36 @@
|
|||||||
|
use regex::Regex;
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
|
|
||||||
fn strip_str(mut input: &str) -> String {
|
|
||||||
input = input.strip_prefix(';').unwrap_or(input);
|
|
||||||
input = input.strip_prefix("[0:v]").unwrap_or(input);
|
|
||||||
input = input.strip_prefix("[0:a]").unwrap_or(input);
|
|
||||||
input = input.strip_suffix(';').unwrap_or(input);
|
|
||||||
input = input.strip_suffix("[c_v_out]").unwrap_or(input);
|
|
||||||
input = input.strip_suffix("[c_a_out]").unwrap_or(input);
|
|
||||||
|
|
||||||
input.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply custom filters
|
/// Apply custom filters
|
||||||
pub fn custom_filter(filter: &str) -> (String, String) {
|
pub fn filter_node(filter: &str) -> (String, String) {
|
||||||
|
let re = Regex::new(r"^;?(\[[^\[]+\])?|\[[^\[]+\]$").unwrap(); // match start/end link;
|
||||||
let mut video_filter = String::new();
|
let mut video_filter = String::new();
|
||||||
let mut audio_filter = String::new();
|
let mut audio_filter = String::new();
|
||||||
|
|
||||||
|
// match chain with audio and video filter
|
||||||
if filter.contains("[c_v_out]") && filter.contains("[c_a_out]") {
|
if filter.contains("[c_v_out]") && filter.contains("[c_a_out]") {
|
||||||
let v_pos = filter.find("[c_v_out]").unwrap();
|
let v_pos = filter.find("[c_v_out]").unwrap();
|
||||||
let a_pos = filter.find("[c_a_out]").unwrap();
|
let a_pos = filter.find("[c_a_out]").unwrap();
|
||||||
let mut delimiter = "[c_v_out]";
|
let mut delimiter = "[c_v_out]";
|
||||||
|
|
||||||
|
// split delimiter should be first filter output link
|
||||||
if v_pos > a_pos {
|
if v_pos > a_pos {
|
||||||
delimiter = "[c_a_out]";
|
delimiter = "[c_a_out]";
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((f_1, f_2)) = filter.split_once(delimiter) {
|
if let Some((f_1, f_2)) = filter.split_once(delimiter) {
|
||||||
if f_2.contains("[c_a_out]") {
|
if f_2.contains("[c_a_out]") {
|
||||||
video_filter = strip_str(f_1);
|
video_filter = re.replace_all(f_1, "").to_string();
|
||||||
audio_filter = strip_str(f_2);
|
audio_filter = re.replace_all(f_2, "").to_string();
|
||||||
} else {
|
} else {
|
||||||
video_filter = strip_str(f_2);
|
video_filter = re.replace_all(f_2, "").to_string();
|
||||||
audio_filter = strip_str(f_1);
|
audio_filter = re.replace_all(f_1, "").to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if filter.contains("[c_v_out]") {
|
} else if filter.contains("[c_v_out]") {
|
||||||
video_filter = strip_str(filter);
|
video_filter = re.replace_all(filter, "").to_string();
|
||||||
} else if filter.contains("[c_a_out]") {
|
} else if filter.contains("[c_a_out]") {
|
||||||
audio_filter = strip_str(filter);
|
audio_filter = re.replace_all(filter, "").to_string();
|
||||||
} else if !filter.is_empty() && filter != "~" {
|
} else if !filter.is_empty() && filter != "~" {
|
||||||
error!("Custom filter is not well formatted, use correct out link names (\"[c_v_out]\" and/or \"[c_a_out]\"). Filter skipped!")
|
error!("Custom filter is not well formatted, use correct out link names (\"[c_v_out]\" and/or \"[c_a_out]\"). Filter skipped!")
|
||||||
}
|
}
|
@ -8,12 +8,11 @@ use regex::Regex;
|
|||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
|
|
||||||
mod a_loudnorm;
|
mod a_loudnorm;
|
||||||
|
mod custom;
|
||||||
pub mod v_drawtext;
|
pub mod v_drawtext;
|
||||||
|
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
controller::ProcessUnit::{self, *},
|
controller::ProcessUnit::*, fps_calc, get_delta, is_close, Media, MediaProbe, OutputMode::*,
|
||||||
fps_calc, get_delta, is_close, Media, MediaProbe,
|
|
||||||
OutputMode::*,
|
|
||||||
PlayoutConfig,
|
PlayoutConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,6 +39,7 @@ use FilterType::*;
|
|||||||
pub struct Filters {
|
pub struct Filters {
|
||||||
pub audio_chain: String,
|
pub audio_chain: String,
|
||||||
pub video_chain: String,
|
pub video_chain: String,
|
||||||
|
pub output_chain: Vec<String>,
|
||||||
pub audio_map: Vec<String>,
|
pub audio_map: Vec<String>,
|
||||||
pub video_map: Vec<String>,
|
pub video_map: Vec<String>,
|
||||||
pub audio_out_link: Vec<String>,
|
pub audio_out_link: Vec<String>,
|
||||||
@ -57,6 +57,7 @@ impl Filters {
|
|||||||
Self {
|
Self {
|
||||||
audio_chain: String::new(),
|
audio_chain: String::new(),
|
||||||
video_chain: String::new(),
|
video_chain: String::new(),
|
||||||
|
output_chain: vec![],
|
||||||
audio_map: vec![],
|
audio_map: vec![],
|
||||||
video_map: vec![],
|
video_map: vec![],
|
||||||
audio_out_link: vec![],
|
audio_out_link: vec![],
|
||||||
@ -118,6 +119,10 @@ impl Filters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn cmd(&mut self) -> Vec<String> {
|
pub fn cmd(&mut self) -> Vec<String> {
|
||||||
|
if !self.output_chain.is_empty() {
|
||||||
|
return self.output_chain.clone();
|
||||||
|
}
|
||||||
|
|
||||||
let mut v_chain = self.video_chain.clone();
|
let mut v_chain = self.video_chain.clone();
|
||||||
let mut a_chain = self.audio_chain.clone();
|
let mut a_chain = self.audio_chain.clone();
|
||||||
|
|
||||||
@ -145,33 +150,9 @@ impl Filters {
|
|||||||
cmd
|
cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map(&mut self, output_number: Option<usize>) -> Vec<String> {
|
pub fn map(&mut self) -> Vec<String> {
|
||||||
let mut o_map = self.output_map.clone();
|
let mut o_map = self.output_map.clone();
|
||||||
|
|
||||||
if let Some(n) = output_number {
|
|
||||||
o_map.clear();
|
|
||||||
|
|
||||||
if self.video_out_link.len() > n {
|
|
||||||
o_map.append(&mut vec_strings!["-map", self.video_out_link[n]])
|
|
||||||
} else {
|
|
||||||
o_map.append(&mut vec_strings!["-map", "0:v"])
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.audio_out_link.len() > n {
|
|
||||||
o_map.append(&mut vec_strings!["-map", self.audio_out_link[n]])
|
|
||||||
} else {
|
|
||||||
for i in 0..self.audio_track_count {
|
|
||||||
let a_map = format!("{}:a:{i}", self.audio_position);
|
|
||||||
|
|
||||||
if !o_map.contains(&a_map) {
|
|
||||||
o_map.append(&mut vec_strings!["-map", a_map]);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return o_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.video_last == -1 {
|
if self.video_last == -1 {
|
||||||
let v_map = "0:v".to_string();
|
let v_map = "0:v".to_string();
|
||||||
|
|
||||||
@ -463,114 +444,48 @@ pub fn split_filter(chain: &mut Filters, count: usize, nr: i32, filter_type: Fil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process custom/output filter
|
/// Process output filter chain and add new filters to existing ones.
|
||||||
///
|
fn process_output_filters(config: &PlayoutConfig, chain: &mut Filters, custom_filter: &str) {
|
||||||
/// Split filter string and add them to the existing filtergraph.
|
let filter =
|
||||||
fn process_custom_filters(
|
if (config.text.add_text && !config.text.text_from_filename) || config.out.mode == HLS {
|
||||||
config: &PlayoutConfig,
|
let re_v = Regex::new(r"\[[0:]+[v^\[]+([:0]+)?\]").unwrap(); // match video filter input link
|
||||||
chain: &mut Filters,
|
let _re_a = Regex::new(r"\[[0:]+[a^\[]+([:0]+)?\]").unwrap(); // match video filter input link
|
||||||
custom_filter: &str,
|
let mut cf = custom_filter.to_string();
|
||||||
unit: ProcessUnit,
|
|
||||||
) {
|
|
||||||
let re_v =
|
|
||||||
Regex::new(r"(^\[([0-9:]+)?[v^\[]([\w:_-]+)?\]|\[([0-9:]+)?[v^\[]([\w:_-]+)?\]$)").unwrap(); // match first/last video filter links
|
|
||||||
let re_a =
|
|
||||||
Regex::new(r"(^\[([0-9:]+)?[a^\[]([\w:_-]+)?\]|\[([0-9:]+)?[a^\[]([\w:_-]+)?\]$)").unwrap(); // match first/last audio filter links
|
|
||||||
let re_v_delim = Regex::new(r";\[[0-9:]+[v^\[]([0-9:]+)?\]").unwrap(); // match video filter link as delimiter
|
|
||||||
let re_a_delim = Regex::new(r";\[[0-9:]+[a^\[]([0-9:]+)?\]").unwrap(); // match video filter link as delimiter
|
|
||||||
|
|
||||||
let mut video_filter = String::new();
|
if !chain.video_chain.is_empty() {
|
||||||
let mut audio_filter = String::new();
|
cf = re_v
|
||||||
|
.replace(&cf, &format!("{},", chain.video_chain))
|
||||||
if custom_filter.contains("split") && unit == Decoder {
|
.to_string()
|
||||||
error!("No split filter in {unit} allow! Skip filter...");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(v_d) = re_v_delim.find(custom_filter) {
|
if !chain.audio_chain.is_empty() {
|
||||||
if let Some((a, v)) = custom_filter.split_once(v_d.as_str()) {
|
let audio_split = chain
|
||||||
video_filter = re_v.replace_all(v, "").to_string();
|
.audio_chain
|
||||||
audio_filter = re_a.replace_all(a, "").to_string();
|
.split(';')
|
||||||
}
|
.enumerate()
|
||||||
} else if let Some(a_d) = re_a_delim.find(custom_filter) {
|
.map(|(i, p)| p.replace(&format!("[aout{i}]"), ""))
|
||||||
if let Some((v, a)) = custom_filter.split_once(a_d.as_str()) {
|
.collect::<Vec<String>>();
|
||||||
video_filter = re_v.replace_all(v, "").to_string();
|
|
||||||
audio_filter = re_a.replace_all(a, "").to_string();
|
|
||||||
}
|
|
||||||
} else if re_v.is_match(custom_filter) {
|
|
||||||
video_filter = re_v.replace_all(custom_filter, "").to_string();
|
|
||||||
} else if re_a.is_match(custom_filter) {
|
|
||||||
audio_filter = re_a.replace_all(custom_filter, "").to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
if video_filter.contains("split") {
|
|
||||||
let nr = Regex::new(".*split=([0-9]+).*").unwrap();
|
|
||||||
let re_s = Regex::new(r"(;?\[[^\]]*\])?,?split=[0-9]+(\[[\w]+\])+;?").unwrap();
|
|
||||||
let split_nr = nr
|
|
||||||
.replace(&video_filter, "$1")
|
|
||||||
.parse::<usize>()
|
|
||||||
.unwrap_or(1);
|
|
||||||
let new_filter = re_s.replace_all(&video_filter, "").to_string();
|
|
||||||
|
|
||||||
if !new_filter.is_empty() {
|
|
||||||
chain.add_filter(&new_filter, 0, Video);
|
|
||||||
}
|
|
||||||
|
|
||||||
split_filter(chain, split_nr, 0, Video);
|
|
||||||
} else if !video_filter.is_empty() {
|
|
||||||
chain.add_filter(&video_filter, 0, Video);
|
|
||||||
}
|
|
||||||
|
|
||||||
if audio_filter.contains("asplit") {
|
|
||||||
let nr = Regex::new(".*split=([0-9]+).*").unwrap();
|
|
||||||
let re_s = Regex::new(r"(;?\[[^\]]*\])?,?asplit=[0-9]+(\[[\w]+\])+;?").unwrap();
|
|
||||||
let split_nr = nr
|
|
||||||
.replace(&audio_filter, "$1")
|
|
||||||
.parse::<usize>()
|
|
||||||
.unwrap_or(1);
|
|
||||||
let new_filter = re_s.replace_all(&audio_filter, "").to_string();
|
|
||||||
|
|
||||||
if !new_filter.is_empty() {
|
|
||||||
for i in 0..config.processing.audio_tracks {
|
|
||||||
chain.add_filter(&new_filter, i, Audio);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0..config.processing.audio_tracks {
|
for i in 0..config.processing.audio_tracks {
|
||||||
split_filter(chain, split_nr, i, Audio);
|
cf = cf.replace(
|
||||||
}
|
&format!("[0:a:{i}]"),
|
||||||
} else if !audio_filter.is_empty() {
|
&format!("{},", &audio_split[i as usize]),
|
||||||
for i in 0..config.processing.audio_tracks {
|
)
|
||||||
chain.add_filter(&audio_filter, i, Audio);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for f in custom_filter.split(delim) {
|
cf
|
||||||
// if re_v.is_match(f) {
|
} else {
|
||||||
// if f.contains("split") {
|
custom_filter.to_string()
|
||||||
// let re = Regex::new(r"split=([0-9]+)").unwrap();
|
};
|
||||||
// let split_nr = re.replace(f, "$1").parse::<usize>().unwrap_or(1);
|
|
||||||
|
|
||||||
// split_filter(chain, split_nr, 0, Video);
|
chain.output_chain = vec_strings!["-filter_complex", filter]
|
||||||
// } else {
|
}
|
||||||
// let filter_str = re_v.replace_all(f, "").to_string();
|
|
||||||
// chain.add_filter(&filter_str, 0, Video);
|
|
||||||
// }
|
|
||||||
// } else if re_a.is_match(f) {
|
|
||||||
// for i in 0..config.processing.audio_tracks {
|
|
||||||
// if f.contains("asplit") {
|
|
||||||
// let re = Regex::new(r"asplit=([0-9]+)").unwrap();
|
|
||||||
// let split_nr = re.replace(f, "$1").parse::<usize>().unwrap_or(1);
|
|
||||||
|
|
||||||
// split_filter(chain, split_nr, i, Audio);
|
fn custom(filter: &str, chain: &mut Filters, nr: i32, filter_type: FilterType) {
|
||||||
// } else {
|
if !filter.is_empty() {
|
||||||
// let filter_str = re_a.replace_all(f, "").to_string();
|
chain.add_filter(filter, nr, filter_type);
|
||||||
// chain.add_filter(&filter_str, i, Audio);
|
}
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filter_chains(
|
pub fn filter_chains(
|
||||||
@ -584,9 +499,9 @@ pub fn filter_chains(
|
|||||||
add_text(node, &mut filters, config, filter_chain);
|
add_text(node, &mut filters, config, filter_chain);
|
||||||
|
|
||||||
if let Some(f) = config.out.output_filter.clone() {
|
if let Some(f) = config.out.output_filter.clone() {
|
||||||
process_custom_filters(config, &mut filters, &f, Encoder)
|
process_output_filters(config, &mut filters, &f)
|
||||||
} else if config.out.output_count > 1 {
|
} else if config.out.output_count > 1 {
|
||||||
split_filter(&mut filters, config.out.output_count, 0, Video)
|
split_filter(&mut filters, config.out.output_count, 0, Video);
|
||||||
}
|
}
|
||||||
|
|
||||||
return filters;
|
return filters;
|
||||||
@ -624,6 +539,12 @@ pub fn filter_chains(
|
|||||||
overlay(node, &mut filters, config);
|
overlay(node, &mut filters, config);
|
||||||
realtime(node, &mut filters, config);
|
realtime(node, &mut filters, config);
|
||||||
|
|
||||||
|
let (proc_vf, proc_af) = custom::filter_node(&config.processing.custom_filter);
|
||||||
|
let (list_vf, list_af) = custom::filter_node(&node.custom_filter);
|
||||||
|
|
||||||
|
custom(&proc_vf, &mut filters, 0, Video);
|
||||||
|
custom(&list_vf, &mut filters, 0, Video);
|
||||||
|
|
||||||
for i in 0..config.processing.audio_tracks {
|
for i in 0..config.processing.audio_tracks {
|
||||||
if node
|
if node
|
||||||
.probe
|
.probe
|
||||||
@ -648,15 +569,16 @@ pub fn filter_chains(
|
|||||||
add_loudnorm(node, &mut filters, config, i);
|
add_loudnorm(node, &mut filters, config, i);
|
||||||
fade(node, &mut filters, i, Audio);
|
fade(node, &mut filters, i, Audio);
|
||||||
audio_volume(&mut filters, config, i);
|
audio_volume(&mut filters, config, i);
|
||||||
|
|
||||||
|
custom(&proc_af, &mut filters, i, Audio);
|
||||||
|
custom(&list_af, &mut filters, i, Audio);
|
||||||
}
|
}
|
||||||
|
|
||||||
process_custom_filters(
|
if config.out.mode == HLS {
|
||||||
config,
|
if let Some(f) = config.out.output_filter.clone() {
|
||||||
&mut filters,
|
process_output_filters(config, &mut filters, &f)
|
||||||
&config.processing.custom_filter,
|
}
|
||||||
node.unit,
|
}
|
||||||
);
|
|
||||||
process_custom_filters(config, &mut filters, &node.custom_filter, node.unit);
|
|
||||||
|
|
||||||
filters
|
filters
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ fn check_media(
|
|||||||
|
|
||||||
enc_cmd.append(&mut node.cmd.unwrap_or_default());
|
enc_cmd.append(&mut node.cmd.unwrap_or_default());
|
||||||
enc_cmd.append(&mut filter.cmd());
|
enc_cmd.append(&mut filter.cmd());
|
||||||
enc_cmd.append(&mut filter.map(None));
|
enc_cmd.append(&mut filter.map());
|
||||||
enc_cmd.append(&mut vec_strings!["-t", "0.1", "-f", "null", "-"]);
|
enc_cmd.append(&mut vec_strings!["-t", "0.1", "-f", "null", "-"]);
|
||||||
|
|
||||||
let mut enc_proc = match Command::new("ffmpeg")
|
let mut enc_proc = match Command::new("ffmpeg")
|
||||||
|
@ -30,7 +30,7 @@ fn video_audio_input() {
|
|||||||
Some(vec_strings!["-i", "./assets/with_audio.mp4"])
|
Some(vec_strings!["-i", "./assets/with_audio.mp4"])
|
||||||
);
|
);
|
||||||
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
||||||
assert_eq!(media.filter.unwrap().map(None), test_filter_map);
|
assert_eq!(media.filter.unwrap().map(), test_filter_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -38,7 +38,7 @@ fn video_audio_custom_filter1_input() {
|
|||||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||||
config.out.mode = Stream;
|
config.out.mode = Stream;
|
||||||
config.processing.add_logo = false;
|
config.processing.add_logo = false;
|
||||||
config.processing.custom_filter = "[0:v]gblur=2;[0:a]volume=0.2".to_string();
|
config.processing.custom_filter = "[0:v]gblur=2[c_v_out];[0:a]volume=0.2[c_a_out]".to_string();
|
||||||
|
|
||||||
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
|
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
|
||||||
let media = gen_source(&config, media_obj, &None);
|
let media = gen_source(&config, media_obj, &None);
|
||||||
@ -55,7 +55,7 @@ fn video_audio_custom_filter1_input() {
|
|||||||
Some(vec_strings!["-i", "./assets/with_audio.mp4"])
|
Some(vec_strings!["-i", "./assets/with_audio.mp4"])
|
||||||
);
|
);
|
||||||
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
||||||
assert_eq!(media.filter.unwrap().map(None), test_filter_map);
|
assert_eq!(media.filter.unwrap().map(), test_filter_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -64,7 +64,8 @@ fn video_audio_custom_filter2_input() {
|
|||||||
config.out.mode = Stream;
|
config.out.mode = Stream;
|
||||||
config.processing.add_logo = false;
|
config.processing.add_logo = false;
|
||||||
config.processing.custom_filter =
|
config.processing.custom_filter =
|
||||||
"[0:v]null[v];movie=logo.png[l];[v][l]overlay[vout0];[0:a]volume=0.2[aout0]".to_string();
|
"[0:v]null[v];movie=logo.png[l];[v][l]overlay[c_v_out];[0:a]volume=0.2[c_a_out]"
|
||||||
|
.to_string();
|
||||||
|
|
||||||
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
|
let media_obj = Media::new(0, "./assets/with_audio.mp4", true);
|
||||||
let media = gen_source(&config, media_obj, &None);
|
let media = gen_source(&config, media_obj, &None);
|
||||||
@ -81,7 +82,7 @@ fn video_audio_custom_filter2_input() {
|
|||||||
Some(vec_strings!["-i", "./assets/with_audio.mp4"])
|
Some(vec_strings!["-i", "./assets/with_audio.mp4"])
|
||||||
);
|
);
|
||||||
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
||||||
assert_eq!(media.filter.unwrap().map(None), test_filter_map);
|
assert_eq!(media.filter.unwrap().map(), test_filter_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -107,7 +108,7 @@ fn dual_audio_aevalsrc_input() {
|
|||||||
Some(vec_strings!["-i", "./assets/with_audio.mp4"])
|
Some(vec_strings!["-i", "./assets/with_audio.mp4"])
|
||||||
);
|
);
|
||||||
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
||||||
assert_eq!(media.filter.unwrap().map(None), test_filter_map);
|
assert_eq!(media.filter.unwrap().map(), test_filter_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -132,7 +133,7 @@ fn dual_audio_input() {
|
|||||||
Some(vec_strings!["-i", "./assets/dual_audio.mp4"])
|
Some(vec_strings!["-i", "./assets/dual_audio.mp4"])
|
||||||
);
|
);
|
||||||
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
||||||
assert_eq!(media.filter.unwrap().map(None), test_filter_map);
|
assert_eq!(media.filter.unwrap().map(), test_filter_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -165,7 +166,7 @@ fn video_separate_audio_input() {
|
|||||||
])
|
])
|
||||||
);
|
);
|
||||||
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
||||||
assert_eq!(media.filter.unwrap().map(None), test_filter_map);
|
assert_eq!(media.filter.unwrap().map(), test_filter_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -228,7 +229,7 @@ fn video_audio_stream() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn video_audio_filter_stream() {
|
fn video_audio_filter1_stream() {
|
||||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||||
config.out.mode = Stream;
|
config.out.mode = Stream;
|
||||||
config.processing.add_logo = false;
|
config.processing.add_logo = false;
|
||||||
@ -279,7 +280,7 @@ fn video_audio_filter_stream() {
|
|||||||
"-i",
|
"-i",
|
||||||
"pipe:0",
|
"pipe:0",
|
||||||
"-filter_complex",
|
"-filter_complex",
|
||||||
"[0:v:0]gblur=2[vout0];[0:a:0]volume=0.2[aout0]",
|
"[0:v]gblur=2[vout0];[0:a]volume=0.2[aout0]",
|
||||||
"-map",
|
"-map",
|
||||||
"[vout0]",
|
"[vout0]",
|
||||||
"-map",
|
"-map",
|
||||||
@ -362,7 +363,7 @@ fn video_audio_filter2_stream() {
|
|||||||
"-i",
|
"-i",
|
||||||
"pipe:0",
|
"pipe:0",
|
||||||
"-filter_complex",
|
"-filter_complex",
|
||||||
format!("[0:v:0]zmq=b=tcp\\\\://'{socket}',drawtext@dyntext=text='',gblur=2[vout0];[0:a:0]volume=0.2[aout0]"),
|
format!("[0:v:0]zmq=b=tcp\\\\://'{socket}',drawtext@dyntext=text='',gblur=2[vout0];[0:a]volume=0.2[aout0]"),
|
||||||
"-map",
|
"-map",
|
||||||
"[vout0]",
|
"[vout0]",
|
||||||
"-map",
|
"-map",
|
||||||
@ -398,7 +399,9 @@ fn video_audio_filter3_stream() {
|
|||||||
);
|
);
|
||||||
config.out.output_cmd = Some(vec_strings![
|
config.out.output_cmd = Some(vec_strings![
|
||||||
"-map",
|
"-map",
|
||||||
"[v_out0]",
|
"[vout0]",
|
||||||
|
"-map",
|
||||||
|
"0:a",
|
||||||
"-c:v",
|
"-c:v",
|
||||||
"libx264",
|
"libx264",
|
||||||
"-c:a",
|
"-c:a",
|
||||||
@ -450,7 +453,7 @@ fn video_audio_filter3_stream() {
|
|||||||
"-map",
|
"-map",
|
||||||
"[vout0]",
|
"[vout0]",
|
||||||
"-map",
|
"-map",
|
||||||
"0:a:0",
|
"0:a",
|
||||||
"-c:v",
|
"-c:v",
|
||||||
"libx264",
|
"libx264",
|
||||||
"-c:a",
|
"-c:a",
|
||||||
@ -909,6 +912,119 @@ fn video_dual_audio_multi_stream() {
|
|||||||
assert_eq!(enc_cmd, test_cmd);
|
assert_eq!(enc_cmd, test_cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn video_audio_text_multi_stream() {
|
||||||
|
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||||
|
config.out.mode = Stream;
|
||||||
|
config.processing.add_logo = false;
|
||||||
|
config.text.add_text = true;
|
||||||
|
config.text.fontfile = String::new();
|
||||||
|
config.out.output_count = 2;
|
||||||
|
config.out.output_cmd = Some(vec_strings![
|
||||||
|
"-c:v",
|
||||||
|
"libx264",
|
||||||
|
"-c:a",
|
||||||
|
"aac",
|
||||||
|
"-ar",
|
||||||
|
"44100",
|
||||||
|
"-b:a",
|
||||||
|
"128k",
|
||||||
|
"-flags",
|
||||||
|
"+global_header",
|
||||||
|
"-f",
|
||||||
|
"mpegts",
|
||||||
|
"srt://127.0.0.1:40051",
|
||||||
|
"-s",
|
||||||
|
"512x288",
|
||||||
|
"-c:v",
|
||||||
|
"libx264",
|
||||||
|
"-c:a",
|
||||||
|
"aac",
|
||||||
|
"-ar",
|
||||||
|
"44100",
|
||||||
|
"-b:a",
|
||||||
|
"128k",
|
||||||
|
"-flags",
|
||||||
|
"+global_header",
|
||||||
|
"-f",
|
||||||
|
"mpegts",
|
||||||
|
"srt://127.0.0.1:40052"
|
||||||
|
]);
|
||||||
|
|
||||||
|
let enc_prefix = vec_strings![
|
||||||
|
"-hide_banner",
|
||||||
|
"-nostats",
|
||||||
|
"-v",
|
||||||
|
"level+error",
|
||||||
|
"-re",
|
||||||
|
"-i",
|
||||||
|
"pipe:0"
|
||||||
|
];
|
||||||
|
|
||||||
|
let socket = config
|
||||||
|
.text
|
||||||
|
.zmq_stream_socket
|
||||||
|
.clone()
|
||||||
|
.unwrap()
|
||||||
|
.replace(':', "\\:");
|
||||||
|
|
||||||
|
let mut media = Media::new(0, "", false);
|
||||||
|
media.unit = Encoder;
|
||||||
|
media.add_filter(&config, &None);
|
||||||
|
|
||||||
|
let enc_cmd = prepare_output_cmd(&config, enc_prefix, &media.filter);
|
||||||
|
|
||||||
|
let test_cmd = vec_strings![
|
||||||
|
"-hide_banner",
|
||||||
|
"-nostats",
|
||||||
|
"-v",
|
||||||
|
"level+error",
|
||||||
|
"-re",
|
||||||
|
"-i",
|
||||||
|
"pipe:0",
|
||||||
|
"-filter_complex",
|
||||||
|
format!("[0:v:0]zmq=b=tcp\\\\://'{socket}',drawtext@dyntext=text='',split=2[vout_0_0][vout_0_1]"),
|
||||||
|
"-map",
|
||||||
|
"[vout_0_0]",
|
||||||
|
"-map",
|
||||||
|
"0:a:0",
|
||||||
|
"-c:v",
|
||||||
|
"libx264",
|
||||||
|
"-c:a",
|
||||||
|
"aac",
|
||||||
|
"-ar",
|
||||||
|
"44100",
|
||||||
|
"-b:a",
|
||||||
|
"128k",
|
||||||
|
"-flags",
|
||||||
|
"+global_header",
|
||||||
|
"-f",
|
||||||
|
"mpegts",
|
||||||
|
"srt://127.0.0.1:40051",
|
||||||
|
"-map",
|
||||||
|
"[vout_0_1]",
|
||||||
|
"-map",
|
||||||
|
"0:a:0",
|
||||||
|
"-s",
|
||||||
|
"512x288",
|
||||||
|
"-c:v",
|
||||||
|
"libx264",
|
||||||
|
"-c:a",
|
||||||
|
"aac",
|
||||||
|
"-ar",
|
||||||
|
"44100",
|
||||||
|
"-b:a",
|
||||||
|
"128k",
|
||||||
|
"-flags",
|
||||||
|
"+global_header",
|
||||||
|
"-f",
|
||||||
|
"mpegts",
|
||||||
|
"srt://127.0.0.1:40052"
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(enc_cmd, test_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn video_dual_audio_multi_filter_stream() {
|
fn video_dual_audio_multi_filter_stream() {
|
||||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||||
@ -1044,6 +1160,7 @@ fn video_audio_text_filter_stream() {
|
|||||||
config.out.mode = Stream;
|
config.out.mode = Stream;
|
||||||
config.processing.add_logo = false;
|
config.processing.add_logo = false;
|
||||||
config.processing.audio_tracks = 1;
|
config.processing.audio_tracks = 1;
|
||||||
|
config.text.add_text = true;
|
||||||
config.text.fontfile = String::new();
|
config.text.fontfile = String::new();
|
||||||
config.out.output_count = 2;
|
config.out.output_count = 2;
|
||||||
config.out.output_cmd = Some(vec_strings![
|
config.out.output_cmd = Some(vec_strings![
|
||||||
@ -1497,7 +1614,7 @@ fn multi_video_audio_hls() {
|
|||||||
"-i",
|
"-i",
|
||||||
"./assets/with_audio.mp4",
|
"./assets/with_audio.mp4",
|
||||||
"-filter_complex",
|
"-filter_complex",
|
||||||
"[0:v:0]scale=1024:576,realtime=speed=1[vout0];[0:a:0]anull[aout0];[vout0]split=2[v1_out][v2];[v2]scale=w=512:h=288[v2_out];[aout0]asplit=2[a1][a2]",
|
"[0:v:0]scale=1024:576,realtime=speed=1,split=2[v1_out][v2];[v2]scale=w=512:h=288[v2_out];[0:a]asplit=2[a1][a2]",
|
||||||
"-map",
|
"-map",
|
||||||
"[v1_out]",
|
"[v1_out]",
|
||||||
"-map",
|
"-map",
|
||||||
@ -1613,7 +1730,7 @@ fn multi_video_multi_audio_hls() {
|
|||||||
"-i",
|
"-i",
|
||||||
"./assets/dual_audio.mp4",
|
"./assets/dual_audio.mp4",
|
||||||
"-filter_complex",
|
"-filter_complex",
|
||||||
"[0:v:0]scale=1024:576,realtime=speed=1[vout0];[0:a:0]anull[aout0];[0:a:1]anull[aout1];[vout0]split=2[v1_out][v2];[v2]scale=w=512:h=288[v2_out];[aout0]asplit=2[a_0_1][a_0_2];[aout1]asplit=2[a_1_1][a_1_2]",
|
"[0:v:0]scale=1024:576,realtime=speed=1,split=2[v1_out][v2];[v2]scale=w=512:h=288[v2_out];[0:a:0]anull,asplit=2[a_0_1][a_0_2];[0:a:1]anull,asplit=2[a_1_1][a_1_2]",
|
||||||
"-map",
|
"-map",
|
||||||
"[v1_out]",
|
"[v1_out]",
|
||||||
"-map",
|
"-map",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user