From 3c712b00eb60e48ccef029429f0b31e27b8bd730 Mon Sep 17 00:00:00 2001 From: jb-alvarado Date: Tue, 30 Aug 2022 17:15:29 +0200 Subject: [PATCH] support custom filters, #183 --- Cargo.lock | 113 ++++++++++++++-------------- assets/ffplayout.yml | 5 +- ffplayout-engine/Cargo.toml | 2 +- ffplayout-engine/src/output/hls.rs | 2 +- ffplayout-engine/src/output/mod.rs | 11 ++- lib/Cargo.toml | 2 +- lib/src/filter/mod.rs | 114 ++++++++++++++++++++++------- lib/src/utils/config.rs | 2 + lib/src/utils/mod.rs | 15 ++-- 9 files changed, 170 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aafaab4c..41911677 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -217,7 +217,7 @@ dependencies = [ "serde_urlencoded", "smallvec", "socket2", - "time 0.3.13", + "time 0.3.14", "url", ] @@ -301,9 +301,9 @@ dependencies = [ [[package]] name = "android_system_properties" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] @@ -362,9 +362,9 @@ dependencies = [ [[package]] name = "async-global-executor" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5262ed948da60dd8956c6c5aca4d4163593dddb7b32d73267c93dab7b2e98940" +checksum = "0da5b41ee986eed3f524c380e6d64965aea573882a8907682ad100f7859305ca" dependencies = [ "async-channel", "async-executor", @@ -372,7 +372,6 @@ dependencies = [ "async-lock", "blocking", "futures-lite", - "num_cpus", "once_cell", ] @@ -502,9 +501,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "base64ct" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851" +checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474" [[package]] name = "bitflags" @@ -645,9 +644,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.17" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b" +checksum = "b15f2ea93df33549dbe2e8eecd1ca55269d63ae0b3ba1f55db030817d1c2867f" dependencies = [ "atty", "bitflags", @@ -662,9 +661,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.17" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13547f7012c01ab4a0e8f8967730ada8f9fdf419e8b6c792788f39cf4e46eefa" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck", "proc-macro-error", @@ -704,7 +703,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" dependencies = [ "percent-encoding", - "time 0.3.13", + "time 0.3.14", "version_check", ] @@ -726,9 +725,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1079fb8528d9f9c888b1e8aa651e6e079ade467323d58f75faf1d30b1808f540" +checksum = "dc948ebb96241bb40ab73effeb80d9f93afaad49359d159a5e61be51619fe813" dependencies = [ "libc", ] @@ -865,9 +864,9 @@ dependencies = [ [[package]] name = "dotenvy" -version = "0.15.1" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e851a83c30366fd01d75b913588e95e74a1705c1ecc5d58b1f8e1a6d556525f" +checksum = "da3db6fcad7c1fc4abdd99bf5276a4db30d6a819127903a709ed41e5ff016e84" dependencies = [ "dirs", ] @@ -942,7 +941,7 @@ dependencies = [ [[package]] name = "ffplayout" -version = "0.14.3" +version = "0.15.0" dependencies = [ "chrono", "clap", @@ -991,7 +990,7 @@ dependencies = [ [[package]] name = "ffplayout-lib" -version = "0.14.3" +version = "0.15.0" dependencies = [ "chrono", "crossbeam-channel", @@ -1010,7 +1009,7 @@ dependencies = [ "serde_yaml", "shlex", "simplelog", - "time 0.3.13", + "time 0.3.14", "walkdir", "winapi 0.3.9", ] @@ -1143,9 +1142,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab30e97ab6aacfe635fad58f22c2bb06c8b685f7421eb1e064a729e2a5f481fa" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" dependencies = [ "futures-channel", "futures-core", @@ -1158,9 +1157,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bfc52cbddcfd745bf1740338492bb0bd83d76c67b445f91c5fb29fae29ecaa1" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" dependencies = [ "futures-core", "futures-sink", @@ -1168,15 +1167,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" [[package]] name = "futures-executor" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d11aa21b5b587a64682c0094c2bdd4df0076c5324961a40cc3abd7f37930528" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" dependencies = [ "futures-core", "futures-task", @@ -1196,9 +1195,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93a66fc6d035a26a3ae255a6d2bca35eda63ae4c5512bef54449113f7a1228e5" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" [[package]] name = "futures-lite" @@ -1217,9 +1216,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0db9cce532b0eae2ccf2766ab246f114b56b9cf6d445e00c2549fbc100ca045d" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" dependencies = [ "proc-macro2", "quote", @@ -1228,21 +1227,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0bae1fe9752cf7fd9b0064c674ae63f97b37bc714d745cbde0afb7ec4e6765" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" [[package]] name = "futures-task" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" [[package]] name = "futures-util" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" dependencies = [ "futures-channel", "futures-core", @@ -1453,13 +1452,14 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.46" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad2bfd338099682614d3ee3fe0cd72e0b6a41ca6a87f6a74a3bd593c91650501" +checksum = "4c495f162af0bf17656d0014a0eded5f3cd2f365fdd204548c2869db89359dc7" dependencies = [ "android_system_properties", "core-foundation-sys", "js-sys", + "once_cell", "wasm-bindgen", "winapi 0.3.9", ] @@ -1727,9 +1727,9 @@ checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390" dependencies = [ "autocfg", "scopeguard", @@ -2154,10 +2154,11 @@ checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "polling" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011" dependencies = [ + "autocfg", "cfg-if 1.0.0", "libc", "log", @@ -2446,18 +2447,18 @@ checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" [[package]] name = "serde" -version = "1.0.143" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.143" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" dependencies = [ "proc-macro2", "quote", @@ -2466,9 +2467,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.83" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ "itoa", "ryu", @@ -2545,7 +2546,7 @@ dependencies = [ "num-bigint", "num-traits", "thiserror", - "time 0.3.13", + "time 0.3.14", ] [[package]] @@ -2557,7 +2558,7 @@ dependencies = [ "log", "paris", "termcolor", - "time 0.3.13", + "time 0.3.14", ] [[package]] @@ -2577,9 +2578,9 @@ checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "10c98bba371b9b22a71a9414e420f92ddeb2369239af08200816169d5e2dd7aa" dependencies = [ "libc", "winapi 0.3.9", @@ -2790,9 +2791,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db76ff9fa4b1458b3c7f077f3ff9887394058460d21e634355b273aaf11eea45" +checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b" dependencies = [ "itoa", "libc", diff --git a/assets/ffplayout.yml b/assets/ffplayout.yml index 2e09fedc..d67e89f7 100644 --- a/assets/ffplayout.yml +++ b/assets/ffplayout.yml @@ -50,7 +50,9 @@ processing: scaling. With 'logo_opacity' logo can become transparent. With 'logo_filter' 'overlay=W-w-12:12' you can modify the logo position. With 'use_loudnorm' you can activate single pass EBU R128 loudness normalization. 'loud_*' can - adjust the loudnorm filter. + adjust the loudnorm filter. With 'custom_filter' it is possible, to apply further + filters. The filter outputs should end with [c_v_out] for video filter, + and [c_a_out] for audio filter. mode: playlist width: 1024 height: 576 @@ -67,6 +69,7 @@ processing: loud_tp: -1.5 loud_lra: 11 volume: 1 + custom_filter: ingest: help_text: Run a server for a ingest stream. This stream will override the normal streaming diff --git a/ffplayout-engine/Cargo.toml b/ffplayout-engine/Cargo.toml index b862c880..b7cddfea 100644 --- a/ffplayout-engine/Cargo.toml +++ b/ffplayout-engine/Cargo.toml @@ -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.14.3" +version = "0.15.0" edition = "2021" [dependencies] diff --git a/ffplayout-engine/src/output/hls.rs b/ffplayout-engine/src/output/hls.rs index 910a236d..f2b4e388 100644 --- a/ffplayout-engine/src/output/hls.rs +++ b/ffplayout-engine/src/output/hls.rs @@ -221,7 +221,7 @@ pub fn write_hls( let dec_err = BufReader::new(enc_proc.stderr.take().unwrap()); *proc_control.decoder_term.lock().unwrap() = Some(enc_proc); - if let Err(e) = stderr_reader(dec_err, "Writer") { + if let Err(e) = stderr_reader(dec_err, "Writer", proc_control.clone()) { error!("{e:?}") }; diff --git a/ffplayout-engine/src/output/mod.rs b/ffplayout-engine/src/output/mod.rs index a9fbe320..f44faec1 100644 --- a/ffplayout-engine/src/output/mod.rs +++ b/ffplayout-engine/src/output/mod.rs @@ -64,10 +64,11 @@ pub fn player( let mut enc_writer = BufWriter::new(enc_proc.stdin.take().unwrap()); let enc_err = BufReader::new(enc_proc.stderr.take().unwrap()); - // spawn a thread to log ffmpeg output error messages - let error_encoder_thread = thread::spawn(move || stderr_reader(enc_err, "Encoder")); - *proc_control.encoder_term.lock().unwrap() = Some(enc_proc); + let enc_p_ctl = proc_control.clone(); + + // spawn a thread to log ffmpeg output error messages + let error_encoder_thread = thread::spawn(move || stderr_reader(enc_err, "Encoder", enc_p_ctl)); let proc_control_c = proc_control.clone(); let mut ingest_receiver = None; @@ -129,9 +130,11 @@ pub fn player( let mut dec_reader = BufReader::new(dec_proc.stdout.take().unwrap()); let dec_err = BufReader::new(dec_proc.stderr.take().unwrap()); - let error_decoder_thread = thread::spawn(move || stderr_reader(dec_err, "Decoder")); *proc_control.decoder_term.lock().unwrap() = Some(dec_proc); + let dec_p_ctl = proc_control.clone(); + + let error_decoder_thread = thread::spawn(move || stderr_reader(dec_err, "Decoder", dec_p_ctl)); loop { // when server is running, read from channel diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 00e39a15..f406a48b 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -4,7 +4,7 @@ description = "Library for ffplayout" license = "GPL-3.0" authors = ["Jonathan Baecker jonbae77@gmail.com"] readme = "README.md" -version = "0.14.3" +version = "0.15.0" edition = "2021" [dependencies] diff --git a/lib/src/filter/mod.rs b/lib/src/filter/mod.rs index e9beabb8..a5d7843e 100644 --- a/lib/src/filter/mod.rs +++ b/lib/src/filter/mod.rs @@ -12,6 +12,14 @@ pub mod v_overlay; use crate::utils::{get_delta, is_close, Media, MediaProbe, PlayoutConfig}; +#[derive(Clone, Copy, PartialEq)] +enum FilterType { + Audio, + Video, +} + +use FilterType::*; + #[derive(Debug, Clone)] struct Filters { audio_chain: Option, @@ -30,9 +38,9 @@ impl Filters { } } - fn add_filter(&mut self, filter: &str, codec_type: &str) { + fn add_filter(&mut self, filter: &str, codec_type: FilterType) { match codec_type { - "audio" => match &self.audio_chain { + Audio => match &self.audio_chain { Some(ac) => { if filter.starts_with(';') || filter.starts_with('[') { self.audio_chain = Some(format!("{ac}{filter}")) @@ -49,7 +57,7 @@ impl Filters { self.audio_map = "[aout1]".to_string(); } }, - "video" => match &self.video_chain { + Video => match &self.video_chain { Some(vc) => { if filter.starts_with(';') || filter.starts_with('[') { self.video_chain = Some(format!("{vc}{filter}")) @@ -62,7 +70,6 @@ impl Filters { self.video_map = "[vout1]".to_string(); } }, - _ => (), } } } @@ -70,7 +77,7 @@ impl Filters { fn deinterlace(field_order: &Option, chain: &mut Filters) { if let Some(order) = field_order { if order != "progressive" { - chain.add_filter("yadif=0:-1:0", "video") + chain.add_filter("yadif=0:-1:0", Video) } } } @@ -91,14 +98,14 @@ fn pad(aspect: f64, chain: &mut Filters, v_stream: &ffprobe::Stream, config: &Pl "{scale}pad=max(iw\\,ih*({0}/{1})):ow/({0}/{1}):(ow-iw)/2:(oh-ih)/2", config.processing.width, config.processing.height ), - "video", + Video, ) } } fn fps(fps: f64, chain: &mut Filters, config: &PlayoutConfig) { if fps != config.processing.fps { - chain.add_filter(&format!("fps={}", config.processing.fps), "video") + chain.add_filter(&format!("fps={}", config.processing.fps), Video) } } @@ -111,14 +118,14 @@ fn scale(v_stream: &ffprobe::Stream, aspect: f64, chain: &mut Filters, config: & "scale={}:{}", config.processing.width, config.processing.height ), - "video", + Video, ); } else { - chain.add_filter("null", "video"); + chain.add_filter("null", Video); } if !is_close(aspect, config.processing.aspect, 0.03) { - chain.add_filter(&format!("setdar=dar={}", config.processing.aspect), "video") + chain.add_filter(&format!("setdar=dar={}", config.processing.aspect), Video) } } else { chain.add_filter( @@ -126,16 +133,16 @@ fn scale(v_stream: &ffprobe::Stream, aspect: f64, chain: &mut Filters, config: & "scale={}:{}", config.processing.width, config.processing.height ), - "video", + Video, ); - chain.add_filter(&format!("setdar=dar={}", config.processing.aspect), "video") + chain.add_filter(&format!("setdar=dar={}", config.processing.aspect), Video) } } -fn fade(node: &mut Media, chain: &mut Filters, codec_type: &str) { +fn fade(node: &mut Media, chain: &mut Filters, codec_type: FilterType) { let mut t = ""; - if codec_type == "audio" { + if codec_type == Audio { t = "a" } @@ -171,7 +178,7 @@ fn overlay(node: &mut Media, chain: &mut Filters, config: &PlayoutConfig) { logo_chain .push_str(format!("[l];[v][l]{}:shortest=1", config.processing.logo_filter).as_str()); - chain.add_filter(&logo_chain, "video"); + chain.add_filter(&logo_chain, Video); } } @@ -189,7 +196,7 @@ fn extend_video(node: &mut Media, chain: &mut Filters) { "tpad=stop_mode=add:stop_duration={}", (node.out - node.seek) - (video_duration - node.seek) ), - "video", + Video, ) } } @@ -207,7 +214,7 @@ fn add_text( { let filter = v_drawtext::filter_node(config, Some(node), filter_chain, false); - chain.add_filter(&filter, "video"); + chain.add_filter(&filter, Video); } } @@ -224,7 +231,7 @@ fn add_audio(node: &mut Media, chain: &mut Filters) { "aevalsrc=0:channel_layout=stereo:duration={}:sample_rate=48000", node.out - node.seek ); - chain.add_filter(&audio, "audio"); + chain.add_filter(&audio, Audio); } } @@ -242,7 +249,7 @@ fn extend_audio(node: &mut Media, chain: &mut Filters) { .and_then(|a| a.parse::().ok()) { if node.out - node.seek > audio_duration - node.seek + 0.1 && node.duration >= node.out { - chain.add_filter(&format!("apad=whole_dur={}", node.out - node.seek), "audio") + chain.add_filter(&format!("apad=whole_dur={}", node.out - node.seek), Audio) } } } @@ -257,13 +264,13 @@ fn add_loudnorm(node: &mut Media, chain: &mut Filters, config: &PlayoutConfig) { .is_none() { let loud_filter = a_loudnorm::filter_node(config); - chain.add_filter(&loud_filter, "audio"); + chain.add_filter(&loud_filter, Audio); } } fn audio_volume(chain: &mut Filters, config: &PlayoutConfig) { if config.processing.volume != 1.0 { - chain.add_filter(&format!("volume={}", config.processing.volume), "audio") + chain.add_filter(&format!("volume={}", config.processing.volume), Audio) } } @@ -294,12 +301,12 @@ fn realtime_filter( node: &mut Media, chain: &mut Filters, config: &PlayoutConfig, - codec_type: &str, + codec_type: FilterType, ) { if config.general.generate.is_none() && &config.out.mode.to_lowercase() == "hls" { let mut t = ""; - if codec_type == "audio" { + if codec_type == Audio { t = "a" } @@ -322,6 +329,57 @@ fn realtime_filter( } } +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 +fn custom_filter(filter: &str, chain: &mut Filters) { + let mut video_filter = String::new(); + let mut audio_filter = String::new(); + + 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]"; + + 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); + } else { + video_filter = strip_str(f_2); + audio_filter = strip_str(f_1); + } + } + } else if filter.contains("[c_v_out]") { + video_filter = strip_str(filter); + } else if filter.contains("[c_a_out]") { + audio_filter = strip_str(filter); + } else if !filter.is_empty() { + error!("Custom filter is not well formatted, use correct out link names (\"[c_v_out]\" and/or \"[c_a_out]\"). Filter skipped!") + } + + if !video_filter.is_empty() { + chain.add_filter(&video_filter, Video); + } + + if !audio_filter.is_empty() { + chain.add_filter(&audio_filter, Audio); + } +} + pub fn filter_chains( config: &PlayoutConfig, node: &mut Media, @@ -351,14 +409,16 @@ pub fn filter_chains( } add_text(node, &mut filters, config, filter_chain); - fade(node, &mut filters, "video"); + fade(node, &mut filters, Video); overlay(node, &mut filters, config); - realtime_filter(node, &mut filters, config, "video"); + realtime_filter(node, &mut filters, config, Video); add_loudnorm(node, &mut filters, config); - fade(node, &mut filters, "audio"); + fade(node, &mut filters, Audio); audio_volume(&mut filters, config); - realtime_filter(node, &mut filters, config, "audio"); + realtime_filter(node, &mut filters, config, Audio); + + custom_filter(&config.processing.custom_filter, &mut filters); let mut filter_cmd = vec![]; let mut filter_str: String = String::new(); diff --git a/lib/src/utils/config.rs b/lib/src/utils/config.rs index 67c76340..6917b0a8 100644 --- a/lib/src/utils/config.rs +++ b/lib/src/utils/config.rs @@ -102,6 +102,8 @@ pub struct Processing { pub loud_tp: f32, pub loud_lra: f32, pub volume: f64, + #[serde(default)] + pub custom_filter: String, #[serde(skip_serializing, skip_deserializing)] pub settings: Option>, diff --git a/lib/src/utils/mod.rs b/lib/src/utils/mod.rs index e15949a1..bd04decd 100644 --- a/lib/src/utils/mod.rs +++ b/lib/src/utils/mod.rs @@ -4,7 +4,7 @@ use std::{ io::{BufRead, BufReader, Error}, net::TcpListener, path::{Path, PathBuf}, - process::{ChildStderr, Command, Stdio}, + process::{ChildStderr, Command, Stdio, exit}, sync::{Arc, Mutex}, time::{self, UNIX_EPOCH}, }; @@ -200,7 +200,7 @@ impl MediaProbe { } Err(e) => { error!( - "Can't read source {input} with ffprobe, source not exists or damaged! Error is: {e:?}" + "Can't read source {input} with ffprobe, source not exists or damaged! Error in: {e:?}" ); MediaProbe { @@ -645,7 +645,7 @@ pub fn format_log_line(line: String, level: &str) -> String { /// Read ffmpeg stderr decoder and encoder instance /// and log the output. -pub fn stderr_reader(buffer: BufReader, suffix: &str) -> Result<(), Error> { +pub fn stderr_reader(buffer: BufReader, suffix: &str, mut proc_control: ProcessControl) -> Result<(), Error> { for line in buffer.lines() { let line = line?; @@ -667,8 +667,13 @@ pub fn stderr_reader(buffer: BufReader, suffix: &str) -> Result<(), } else if line.contains("[fatal]") { error!( "[{suffix}] {}", - format_log_line(line, "fatal") - ) + format_log_line(line.clone(), "fatal") + ); + + if line.contains("Invalid argument") { + proc_control.kill_all(); + exit(1); + } } }