Merge pull request #217 from jb-alvarado/master
rearrange custom filters
This commit is contained in:
commit
2a98293ec7
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,5 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## [1.6.1](https://github.com/ffplayout/ffplayout/compare/v0.16.0...v0.16.1) (2022-10-23)
|
||||
|
||||
### ffplayout
|
||||
|
||||
- fix deb and rpm bundle [79e4d5d](https://github.com/ffplayout/ffplayout/pull/217/commits/79e4d5dda05e715df96a38070466ea7a4c8378b2)
|
||||
- rearrange custom filters (fix missing output mapping on multiple outputs) [9cb3a62](https://github.com/ffplayout/ffplayout/pull/217/commits/9cb3a6206938adcf1fbe4ce0ec763cad9e812c76)
|
||||
|
||||
## Development
|
||||
|
||||
- add subtitle example [d0ef717](https://github.com/ffplayout/ffplayout/pull/217/commits/d0ef71767b2af7d6053aeb83e3a6906fb84c984c), [e72967a](https://github.com/ffplayout/ffplayout/pull/217/commits/e72967a21c14ee8c71e18085dc397740d3586d01)
|
||||
|
||||
## [1.6.0](https://github.com/ffplayout/ffplayout/compare/v0.15.2...v0.16.0) (2022-10-19)
|
||||
|
||||
### ffplayout
|
||||
|
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -984,7 +984,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ffplayout"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
@ -1034,7 +1034,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ffplayout-lib"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossbeam-channel",
|
||||
@ -2826,7 +2826,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tests"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossbeam-channel",
|
||||
|
@ -4,7 +4,7 @@ description = "24/7 playout based on rust and ffmpeg"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
||||
readme = "README.md"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
edition = "2021"
|
||||
default-run = "ffplayout"
|
||||
|
||||
|
@ -9,8 +9,9 @@ pub mod arg_parse;
|
||||
|
||||
pub use arg_parse::Args;
|
||||
use ffplayout_lib::{
|
||||
filter::{FilterType::*, Filters},
|
||||
filter::Filters,
|
||||
utils::{time_to_sec, PlayoutConfig, ProcessMode::*},
|
||||
vec_strings,
|
||||
};
|
||||
|
||||
/// Read command line arguments, and override the config with them.
|
||||
@ -87,56 +88,6 @@ pub fn get_config(args: Args) -> PlayoutConfig {
|
||||
config
|
||||
}
|
||||
|
||||
/// Process filter from output_param
|
||||
///
|
||||
/// Split filter string and add them to the existing filtergraph.
|
||||
fn process_filters(filters: &mut Filters, output_filter: &str) {
|
||||
let re_v = Regex::new(r"(\[0:v(:[0-9]+)?\]|\[v[\w_-]+\])").unwrap(); // match video filter links
|
||||
let re_a = Regex::new(r"(\[0:a(:[0-9]+)?\]|\[a[\w_-]+\])").unwrap(); // match audio filter links
|
||||
let nr = Regex::new(r"\[[\w:-_]+([0-9])\]").unwrap(); // match filter link and get track number
|
||||
|
||||
for f in output_filter.split(';') {
|
||||
if re_v.is_match(f) {
|
||||
let filter_str = re_v.replace_all(f, "").to_string();
|
||||
filters.add_filter(&filter_str, 0, Video);
|
||||
} else if re_a.is_match(f) {
|
||||
let filter_str = re_a.replace_all(f, "").to_string();
|
||||
let track_nr = nr.replace(f, "$1").parse::<i32>().unwrap_or_default();
|
||||
filters.add_filter(&filter_str, track_nr, Audio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process filter with multiple in- or output
|
||||
///
|
||||
/// Concat filter to the existing filters and adjust filter connections.
|
||||
/// Output mapping is up to the user.
|
||||
fn process_multi_in_out(filters: &mut Filters, output_filter: &str) -> Vec<String> {
|
||||
let v_map = filters.video_map[filters.video_map.len() - 1].clone();
|
||||
let mut new_filter = format!("{}{v_map}", filters.video_chain);
|
||||
let re_v = Regex::new(r"\[0:v(:[0-9]+)?\]").unwrap(); // match video input
|
||||
let re_a = Regex::new(r"\[0:a(:(?P<num>[0-9]+))?\]").unwrap(); // match audio input, with getting track number
|
||||
let mut o_filter = re_v.replace(output_filter, v_map).to_string();
|
||||
|
||||
if !filters.audio_map.is_empty() {
|
||||
let a_map = filters.audio_map[filters.audio_map.len() - 1].clone();
|
||||
let a_filter = format!("{}{a_map}", filters.audio_chain);
|
||||
|
||||
new_filter.push(';');
|
||||
new_filter.push_str(&a_filter);
|
||||
|
||||
o_filter = re_a
|
||||
.replace_all(&o_filter, "[aout$num]")
|
||||
.to_string()
|
||||
.replace("[aout]", "[aout0]"); // when no number matched, set default 0
|
||||
}
|
||||
|
||||
new_filter.push(';');
|
||||
new_filter.push_str(&o_filter);
|
||||
|
||||
vec!["-filter_complex".to_string(), new_filter]
|
||||
}
|
||||
|
||||
/// Prepare output parameters
|
||||
///
|
||||
/// Seek for multiple outputs and add mapping for it.
|
||||
@ -147,50 +98,55 @@ pub fn prepare_output_cmd(
|
||||
) -> Vec<String> {
|
||||
let mut output_params = config.out.clone().output_cmd.unwrap();
|
||||
let mut new_params = vec![];
|
||||
let mut output_filter = String::new();
|
||||
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_multi = Regex::new(r"\[[\w:_-]+\]\[[\w:_-]+\]").unwrap(); // match multiple filter in/outputs links
|
||||
let mut count = 0;
|
||||
let re_v = Regex::new(r"\[?0:v(:0)?\]?").unwrap();
|
||||
|
||||
if let Some(mut filter) = filters.clone() {
|
||||
// Check if it contains a filtergraph and set correct output mapping.
|
||||
for (i, param) in output_params.iter().enumerate() {
|
||||
if param != "-filter_complex" {
|
||||
if i > 0 && output_params[i - 1] == "-filter_complex" {
|
||||
output_filter = param.clone();
|
||||
} else if !re_multi.is_match(&output_filter) {
|
||||
if !re_map.is_match(param)
|
||||
|| (i < output_params.len() - 2
|
||||
&& (output_params[i + 1].contains("0:s") || param.contains("0:s")))
|
||||
{
|
||||
// Skip mapping parameters, when no multi in/out filter is set.
|
||||
// Only add subtitle mapping.
|
||||
new_params.push(param.clone());
|
||||
}
|
||||
} else {
|
||||
new_params.push(param.clone());
|
||||
}
|
||||
if filter.video_out_link.len() > count && re_v.is_match(param) {
|
||||
new_params.push(filter.video_out_link[count].clone());
|
||||
} else {
|
||||
new_params.push(param.clone());
|
||||
}
|
||||
|
||||
// Check if parameter is a output
|
||||
if i > 0
|
||||
&& !param.starts_with('-')
|
||||
&& !output_params[i - 1].starts_with('-')
|
||||
&& i < output_params.len() - 1
|
||||
// Check if parameter is a output
|
||||
if i > 0
|
||||
&& !param.starts_with('-')
|
||||
&& !output_params[i - 1].starts_with('-')
|
||||
&& i < output_params.len() - 1
|
||||
{
|
||||
count += 1;
|
||||
|
||||
if filter.video_out_link.len() > count
|
||||
&& !output_params.contains(&"-map".to_string())
|
||||
{
|
||||
// add mapping to following outputs
|
||||
new_params.append(&mut filter.map().clone());
|
||||
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}")]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output_params = new_params;
|
||||
|
||||
if re_multi.is_match(&output_filter) {
|
||||
let mut split_filter = process_multi_in_out(&mut filter, &output_filter);
|
||||
cmd.append(&mut split_filter);
|
||||
} else {
|
||||
process_filters(&mut filter, &output_filter);
|
||||
cmd.append(&mut filter.cmd());
|
||||
cmd.append(&mut filter.map());
|
||||
cmd.append(&mut filter.cmd());
|
||||
|
||||
if !filter.map().iter().all(|item| output_params.contains(item))
|
||||
&& filter.output_chain.is_empty()
|
||||
&& filter.video_out_link.is_empty()
|
||||
{
|
||||
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}")]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ description = "Library for ffplayout"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
||||
readme = "README.md"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
@ -1,43 +1,36 @@
|
||||
use regex::Regex;
|
||||
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
|
||||
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 audio_filter = String::new();
|
||||
|
||||
// match chain with audio and video filter
|
||||
if filter.contains("[c_v_out]") && filter.contains("[c_a_out]") {
|
||||
let v_pos = filter.find("[c_v_out]").unwrap();
|
||||
let a_pos = filter.find("[c_a_out]").unwrap();
|
||||
let mut delimiter = "[c_v_out]";
|
||||
|
||||
// split delimiter should be first filter output link
|
||||
if v_pos > a_pos {
|
||||
delimiter = "[c_a_out]";
|
||||
}
|
||||
|
||||
if let Some((f_1, f_2)) = filter.split_once(delimiter) {
|
||||
if f_2.contains("[c_a_out]") {
|
||||
video_filter = strip_str(f_1);
|
||||
audio_filter = strip_str(f_2);
|
||||
video_filter = re.replace_all(f_1, "").to_string();
|
||||
audio_filter = re.replace_all(f_2, "").to_string();
|
||||
} else {
|
||||
video_filter = strip_str(f_2);
|
||||
audio_filter = strip_str(f_1);
|
||||
video_filter = re.replace_all(f_2, "").to_string();
|
||||
audio_filter = re.replace_all(f_1, "").to_string();
|
||||
}
|
||||
}
|
||||
} 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]") {
|
||||
audio_filter = strip_str(filter);
|
||||
audio_filter = re.replace_all(filter, "").to_string();
|
||||
} 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!")
|
||||
}
|
@ -4,19 +4,20 @@ use std::{
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use regex::Regex;
|
||||
use simplelog::*;
|
||||
|
||||
mod a_loudnorm;
|
||||
mod custom_filter;
|
||||
mod custom;
|
||||
pub mod v_drawtext;
|
||||
|
||||
// get_delta
|
||||
use self::custom_filter::custom_filter;
|
||||
use crate::utils::{
|
||||
controller::ProcessUnit::*, fps_calc, get_delta, is_close, Media, MediaProbe, OutputMode::*,
|
||||
PlayoutConfig,
|
||||
};
|
||||
|
||||
use super::vec_strings;
|
||||
|
||||
#[derive(Clone, Debug, Copy, Eq, PartialEq)]
|
||||
pub enum FilterType {
|
||||
Audio,
|
||||
@ -38,8 +39,11 @@ use FilterType::*;
|
||||
pub struct Filters {
|
||||
pub audio_chain: String,
|
||||
pub video_chain: String,
|
||||
pub output_chain: Vec<String>,
|
||||
pub audio_map: Vec<String>,
|
||||
pub video_map: Vec<String>,
|
||||
pub audio_out_link: Vec<String>,
|
||||
pub video_out_link: Vec<String>,
|
||||
pub output_map: Vec<String>,
|
||||
audio_track_count: i32,
|
||||
audio_position: i32,
|
||||
@ -53,8 +57,11 @@ impl Filters {
|
||||
Self {
|
||||
audio_chain: String::new(),
|
||||
video_chain: String::new(),
|
||||
output_chain: vec![],
|
||||
audio_map: vec![],
|
||||
video_map: vec![],
|
||||
audio_out_link: vec![],
|
||||
video_out_link: vec![],
|
||||
output_map: vec![],
|
||||
audio_track_count,
|
||||
audio_position,
|
||||
@ -102,7 +109,7 @@ impl Filters {
|
||||
|
||||
let m = format!("[{}out{track_nr}]", filter_type);
|
||||
map.push(m.clone());
|
||||
self.output_map.append(&mut vec!["-map".to_string(), m]);
|
||||
self.output_map.append(&mut vec_strings!["-map", m]);
|
||||
*last = track_nr;
|
||||
} else if filter.starts_with(';') || filter.starts_with('[') {
|
||||
chain.push_str(filter);
|
||||
@ -112,14 +119,18 @@ impl Filters {
|
||||
}
|
||||
|
||||
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 a_chain = self.audio_chain.clone();
|
||||
|
||||
if self.video_last >= 0 {
|
||||
if self.video_last >= 0 && !v_chain.ends_with(']') {
|
||||
v_chain.push_str(&format!("[vout{}]", self.video_last));
|
||||
}
|
||||
|
||||
if self.audio_last >= 0 {
|
||||
if self.audio_last >= 0 && !a_chain.ends_with(']') {
|
||||
a_chain.push_str(&format!("[aout{}]", self.audio_last));
|
||||
}
|
||||
|
||||
@ -146,7 +157,7 @@ impl Filters {
|
||||
let v_map = "0:v".to_string();
|
||||
|
||||
if !o_map.contains(&v_map) {
|
||||
o_map.append(&mut vec!["-map".to_string(), v_map]);
|
||||
o_map.append(&mut vec_strings!["-map", v_map]);
|
||||
};
|
||||
}
|
||||
|
||||
@ -155,7 +166,7 @@ impl Filters {
|
||||
let a_map = format!("{}:a:{i}", self.audio_position);
|
||||
|
||||
if !o_map.contains(&a_map) {
|
||||
o_map.append(&mut vec!["-map".to_string(), a_map]);
|
||||
o_map.append(&mut vec_strings!["-map", a_map]);
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -414,6 +425,63 @@ fn realtime(node: &mut Media, chain: &mut Filters, config: &PlayoutConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split_filter(chain: &mut Filters, count: usize, nr: i32, filter_type: FilterType) {
|
||||
if count > 1 {
|
||||
let out_link = match filter_type {
|
||||
Audio => &mut chain.audio_out_link,
|
||||
Video => &mut chain.video_out_link,
|
||||
};
|
||||
|
||||
for i in 0..count {
|
||||
let link = format!("[{filter_type}out_{nr}_{i}]");
|
||||
if !out_link.contains(&link) {
|
||||
out_link.push(link)
|
||||
}
|
||||
}
|
||||
|
||||
let split_filter = format!("split={count}{}", out_link.join(""));
|
||||
chain.add_filter(&split_filter, nr, filter_type);
|
||||
}
|
||||
}
|
||||
|
||||
/// Process output filter chain and add new filters to existing ones.
|
||||
fn process_output_filters(config: &PlayoutConfig, chain: &mut Filters, custom_filter: &str) {
|
||||
let filter =
|
||||
if (config.text.add_text && !config.text.text_from_filename) || config.out.mode == HLS {
|
||||
let re_v = Regex::new(r"\[[0:]+[v^\[]+([:0]+)?\]").unwrap(); // match video filter input link
|
||||
let _re_a = Regex::new(r"\[[0:]+[a^\[]+([:0]+)?\]").unwrap(); // match video filter input link
|
||||
let mut cf = custom_filter.to_string();
|
||||
|
||||
if !chain.video_chain.is_empty() {
|
||||
cf = re_v
|
||||
.replace(&cf, &format!("{},", chain.video_chain))
|
||||
.to_string()
|
||||
}
|
||||
|
||||
if !chain.audio_chain.is_empty() {
|
||||
let audio_split = chain
|
||||
.audio_chain
|
||||
.split(';')
|
||||
.enumerate()
|
||||
.map(|(i, p)| p.replace(&format!("[aout{i}]"), ""))
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
for i in 0..config.processing.audio_tracks {
|
||||
cf = cf.replace(
|
||||
&format!("[0:a:{i}]"),
|
||||
&format!("{},", &audio_split[i as usize]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
cf
|
||||
} else {
|
||||
custom_filter.to_string()
|
||||
};
|
||||
|
||||
chain.output_chain = vec_strings!["-filter_complex", filter]
|
||||
}
|
||||
|
||||
fn custom(filter: &str, chain: &mut Filters, nr: i32, filter_type: FilterType) {
|
||||
if !filter.is_empty() {
|
||||
chain.add_filter(filter, nr, filter_type);
|
||||
@ -430,6 +498,12 @@ pub fn filter_chains(
|
||||
if node.unit == Encoder {
|
||||
add_text(node, &mut filters, config, filter_chain);
|
||||
|
||||
if let Some(f) = config.out.output_filter.clone() {
|
||||
process_output_filters(config, &mut filters, &f)
|
||||
} else if config.out.output_count > 1 {
|
||||
split_filter(&mut filters, config.out.output_count, 0, Video);
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
@ -465,8 +539,8 @@ pub fn filter_chains(
|
||||
overlay(node, &mut filters, config);
|
||||
realtime(node, &mut filters, config);
|
||||
|
||||
let (proc_vf, proc_af) = custom_filter(&config.processing.custom_filter);
|
||||
let (list_vf, list_af) = custom_filter(&node.custom_filter);
|
||||
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);
|
||||
@ -487,6 +561,7 @@ pub fn filter_chains(
|
||||
);
|
||||
add_audio(node, &mut filters, i);
|
||||
}
|
||||
|
||||
// add at least anull filter, for correct filter construction,
|
||||
// is important for split filter in HLS mode
|
||||
filters.add_filter("anull", i, Audio);
|
||||
@ -499,5 +574,11 @@ pub fn filter_chains(
|
||||
custom(&list_af, &mut filters, i, Audio);
|
||||
}
|
||||
|
||||
if config.out.mode == HLS {
|
||||
if let Some(f) = config.out.output_filter.clone() {
|
||||
process_output_filters(config, &mut filters, &f)
|
||||
}
|
||||
}
|
||||
|
||||
filters
|
||||
}
|
||||
|
@ -229,6 +229,10 @@ pub struct Out {
|
||||
pub mode: OutputMode,
|
||||
pub output_param: String,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub output_count: usize,
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub output_filter: Option<String>,
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub output_cmd: Option<Vec<String>>,
|
||||
}
|
||||
@ -308,10 +312,30 @@ impl PlayoutConfig {
|
||||
|
||||
config.ingest.input_cmd = split(config.ingest.input_param.as_str());
|
||||
|
||||
config.out.output_count = 1;
|
||||
config.out.output_filter = None;
|
||||
|
||||
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());
|
||||
} else if let Some(mut cmd) = split(config.out.output_param.as_str()) {
|
||||
// get output count according to the var_stream_map value, or by counting output parameters
|
||||
if let Some(i) = cmd.clone().iter().position(|m| m == "-var_stream_map") {
|
||||
config.out.output_count = cmd[i + 1].split_whitespace().count();
|
||||
} else {
|
||||
config.out.output_count = cmd
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(i, p)| i > &0 && !p.starts_with('-') && !cmd[i - 1].starts_with('-'))
|
||||
.count();
|
||||
}
|
||||
|
||||
if let Some(i) = cmd.clone().iter().position(|r| r == "-filter_complex") {
|
||||
config.out.output_filter = Some(cmd[i + 1].clone());
|
||||
cmd.remove(i);
|
||||
cmd.remove(i);
|
||||
}
|
||||
|
||||
config.out.output_cmd = Some(cmd);
|
||||
}
|
||||
|
||||
// when text overlay without text_from_filename is on, turn also the RPC server on,
|
||||
|
@ -11,7 +11,7 @@ use simplelog::*;
|
||||
|
||||
use crate::utils::{
|
||||
format_log_line, loop_image, sec_to_time, seek_and_length, valid_source, vec_strings,
|
||||
JsonPlaylist, Media, PlayoutConfig, FFMPEG_IGNORE_ERRORS, IMAGE_FORMAT,
|
||||
JsonPlaylist, Media, OutputMode::Null, PlayoutConfig, FFMPEG_IGNORE_ERRORS, IMAGE_FORMAT,
|
||||
};
|
||||
|
||||
/// check if ffmpeg can read the file and apply filter to it.
|
||||
@ -23,6 +23,8 @@ fn check_media(
|
||||
) -> Result<(), Error> {
|
||||
let mut enc_cmd = vec_strings!["-hide_banner", "-nostats", "-v", "level+error"];
|
||||
let mut error_list = vec![];
|
||||
let mut config = config.clone();
|
||||
config.out.mode = Null;
|
||||
|
||||
node.add_probe();
|
||||
|
||||
@ -53,7 +55,7 @@ fn check_media(
|
||||
node.cmd = Some(seek_and_length(&node));
|
||||
}
|
||||
|
||||
node.add_filter(config, &None);
|
||||
node.add_filter(&config, &None);
|
||||
|
||||
let mut filter = node.filter.unwrap_or_default();
|
||||
|
||||
|
@ -44,7 +44,7 @@ for target in "${targets[@]}"; do
|
||||
|
||||
cp ./target/${target}/release/ffpapi.exe .
|
||||
cp ./target/${target}/release/ffplayout.exe .
|
||||
zip -r "ffplayout-v${version}_${target}.zip" assets docs public LICENSE README.md ffplayout.exe ffpapi.exe -x *.db
|
||||
zip -r "ffplayout-v${version}_${target}.zip" assets docs public LICENSE README.md CHANGELOG.md ffplayout.exe ffpapi.exe -x *.db
|
||||
rm -f ffplayout.exe ffpapi.exe
|
||||
elif [[ $target == "x86_64-apple-darwin" ]] || [[ $target == "aarch64-apple-darwin" ]]; then
|
||||
if [[ -f "ffplayout-v${version}_${target}.tar.gz" ]]; then
|
||||
@ -55,7 +55,7 @@ for target in "${targets[@]}"; do
|
||||
|
||||
cp ./target/${target}/release/ffpapi .
|
||||
cp ./target/${target}/release/ffplayout .
|
||||
tar -czvf "ffplayout-v${version}_${target}.tar.gz" --exclude='*.db' assets docs public LICENSE README.md ffplayout ffpapi
|
||||
tar -czvf "ffplayout-v${version}_${target}.tar.gz" --exclude='*.db' assets docs public LICENSE README.md CHANGELOG.md ffplayout ffpapi
|
||||
rm -f ffplayout ffpapi
|
||||
else
|
||||
if [[ -f "ffplayout-v${version}_${target}.tar.gz" ]]; then
|
||||
@ -66,14 +66,14 @@ for target in "${targets[@]}"; do
|
||||
|
||||
cp ./target/${target}/release/ffpapi .
|
||||
cp ./target/${target}/release/ffplayout .
|
||||
tar -czvf "ffplayout-v${version}_${target}.tar.gz" --exclude='*.db' assets docs public LICENSE README.md ffplayout ffpapi
|
||||
tar -czvf "ffplayout-v${version}_${target}.tar.gz" --exclude='*.db' assets docs public LICENSE README.md CHANGELOG.md ffplayout ffpapi
|
||||
rm -f ffplayout ffpapi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
done
|
||||
|
||||
if [[ -z $target ]] || [[ $target == "x86_64-unknown-linux-musl" ]]; then
|
||||
if [[ "${#targets[@]}" == "5" ]] || [[ $targets == "x86_64-unknown-linux-musl" ]]; then
|
||||
cargo deb --target=x86_64-unknown-linux-musl -p ffplayout --manifest-path=ffplayout-engine/Cargo.toml -o ffplayout_${version}_amd64.deb
|
||||
cd ffplayout-engine
|
||||
cargo generate-rpm --target=x86_64-unknown-linux-musl -o ../ffplayout-${version}-1.x86_64.rpm
|
||||
@ -81,6 +81,6 @@ if [[ -z $target ]] || [[ $target == "x86_64-unknown-linux-musl" ]]; then
|
||||
cd ..
|
||||
fi
|
||||
|
||||
if [[ -z $target ]] || [[ $target == "aarch64-unknown-linux-gnu" ]]; then
|
||||
if [[ "${#targets[@]}" == "5" ]] || [[ $targets == "aarch64-unknown-linux-gnu" ]]; then
|
||||
cargo deb --target=aarch64-unknown-linux-gnu --variant=arm64 -p ffplayout --manifest-path=ffplayout-engine/Cargo.toml -o ffplayout_${version}_arm64.deb
|
||||
fi
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tests"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
|
BIN
tests/assets/dual_audio_vtt.mkv
Normal file
BIN
tests/assets/dual_audio_vtt.mkv
Normal file
Binary file not shown.
12
tests/assets/subtitle.vtt
Normal file
12
tests/assets/subtitle.vtt
Normal file
@ -0,0 +1,12 @@
|
||||
WEBVTT
|
||||
00:00:01.500 --> 00:00:04.000
|
||||
Good Morning!
|
||||
|
||||
00:00:04.500 --> 00:00:08.300
|
||||
Good Afternoon!
|
||||
|
||||
00:00:08.500 --> 00:00:12.300
|
||||
Good Evening!
|
||||
|
||||
00:00:013.500 --> 00:00:20.300
|
||||
Good Night!
|
BIN
tests/assets/video_audio_vtt.mkv
Normal file
BIN
tests/assets/video_audio_vtt.mkv
Normal file
Binary file not shown.
@ -33,6 +33,58 @@ fn video_audio_input() {
|
||||
assert_eq!(media.filter.unwrap().map(), test_filter_map);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_audio_custom_filter1_input() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = Stream;
|
||||
config.processing.add_logo = false;
|
||||
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 = gen_source(&config, media_obj, &None);
|
||||
|
||||
let test_filter_cmd = vec_strings![
|
||||
"-filter_complex",
|
||||
"[0:v:0]scale=1024:576,gblur=2[vout0];[0:a:0]anull,volume=0.2[aout0]"
|
||||
];
|
||||
|
||||
let test_filter_map = vec_strings!["-map", "[vout0]", "-map", "[aout0]"];
|
||||
|
||||
assert_eq!(
|
||||
media.cmd,
|
||||
Some(vec_strings!["-i", "./assets/with_audio.mp4"])
|
||||
);
|
||||
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
||||
assert_eq!(media.filter.unwrap().map(), test_filter_map);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_audio_custom_filter2_input() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = Stream;
|
||||
config.processing.add_logo = false;
|
||||
config.processing.custom_filter =
|
||||
"[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 = gen_source(&config, media_obj, &None);
|
||||
|
||||
let test_filter_cmd = vec_strings![
|
||||
"-filter_complex",
|
||||
"[0:v:0]scale=1024:576,null[v];movie=logo.png[l];[v][l]overlay[vout0];[0:a:0]anull,volume=0.2[aout0]"
|
||||
];
|
||||
|
||||
let test_filter_map = vec_strings!["-map", "[vout0]", "-map", "[aout0]"];
|
||||
|
||||
assert_eq!(
|
||||
media.cmd,
|
||||
Some(vec_strings!["-i", "./assets/with_audio.mp4"])
|
||||
);
|
||||
assert_eq!(media.filter.clone().unwrap().cmd(), test_filter_cmd);
|
||||
assert_eq!(media.filter.unwrap().map(), test_filter_map);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dual_audio_aevalsrc_input() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
@ -177,14 +229,13 @@ fn video_audio_stream() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_audio_filter_stream() {
|
||||
fn video_audio_filter1_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 = false;
|
||||
config.out.output_filter = Some("[0:v]gblur=2[vout0];[0:a]volume=0.2[aout0]".to_string());
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-filter_complex",
|
||||
"[0:v]gblur=2[vout0];[0:a]volume=0.2[aout0]",
|
||||
"-map",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
@ -229,7 +280,7 @@ fn video_audio_filter_stream() {
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-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",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
@ -259,9 +310,8 @@ fn video_audio_filter2_stream() {
|
||||
config.processing.add_logo = false;
|
||||
config.text.add_text = true;
|
||||
config.text.fontfile = String::new();
|
||||
config.out.output_filter = Some("[0:v]gblur=2[vout0];[0:a]volume=0.2[aout0]".to_string());
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-filter_complex",
|
||||
"[0:v]gblur=2[vout0];[0:a]volume=0.2[aout0]",
|
||||
"-map",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
@ -313,7 +363,7 @@ fn video_audio_filter2_stream() {
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-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",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
@ -343,11 +393,15 @@ fn video_audio_filter3_stream() {
|
||||
config.processing.add_logo = false;
|
||||
config.text.add_text = true;
|
||||
config.text.fontfile = String::new();
|
||||
config.out.output_filter = Some(
|
||||
"[0:v]null[o];movie=/path/to/lower_third.png[l];[o][l]overlay=shortest=1[vout0]"
|
||||
.to_string(),
|
||||
);
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-filter_complex",
|
||||
"[0:v]null[o];movie=/path/to/lower_third.png[l];[o][l]overlay=shortest=1[v_out0]",
|
||||
"-map",
|
||||
"[v_out0]",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
"0:a",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
@ -395,9 +449,11 @@ fn video_audio_filter3_stream() {
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-filter_complex",
|
||||
format!("[0:v:0]zmq=b=tcp\\\\://'{socket}',drawtext@dyntext=text=''[vout0];[vout0]null[o];movie=/path/to/lower_third.png[l];[o][l]overlay=shortest=1[v_out0]"),
|
||||
format!("[0:v:0]zmq=b=tcp\\\\://'{socket}',drawtext@dyntext=text='',null[o];movie=/path/to/lower_third.png[l];[o][l]overlay=shortest=1[vout0]"),
|
||||
"-map",
|
||||
"[v_out0]",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
"0:a",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
@ -423,13 +479,15 @@ fn video_audio_filter4_stream() {
|
||||
config.processing.add_logo = false;
|
||||
config.text.add_text = true;
|
||||
config.text.fontfile = String::new();
|
||||
config.out.output_filter = Some(
|
||||
"[0:v]null[o];movie=/path/to/lower_third.png[l];[o][l]overlay=shortest=1[vout0];[0:a:0]volume=0.2[aout0]"
|
||||
.to_string(),
|
||||
);
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-filter_complex",
|
||||
"[0:v]null[o];movie=/path/to/lower_third.png[l];[o][l]overlay=shortest=1[v_out0];[0:a:0]volume=0.2[a_out0]",
|
||||
"-map",
|
||||
"[v_out0]",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
"[a_out0]",
|
||||
"[aout0]",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
@ -477,11 +535,11 @@ fn video_audio_filter4_stream() {
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-filter_complex",
|
||||
format!("[0:v:0]zmq=b=tcp\\\\://'{socket}',drawtext@dyntext=text=''[vout0];[vout0]null[o];movie=/path/to/lower_third.png[l];[o][l]overlay=shortest=1[v_out0];[0:a:0]volume=0.2[a_out0]"),
|
||||
format!("[0:v:0]zmq=b=tcp\\\\://'{socket}',drawtext@dyntext=text='',null[o];movie=/path/to/lower_third.png[l];[o][l]overlay=shortest=1[vout0];[0:a:0]volume=0.2[aout0]"),
|
||||
"-map",
|
||||
"[v_out0]",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
"[a_out0]",
|
||||
"[aout0]",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
@ -854,12 +912,126 @@ fn video_dual_audio_multi_stream() {
|
||||
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]
|
||||
fn video_dual_audio_multi_filter_stream() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = Stream;
|
||||
config.processing.add_logo = false;
|
||||
config.processing.audio_tracks = 2;
|
||||
config.out.output_count = 2;
|
||||
config.text.fontfile = String::new();
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-map",
|
||||
@ -936,9 +1108,9 @@ fn video_dual_audio_multi_filter_stream() {
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-filter_complex",
|
||||
format!("[0:v:0]zmq=b=tcp\\\\://'{socket}',drawtext@dyntext=text=''[vout0]"),
|
||||
format!("[0:v:0]zmq=b=tcp\\\\://'{socket}',drawtext@dyntext=text='',split=2[vout_0_0][vout_0_1]"),
|
||||
"-map",
|
||||
"[vout0]",
|
||||
"[vout_0_0]",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
@ -957,7 +1129,7 @@ fn video_dual_audio_multi_filter_stream() {
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40051",
|
||||
"-map",
|
||||
"[vout0]",
|
||||
"[vout_0_1]",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
@ -982,6 +1154,128 @@ fn video_dual_audio_multi_filter_stream() {
|
||||
assert_eq!(enc_cmd, test_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_audio_text_filter_stream() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = Stream;
|
||||
config.processing.add_logo = false;
|
||||
config.processing.audio_tracks = 1;
|
||||
config.text.add_text = true;
|
||||
config.text.fontfile = String::new();
|
||||
config.out.output_count = 2;
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-map",
|
||||
"0:v",
|
||||
"-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",
|
||||
"0:v",
|
||||
"-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"
|
||||
]);
|
||||
|
||||
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]
|
||||
fn video_audio_hls() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
@ -1254,9 +1548,11 @@ fn multi_video_audio_hls() {
|
||||
config.out.mode = HLS;
|
||||
config.processing.add_logo = false;
|
||||
config.text.add_text = false;
|
||||
config.out.output_count = 2;
|
||||
config.out.output_filter = Some(
|
||||
"[0:v]split=2[v1_out][v2];[v2]scale=w=512:h=288[v2_out];[0:a]asplit=2[a1][a2]".to_string(),
|
||||
);
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-filter_complex",
|
||||
"[0:v]split=2[v1_out][v2];[v2]scale=w=512:h=288[v2_out];[0:a]asplit=2[a1][a2]",
|
||||
"-map",
|
||||
"[v1_out]",
|
||||
"-map",
|
||||
@ -1318,7 +1614,7 @@ fn multi_video_audio_hls() {
|
||||
"-i",
|
||||
"./assets/with_audio.mp4",
|
||||
"-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",
|
||||
"[v1_out]",
|
||||
"-map",
|
||||
@ -1366,9 +1662,9 @@ fn multi_video_multi_audio_hls() {
|
||||
config.processing.add_logo = false;
|
||||
config.processing.audio_tracks = 2;
|
||||
config.text.add_text = false;
|
||||
config.out.output_count = 2;
|
||||
config.out.output_filter = Some("[0:v]split=2[v1_out][v2];[v2]scale=w=512:h=288[v2_out];[0:a:0]asplit=2[a_0_1][a_0_2];[0:a:1]asplit=2[a_1_1][a_1_2]".to_string());
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-filter_complex",
|
||||
"[0:v]split=2[v1_out][v2];[v2]scale=w=512:h=288[v2_out];[0:a:0]asplit=2[a_0_1][a_0_2];[0:a:1]asplit=2[a_1_1][a_1_2]",
|
||||
"-map",
|
||||
"[v1_out]",
|
||||
"-map",
|
||||
@ -1434,7 +1730,7 @@ fn multi_video_multi_audio_hls() {
|
||||
"-i",
|
||||
"./assets/dual_audio.mp4",
|
||||
"-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",
|
||||
"[v1_out]",
|
||||
"-map",
|
||||
|
Loading…
Reference in New Issue
Block a user