- update to chrono 0.4.20.rc.1
- preserve text filter in HLS mode when clips changes
- cross compile ffpapi for macOS
This commit is contained in:
jb-alvarado 2022-07-27 17:58:56 +02:00
parent 9b054c8ce3
commit 32454e41d6
27 changed files with 194 additions and 134 deletions

92
Cargo.lock generated
View File

@ -463,9 +463,9 @@ dependencies = [
[[package]]
name = "atoi"
version = "0.4.0"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5"
checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e"
dependencies = [
"num-traits",
]
@ -629,33 +629,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.19"
version = "0.4.20-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time 0.1.44",
"winapi 0.3.9",
]
[[package]]
name = "chrono"
version = "0.4.19"
source = "git+https://github.com/sbrocket/chrono?branch=parse-error-kind-public#d9cac30bd0fb46ed410718e6c83753a7c9daa669"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time 0.1.44",
"winapi 0.3.9",
]
[[package]]
name = "chrono"
version = "0.4.20-beta.1"
source = "git+https://github.com/chronotope/chrono.git#acd4ecf09fd0e5e35e2b5d5e074f6e1cc77172fc"
checksum = "856628f00a013eb30ae7b136b20fba37f7a39f8026d6f735dfac07c6fce1b8cf"
dependencies = [
"num-integer",
"num-traits",
@ -777,18 +753,18 @@ dependencies = [
[[package]]
name = "crc"
version = "2.1.0"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23"
checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "1.1.1"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403"
checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff"
[[package]]
name = "crc32fast"
@ -1038,7 +1014,7 @@ dependencies = [
[[package]]
name = "ffplayout"
version = "0.12.0"
version = "0.13.0"
dependencies = [
"clap",
"crossbeam-channel 0.5.6",
@ -1056,7 +1032,7 @@ dependencies = [
[[package]]
name = "ffplayout-api"
version = "0.5.0"
version = "0.5.1"
dependencies = [
"actix-files",
"actix-multipart",
@ -1064,7 +1040,7 @@ dependencies = [
"actix-web-grants",
"actix-web-httpauth",
"argon2",
"chrono 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono",
"clap",
"derive_more",
"faccess",
@ -1086,9 +1062,9 @@ dependencies = [
[[package]]
name = "ffplayout-lib"
version = "0.12.0"
version = "0.13.0"
dependencies = [
"chrono 0.4.20-beta.1",
"chrono",
"crossbeam-channel 0.5.6",
"ffprobe",
"file-rotate",
@ -1121,10 +1097,11 @@ dependencies = [
[[package]]
name = "file-rotate"
version = "0.6.0"
source = "git+https://github.com/Ploppz/file-rotate.git?branch=timestamp-parse-fix#cb1874a15a7a18de820a57df48d3513e5a4076f4"
version = "0.7.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8453b56141b15cd55cac61ce49326799d31fe4a602ad41f9f5b9b88a42ecbdcf"
dependencies = [
"chrono 0.4.19 (git+https://github.com/sbrocket/chrono?branch=parse-error-kind-public)",
"chrono",
"flate2",
]
@ -1427,26 +1404,20 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.11.2"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash 0.7.6",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashlink"
version = "0.7.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
checksum = "d452c155cb93fecdfb02a73dd57b5d8e442c2063bd7aac72f1bc5e4263a43086"
dependencies = [
"hashbrown 0.11.2",
"hashbrown",
]
[[package]]
@ -1579,7 +1550,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
"hashbrown",
]
[[package]]
@ -2761,9 +2732,9 @@ dependencies = [
[[package]]
name = "sqlx"
version = "0.5.13"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "551873805652ba0d912fec5bbb0f8b4cdd96baf8e2ebf5970e5671092966019b"
checksum = "1f82cbe94f41641d6c410ded25bbf5097c240cefdf8e3b06d04198d0a96af6a4"
dependencies = [
"sqlx-core",
"sqlx-macros",
@ -2771,16 +2742,15 @@ dependencies = [
[[package]]
name = "sqlx-core"
version = "0.5.13"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5"
checksum = "6b69bf218860335ddda60d6ce85ee39f6cf6e5630e300e19757d1de15886a093"
dependencies = [
"ahash 0.7.6",
"atoi",
"bitflags",
"byteorder",
"bytes",
"chrono 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)",
"crc",
"crossbeam-queue 0.3.6",
"either",
@ -2814,9 +2784,9 @@ dependencies = [
[[package]]
name = "sqlx-macros"
version = "0.5.13"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1"
checksum = "f40c63177cf23d356b159b60acd27c54af7423f1736988502e36bae9a712118f"
dependencies = [
"dotenv",
"either",
@ -2833,9 +2803,9 @@ dependencies = [
[[package]]
name = "sqlx-rt"
version = "0.5.13"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae"
checksum = "874e93a365a598dc3dadb197565952cb143ae4aa716f7bcc933a8d836f6bf89f"
dependencies = [
"actix-rt",
"native-tls",

View File

@ -3,7 +3,7 @@
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
The ffplayout apps are mostly made to run on Linux as system services. But in general they should run on all platforms which are supported by Rust. At the moment the cross compiled version from *ffpapi* runs on Windows and Linux, and not on Mac. If it is needed there, it should be compile natively.
The ffplayout apps are mostly made to run on Linux as system services. But in general they should run on all platforms which are supported by Rust.
Check the [releases](https://github.com/ffplayout/ffplayout/releases/latest) for pre compiled version.

View File

@ -116,8 +116,9 @@ text:
out:
help_text: The final playout compression. Set the settings to your needs. 'mode'
has the options 'desktop', 'hls', 'null', 'stream'. Use 'stream' and adjust 'output_param:' settings,
when you want to stream to a rtmp/rtsp/srt/... server.
has the options 'desktop', 'hls', 'null', 'stream'. Use 'stream' and adjust
'output_param:' settings, when you want to stream to a rtmp/rtsp/srt/... server.
In production don't server hls playlist with ffpapi, use nginx or another web server!
mode: hls
output_param: >-
-c:v libx264

View File

@ -184,7 +184,7 @@ curl -X POST http://127.0.0.1:8787/api/control/1/text/ \
- reset
```BASH
curl -X POST http://127.0.0.1:8787/api/control/1/playout/next/ -H 'Content-Type: application/json'
curl -X POST http://127.0.0.1:8787/api/control/1/playout/ -H 'Content-Type: application/json'
-d '{ "command": "reset" }' -H 'Authorization: <TOKEN>'
```

View File

@ -4,7 +4,7 @@ description = "Rest API for ffplayout"
license = "GPL-3.0"
authors = ["Jonathan Baecker jonbae77@gmail.com"]
readme = "README.md"
version = "0.5.0"
version = "0.5.1"
edition = "2021"
[dependencies]
@ -15,7 +15,7 @@ actix-web = "4"
actix-web-grants = "3"
actix-web-httpauth = "0.6"
argon2 = "0.4"
chrono = "0.4"
chrono = "0.4.20-rc.1"
clap = { version = "3.2", features = ["derive"] }
derive_more = "0.99"
faccess = "0.2"
@ -31,8 +31,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.8"
simplelog = { version = "^0.12", features = ["paris"] }
sqlx = { version = "0.5", features = [
"chrono",
sqlx = { version = "0.6", features = [
"runtime-actix-native-tls",
"sqlite"
] }

View File

@ -80,7 +80,7 @@ async fn main() -> std::io::Result<()> {
info!("running ffplayout API, listen on {conn}");
// TODO: add allow origin (or give it to the proxy)
// no allow origin here, give it to the reverse proxy
HttpServer::new(move || {
let auth = HttpAuthentication::bearer(validator);
App::new()

View File

@ -482,7 +482,7 @@ pub async fn send_text_message(
/// - reset
///
/// ```BASH
/// curl -X POST http://127.0.0.1:8787/api/control/1/playout/next/ -H 'Content-Type: application/json'
/// curl -X POST http://127.0.0.1:8787/api/control/1/playout/ -H 'Content-Type: application/json'
/// -d '{ "command": "reset" }' -H 'Authorization: <TOKEN>'
/// ```
#[post("/control/{id}/playout/")]

View File

@ -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.12.0"
version = "0.13.0"
edition = "2021"
[dependencies]

View File

@ -2,6 +2,7 @@ use std::{
io::{BufRead, BufReader, Error, Read},
process::{ChildStderr, Command, Stdio},
sync::atomic::Ordering,
sync::{Arc, Mutex},
thread,
};
@ -78,7 +79,7 @@ pub fn ingest_server(
let stream_input = config.ingest.input_cmd.clone().unwrap();
server_cmd.append(&mut stream_input.clone());
server_cmd.append(&mut filter_cmd(&config));
server_cmd.append(&mut filter_cmd(&config, &Arc::new(Mutex::new(vec![]))));
server_cmd.append(&mut config.processing.settings.unwrap());
let mut is_running;

View File

@ -38,7 +38,7 @@ pub fn source_generator(
);
let config_clone = config.clone();
let folder_source = FolderSource::new(&config, current_list, index);
let folder_source = FolderSource::new(&config, playout_stat.chain, current_list, index);
let node_clone = folder_source.nodes.clone();
// Spawn a thread to monitor folder for file changes.

View File

@ -254,7 +254,8 @@ impl CurrentProgram {
let mut node_clone = self.nodes.lock().unwrap()[index].clone();
node_clone.seek = time_sec - node_clone.begin.unwrap();
self.current_node = handle_list_init(&self.config, node_clone);
self.current_node =
handle_list_init(&self.config, node_clone, &self.playout_stat.chain);
}
}
}
@ -307,7 +308,7 @@ impl Iterator for CurrentProgram {
media.duration = duration;
media.out = duration;
self.current_node = gen_source(&self.config, media);
self.current_node = gen_source(&self.config, media, &self.playout_stat.chain);
let mut nodes = self.nodes.lock().unwrap();
nodes.push(self.current_node.clone());
self.index.store(nodes.len(), Ordering::SeqCst);
@ -364,12 +365,17 @@ impl Iterator for CurrentProgram {
}
self.current_node.duration = duration;
self.current_node.out = duration;
self.current_node = gen_source(&self.config, self.current_node.clone());
self.current_node = gen_source(
&self.config,
self.current_node.clone(),
&self.playout_stat.chain,
);
self.nodes.lock().unwrap().push(self.current_node.clone());
self.last_next_ad();
self.current_node.last_ad = last_ad;
self.current_node.add_filter(&self.config);
self.current_node
.add_filter(&self.config, &self.playout_stat.chain);
self.index.fetch_add(1, Ordering::SeqCst);
@ -377,7 +383,11 @@ impl Iterator for CurrentProgram {
}
self.index.store(0, Ordering::SeqCst);
self.current_node = gen_source(&self.config, self.nodes.lock().unwrap()[0].clone());
self.current_node = gen_source(
&self.config,
self.nodes.lock().unwrap()[0].clone(),
&self.playout_stat.chain,
);
self.last_next_ad();
self.current_node.last_ad = last_ad;
@ -432,20 +442,24 @@ fn timed_source(
|| !config.playlist.length.contains(':')
{
// when we are in the 24 hour range, get the clip
new_node = gen_source(config, node);
new_node = gen_source(config, node, &playout_stat.chain);
new_node.process = Some(true);
} else if total_delta <= 0.0 {
info!("Begin is over play time, skip: {}", node.source);
} else if total_delta < node.duration - node.seek || last {
new_node = handle_list_end(node, total_delta);
new_node.add_filter(config);
new_node.add_filter(config, &playout_stat.chain);
}
new_node
}
/// Generate the source CMD, or when clip not exist, get a dummy.
fn gen_source(config: &PlayoutConfig, mut node: Media) -> Media {
fn gen_source(
config: &PlayoutConfig,
mut node: Media,
filter_chain: &Arc<Mutex<Vec<String>>>,
) -> Media {
if valid_source(&node.source) {
node.add_probe();
node.cmd = Some(seek_and_length(
@ -454,7 +468,7 @@ fn gen_source(config: &PlayoutConfig, mut node: Media) -> Media {
node.out,
node.duration,
));
node.add_filter(config);
node.add_filter(config, filter_chain);
} else {
if node.source.is_empty() {
warn!(
@ -467,7 +481,7 @@ fn gen_source(config: &PlayoutConfig, mut node: Media) -> Media {
let (source, cmd) = gen_dummy(config, node.out - node.seek);
node.source = source;
node.cmd = Some(cmd);
node.add_filter(config);
node.add_filter(config, filter_chain);
}
node
@ -475,7 +489,11 @@ fn gen_source(config: &PlayoutConfig, mut node: Media) -> Media {
/// Handle init clip, but this clip can be the last one in playlist,
/// this we have to figure out and calculate the right length.
fn handle_list_init(config: &PlayoutConfig, mut node: Media) -> Media {
fn handle_list_init(
config: &PlayoutConfig,
mut node: Media,
filter_chain: &Arc<Mutex<Vec<String>>>,
) -> Media {
debug!("Playlist init");
let (_, total_delta) = get_delta(config, &node.begin.unwrap());
let mut out = node.out;
@ -485,7 +503,7 @@ fn handle_list_init(config: &PlayoutConfig, mut node: Media) -> Media {
}
node.out = out;
gen_source(config, node)
gen_source(config, node, filter_chain)
}
/// when we come to last clip in playlist,

View File

@ -1,9 +1,12 @@
use std::process::{self, Command, Stdio};
use std::{
process::{self, Command, Stdio},
sync::{Arc, Mutex},
};
use simplelog::*;
use ffplayout_lib::filter::v_drawtext;
use ffplayout_lib::utils::{Media, PlayoutConfig};
use ffplayout_lib::utils::PlayoutConfig;
use ffplayout_lib::vec_strings;
/// Desktop Output
@ -23,7 +26,7 @@ pub fn output(config: &PlayoutConfig, log_format: &str) -> process::Child {
let mut filter: String = "null,".to_string();
filter.push_str(
v_drawtext::filter_node(config, &Media::new(0, String::new(), false)).as_str(),
v_drawtext::filter_node(config, None, &Arc::new(Mutex::new(vec![]))).as_str(),
);
enc_filter = vec!["-vf".to_string(), filter];
}

View File

@ -47,7 +47,7 @@ fn ingest_to_hls_server(
let mut server_prefix = vec_strings!["-hide_banner", "-nostats", "-v", "level+info"];
let mut stream_input = config.ingest.input_cmd.clone().unwrap();
server_prefix.append(&mut stream_input);
let server_filter = filter_cmd(&config);
let server_filter = filter_cmd(&config, &playout_stat.chain);
let server_cmd = prepare_output_cmd(
server_prefix,

View File

@ -1,10 +1,11 @@
use std::process::{self, Command, Stdio};
use std::{
process::{self, Command, Stdio},
sync::{Arc, Mutex},
};
use simplelog::*;
use ffplayout_lib::filter::v_drawtext;
use ffplayout_lib::utils::{Media, PlayoutConfig};
use ffplayout_lib::vec_strings;
use ffplayout_lib::{filter::v_drawtext, utils::PlayoutConfig, vec_strings};
/// Desktop Output
///
@ -34,7 +35,7 @@ pub fn output(config: &PlayoutConfig, log_format: &str) -> process::Child {
let mut filter: String = "null,".to_string();
filter.push_str(
v_drawtext::filter_node(config, &Media::new(0, String::new(), false)).as_str(),
v_drawtext::filter_node(config, None, &Arc::new(Mutex::new(vec![]))).as_str(),
);
enc_filter = vec!["-vf".to_string(), filter];
}

View File

@ -1,9 +1,12 @@
use std::process::{self, Command, Stdio};
use std::{
process::{self, Command, Stdio},
sync::{Arc, Mutex},
};
use simplelog::*;
use ffplayout_lib::filter::v_drawtext;
use ffplayout_lib::utils::{prepare_output_cmd, Media, PlayoutConfig};
use ffplayout_lib::utils::{prepare_output_cmd, PlayoutConfig};
use ffplayout_lib::vec_strings;
/// Streaming Output
@ -34,7 +37,7 @@ pub fn output(config: &PlayoutConfig, log_format: &str) -> process::Child {
let mut filter = "[0:v]null,".to_string();
filter.push_str(
v_drawtext::filter_node(config, &Media::new(0, String::new(), false)).as_str(),
v_drawtext::filter_node(config, None, &Arc::new(Mutex::new(vec![]))).as_str(),
);
enc_filter = vec!["-filter_complex".to_string(), filter];

View File

@ -86,8 +86,12 @@ pub fn json_rpc_server(
let mut filter = get_filter_from_json(map["message"].to_string());
let socket = config.text.bind_address.clone();
// TODO: in Rust 1.64 use let_chains instead
if !filter.is_empty() && config.text.bind_address.is_some() {
let mut clips_filter = playout_stat.chain.lock().unwrap();
*clips_filter = vec![filter.clone()];
filter = format!("Parsed_drawtext_2 reinit {filter}");
if let Ok(reply) = executor::block_on(zmq_send(&filter, &socket.unwrap())) {
return Ok(Value::String(reply));
};

View File

@ -4,14 +4,14 @@ description = "Library for ffplayout"
license = "GPL-3.0"
authors = ["Jonathan Baecker jonbae77@gmail.com"]
readme = "README.md"
version = "0.12.0"
version = "0.13.0"
edition = "2021"
[dependencies]
chrono = { git = "https://github.com/chronotope/chrono.git" }
chrono = "0.4.20-rc.1"
crossbeam-channel = "0.5"
ffprobe = "0.3"
file-rotate = { git = "https://github.com/Ploppz/file-rotate.git", branch = "timestamp-parse-fix" }
file-rotate = "0.7.0-rc.0"
jsonrpc-http-server = "18.0"
lettre = "0.10"
log = "0.4"

View File

@ -1,4 +1,6 @@
use crate::filter::{a_loudnorm, v_overlay};
use std::sync::{Arc, Mutex};
use crate::filter::{a_loudnorm, v_drawtext, v_overlay};
use crate::utils::PlayoutConfig;
/// Audio Filter
@ -22,7 +24,7 @@ fn audio_filter(config: &PlayoutConfig) -> String {
}
/// Create filter nodes for ingest live stream.
pub fn filter_cmd(config: &PlayoutConfig) -> Vec<String> {
pub fn filter_cmd(config: &PlayoutConfig, filter_chain: &Arc<Mutex<Vec<String>>>) -> Vec<String> {
let mut filter = format!(
"[0:v]fps={},scale={}:{},setdar=dar={},fade=in:st=0:d=0.5",
config.processing.fps,
@ -32,12 +34,18 @@ pub fn filter_cmd(config: &PlayoutConfig) -> Vec<String> {
);
let overlay = v_overlay::filter_node(config, true);
let drawtext = v_drawtext::filter_node(config, None, filter_chain);
if !overlay.is_empty() {
filter.push(',');
}
if !drawtext.is_empty() {
filter.push(',');
}
filter.push_str(&overlay);
filter.push_str(&drawtext);
filter.push_str("[vout1]");
filter.push_str(audio_filter(config).as_str());

View File

@ -1,4 +1,7 @@
use std::path::Path;
use std::{
path::Path,
sync::{Arc, Mutex},
};
use simplelog::*;
@ -100,7 +103,9 @@ fn scale(v_stream: &ffprobe::Stream, aspect: f64, chain: &mut Filters, config: &
config.processing.width, config.processing.height
),
"video",
)
);
} else {
chain.add_filter("null", "video");
}
if !is_close(aspect, config.processing.aspect, 0.03) {
@ -183,11 +188,16 @@ fn extend_video(node: &mut Media, chain: &mut Filters) {
}
/// add drawtext filter for lower thirds messages
fn add_text(node: &mut Media, chain: &mut Filters, config: &PlayoutConfig) {
fn add_text(
node: &mut Media,
chain: &mut Filters,
config: &PlayoutConfig,
filter_chain: &Arc<Mutex<Vec<String>>>,
) {
if config.text.add_text
&& (config.text.text_from_filename || config.out.mode.to_lowercase() == "hls")
{
let filter = v_drawtext::filter_node(config, node);
let filter = v_drawtext::filter_node(config, Some(node), filter_chain);
chain.add_filter(&filter, "video");
}
@ -298,7 +308,11 @@ fn realtime_filter(
}
}
pub fn filter_chains(config: &PlayoutConfig, node: &mut Media) -> Vec<String> {
pub fn filter_chains(
config: &PlayoutConfig,
node: &mut Media,
filter_chain: &Arc<Mutex<Vec<String>>>,
) -> Vec<String> {
let mut filters = Filters::new();
if let Some(probe) = node.probe.as_ref() {
@ -324,7 +338,7 @@ pub fn filter_chains(config: &PlayoutConfig, node: &mut Media) -> Vec<String> {
extend_audio(node, &mut filters);
}
add_text(node, &mut filters, config);
add_text(node, &mut filters, config, filter_chain);
fade(node, &mut filters, "video");
overlay(node, &mut filters, config);
realtime_filter(node, &mut filters, config, "video");

View File

@ -1,10 +1,17 @@
use std::path::Path;
use std::{
path::Path,
sync::{Arc, Mutex},
};
use regex::Regex;
use crate::utils::{Media, PlayoutConfig};
pub fn filter_node(config: &PlayoutConfig, node: &Media) -> String {
pub fn filter_node(
config: &PlayoutConfig,
node: Option<&Media>,
filter_chain: &Arc<Mutex<Vec<String>>>,
) -> String {
let mut filter = String::new();
let mut font = String::new();
@ -13,8 +20,12 @@ pub fn filter_node(config: &PlayoutConfig, node: &Media) -> String {
font = format!(":fontfile='{}'", config.text.fontfile)
}
if config.text.text_from_filename {
let source = node.source.clone();
// TODO: in Rust 1.64 use let_chains instead
if config.text.text_from_filename && node.is_some() {
let source = node
.unwrap_or(&Media::new(0, String::new(), false))
.source
.clone();
let regex: Regex = Regex::new(&config.text.regex).unwrap();
let text: String = match regex.captures(&source) {
@ -28,8 +39,17 @@ pub fn filter_node(config: &PlayoutConfig, node: &Media) -> String {
.replace(':', "\\:");
filter = format!("drawtext=text='{escape}':{}{font}", config.text.style)
} else if let Some(socket) = config.text.bind_address.clone() {
let chain = filter_chain.lock().unwrap();
let mut filter_cmd = format!("text=''{font}");
if !chain.is_empty() {
if let Some(link) = chain.iter().find(|&l| l.contains("text")) {
filter_cmd = link.to_string();
}
}
filter = format!(
"zmq=b=tcp\\\\://'{}',drawtext=text=''{font}",
"zmq=b=tcp\\\\://'{}',drawtext={filter_cmd}",
socket.replace(':', "\\:")
)
}

View File

@ -178,19 +178,21 @@ impl Default for PlayerControl {
/// Global playout control, for move forward/backward clip, or resetting playlist/state.
#[derive(Clone, Debug)]
pub struct PlayoutStatus {
pub time_shift: Arc<Mutex<f64>>,
pub date: Arc<Mutex<String>>,
pub chain: Arc<Mutex<Vec<String>>>,
pub current_date: Arc<Mutex<String>>,
pub date: Arc<Mutex<String>>,
pub list_init: Arc<AtomicBool>,
pub time_shift: Arc<Mutex<f64>>,
}
impl PlayoutStatus {
pub fn new() -> Self {
Self {
time_shift: Arc::new(Mutex::new(0.0)),
date: Arc::new(Mutex::new(String::new())),
chain: Arc::new(Mutex::new(vec![])),
current_date: Arc::new(Mutex::new(String::new())),
date: Arc::new(Mutex::new(String::new())),
list_init: Arc::new(AtomicBool::new(true)),
time_shift: Arc::new(Mutex::new(0.0)),
}
}
}

View File

@ -19,6 +19,7 @@ use crate::utils::{file_extension, get_sec, Media, PlayoutConfig};
#[derive(Debug, Clone)]
pub struct FolderSource {
config: PlayoutConfig,
filter_chain: Arc<Mutex<Vec<String>>>,
pub nodes: Arc<Mutex<Vec<Media>>>,
current_node: Media,
index: Arc<AtomicUsize>,
@ -27,6 +28,7 @@ pub struct FolderSource {
impl FolderSource {
pub fn new(
config: &PlayoutConfig,
filter_chain: Arc<Mutex<Vec<String>>>,
current_list: Arc<Mutex<Vec<Media>>>,
global_index: Arc<AtomicUsize>,
) -> Self {
@ -77,6 +79,7 @@ impl FolderSource {
Self {
config: config.clone(),
filter_chain,
nodes: current_list,
current_node: Media::new(0, String::new(), false),
index: global_index,
@ -114,7 +117,8 @@ impl Iterator for FolderSource {
let i = self.index.load(Ordering::SeqCst);
self.current_node = self.nodes.lock().unwrap()[i].clone();
self.current_node.add_probe();
self.current_node.add_filter(&self.config);
self.current_node
.add_filter(&self.config, &self.filter_chain);
self.current_node.begin = Some(get_sec());
self.index.fetch_add(1, Ordering::SeqCst);
@ -137,7 +141,8 @@ impl Iterator for FolderSource {
self.current_node = self.nodes.lock().unwrap()[0].clone();
self.current_node.add_probe();
self.current_node.add_filter(&self.config);
self.current_node
.add_filter(&self.config, &self.filter_chain);
self.current_node.begin = Some(get_sec());
self.index.store(1, Ordering::SeqCst);

View File

@ -93,7 +93,7 @@ pub fn generate_playlist(
date_range = get_date_range(&date_range)
}
let media_list = FolderSource::new(config, current_list, index);
let media_list = FolderSource::new(config, Arc::new(Mutex::new(vec![])), current_list, index);
let list_length = media_list.nodes.lock().unwrap().len();
for date in date_range {

View File

@ -37,7 +37,7 @@ pub fn validate_playlist(
if probe.format.is_none() {
error!(
"No Metadata from file <b><magenta>{}</></b> at <yellow>{}</>",
"No Metadata at <yellow>{}</>, from file <b><magenta>{}</></b>",
sec_to_time(begin),
item.source
);

View File

@ -229,6 +229,8 @@ pub fn init_logging(
),
ContentLimit::Time(TimeFrequency::Daily),
Compression::None,
#[cfg(unix)]
None,
);
app_logger.push(WriteLogger::new(LevelFilter::Debug, file_config, log_file));

View File

@ -5,6 +5,7 @@ use std::{
net::TcpListener,
path::Path,
process::{ChildStderr, Command, Stdio},
sync::{Arc, Mutex},
time::{self, UNIX_EPOCH},
};
@ -125,9 +126,9 @@ impl Media {
}
}
pub fn add_filter(&mut self, config: &PlayoutConfig) {
pub fn add_filter(&mut self, config: &PlayoutConfig, filter_chain: &Arc<Mutex<Vec<String>>>) {
let mut node = self.clone();
self.filter = Some(filter_chains(config, &mut node))
self.filter = Some(filter_chains(config, &mut node, filter_chain))
}
}
@ -537,6 +538,11 @@ pub fn stderr_reader(buffer: BufReader<ChildStderr>, suffix: &str) -> Result<(),
"<bright black>[{suffix}]</> {}",
format_log_line(line, "warning")
)
} else if line.contains("[error]") {
error!(
"<bright black>[{suffix}]</> {}",
format_log_line(line, "error")
)
}
}

View File

@ -1,13 +1,15 @@
#!/usr/bin/bash
cd ffplayout-frontend
if [[ ! -d public ]]; then
cd ffplayout-frontend
npm install
npm run build
yes | rm -rf ../public
mv dist ../public
npm install
npm run build
yes | rm -rf ../public
mv dist ../public
cd ..
cd ..
fi
targets=("x86_64-unknown-linux-musl" "aarch64-unknown-linux-gnu" "x86_64-pc-windows-gnu" "x86_64-apple-darwin" "aarch64-apple-darwin")
@ -41,11 +43,12 @@ for target in "${targets[@]}"; do
rm -f "ffplayout-v${version}_${target}.tar.gz"
fi
cargo build --release --target=$target --bin ffplayout
CC="x86_64-apple-darwin20.4-cc" cargo build --release --target=$target
cp ./target/${target}/release/ffpapi .
cp ./target/${target}/release/ffplayout .
tar -czvf "ffplayout-v${version}_${target}.tar.gz" --exclude='*.db' assets docs LICENSE README.md ffplayout
rm -f ffplayout
tar -czvf "ffplayout-v${version}_${target}.tar.gz" --exclude='*.db' assets docs public LICENSE README.md ffplayout ffpapi
rm -f ffplayout ffpapi
else
if [[ -f "ffplayout-v${version}_${target}.tar.gz" ]]; then
rm -f "ffplayout-v${version}_${target}.tar.gz"
@ -55,7 +58,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 LICENSE README.md ffplayout ffpapi
tar -czvf "ffplayout-v${version}_${target}.tar.gz" --exclude='*.db' assets docs public LICENSE README.md ffplayout ffpapi
rm -f ffplayout ffpapi
fi