From 5af37c51877601ea9c60110e15b656a88dba88d1 Mon Sep 17 00:00:00 2001 From: jb-alvarado Date: Wed, 16 Feb 2022 18:27:03 +0100 Subject: [PATCH] rearange file structur, add desktop play --- ffplayout.yml => assets/ffplayout.yml | 0 src/main.rs | 14 ++-- src/output/desktop.rs | 92 +++++++++++++++++++++++++++ src/output/mod.rs | 3 + src/{ => utils}/arg_parse.rs | 17 +++-- src/{ => utils}/config.rs | 57 +++++++++++++++-- src/{ => utils}/folder.rs | 21 +++--- src/{json.rs => utils/json_reader.rs} | 10 +-- src/{utils.rs => utils/mod.rs} | 28 +++++--- src/utils/playlist.rs | 36 +++++++++++ 10 files changed, 234 insertions(+), 44 deletions(-) rename ffplayout.yml => assets/ffplayout.yml (100%) create mode 100644 src/output/desktop.rs create mode 100644 src/output/mod.rs rename src/{ => utils}/arg_parse.rs (78%) rename src/{ => utils}/config.rs (70%) rename src/{ => utils}/folder.rs (93%) rename src/{json.rs => utils/json_reader.rs} (89%) rename src/{utils.rs => utils/mod.rs} (68%) create mode 100644 src/utils/playlist.rs diff --git a/ffplayout.yml b/assets/ffplayout.yml similarity index 100% rename from ffplayout.yml rename to assets/ffplayout.yml diff --git a/src/main.rs b/src/main.rs index f25019ec..658a8156 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,16 @@ -mod arg_parse; -mod config; +mod output; mod utils; -// mod folder; -mod json; + +use crate::output::desktop; +use crate::utils::get_config; fn main() { - let config = config::get_config(); + let config = get_config(); // println!("{:#?}", config); // folder::walk(&config.storage.path, config.storage.shuffle, &config.storage.extensions); - json::read(&config); + // read_json(&config); // let args = arg_parse::get_args(); @@ -21,4 +21,6 @@ fn main() { // println!("{:#?}", utils::get_sec()); // println!("{:#?}", utils::get_timestamp()); + + desktop::play(config.processing.settings).expect("Play on desktop failed!"); } diff --git a/src/output/desktop.rs b/src/output/desktop.rs new file mode 100644 index 00000000..5ece3e0d --- /dev/null +++ b/src/output/desktop.rs @@ -0,0 +1,92 @@ +// use std::io::prelude::*; +use std::{ + io, + process::{Command, Stdio}, +}; + +use crate::utils::program; + +pub fn play(_settings: Option>) -> io::Result<()> { + let get_source = program(); + + let mut enc_proc = Command::new("ffplay") + .args([ + "-hide_banner", + "-nostats", + "-v", + "level+error", + "-i", + "pipe:0", + ]) + .stdin(Stdio::piped()) + // .stderr(Stdio::piped()) + .spawn() + .unwrap(); + + // let mut stdin = enc_proc.stdin.unwrap(); + // let mut buffer = vec![0; 65376]; + + if let Some(mut enc_input) = enc_proc.stdin.take() { + for node in get_source { + println!("Play: {}", node.source); + + let mut dec_proc = Command::new("ffmpeg") + .args([ + "-v", + "level+error", + "-hide_banner", + "-nostats", + "-i", + &node.source, + "-pix_fmt", + "yuv420p", + "-r", + "25", + "-c:v", + "mpeg2video", + "-g", + "1", + "-b:v", + "50000k", + "-minrate", + "50000k", + "-maxrate", + "50000k", + "-bufsize", + "25000k", + "-c:a", + "s302m", + "-strict", + "-2", + "-ar", + "48000", + "-ac", + "2", + "-f", + "mpegts", + "-", + ]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap(); + + if let Some(mut dec_output) = dec_proc.stdout.take() { + io::copy(&mut dec_output, &mut enc_input).expect("Write to streaming pipe failed!"); + + dec_proc.wait()?; + let dec_output = dec_proc.wait_with_output()?; + + if dec_output.stderr.len() > 0 { + println!("[Encoder] {}", String::from_utf8(dec_output.stderr).unwrap()); + } + } + } + + enc_proc.wait()?; + let enc_output = enc_proc.wait_with_output()?; + println!("[Encoder] {}", String::from_utf8(enc_output.stderr).unwrap()); + } + + Ok(()) +} diff --git a/src/output/mod.rs b/src/output/mod.rs new file mode 100644 index 00000000..5862a5be --- /dev/null +++ b/src/output/mod.rs @@ -0,0 +1,3 @@ +pub mod desktop; + +pub use desktop::play; diff --git a/src/arg_parse.rs b/src/utils/arg_parse.rs similarity index 78% rename from src/arg_parse.rs rename to src/utils/arg_parse.rs index b3d1dfd5..3ff09d72 100644 --- a/src/arg_parse.rs +++ b/src/utils/arg_parse.rs @@ -17,19 +17,24 @@ pub struct Args { #[clap(short, long, help = "path from playlist")] pub playlist: Option, - #[clap(short, long, - help = "start time in 'hh:mm:ss', 'now' for start with first")] + #[clap( + short, + long, + help = "start time in 'hh:mm:ss', 'now' for start with first" + )] pub start: Option, - #[clap(short = 't', - long, help = "set length in 'hh:mm:ss', 'none' for no length check")] + #[clap( + short = 't', + long, + help = "set length in 'hh:mm:ss', 'none' for no length check" + )] pub length: Option, #[clap(short, long, help = "loop playlist infinitely")] pub infinit: bool, - #[clap(short, long, - help = "set output mode: desktop, hls, stream")] + #[clap(short, long, help = "set output mode: desktop, hls, stream")] pub output: Option, #[clap(short, long, help = "set audio volume")] diff --git a/src/config.rs b/src/utils/config.rs similarity index 70% rename from src/config.rs rename to src/utils/config.rs index a931f842..0575218c 100644 --- a/src/config.rs +++ b/src/utils/config.rs @@ -1,10 +1,9 @@ use serde::{Deserialize, Serialize}; use serde_yaml::{self}; -use std::fs::File; -use std::path::Path; +use std::{fs::File, path::Path, process}; // use regex::Regex; -use crate::arg_parse; +use crate::utils::get_args; #[derive(Debug, Serialize, Deserialize)] pub struct Config { @@ -62,6 +61,7 @@ pub struct Processing { pub loud_lra: f32, pub output_count: u32, pub volume: String, + pub settings: Option>, } #[derive(Debug, Serialize, Deserialize)] @@ -105,18 +105,61 @@ pub struct Out { } pub fn get_config() -> Config { - let args = arg_parse::get_args(); + let args = get_args(); let mut config_path: String = "ffplayout.yml".to_string(); if args.config.is_some() { config_path = args.config.unwrap(); - } else if Path::new("/etc/ffplayout/ffplayout.yml").exists() { + } else if Path::new("/etc/ffplayout/ffplayout.yml").is_file() { config_path = "/etc/ffplayout/ffplayout.yml".to_string(); } + if !Path::new(&config_path).is_file() { + println!( + "{} '{config_path}'\n{}", + "ffplayout config doesn't exists:", + "Put 'ffplayout.yml' in '/etc/playout/' or beside the executable!" + ); + process::exit(0x0100); + } + let f = File::open(config_path).expect("Could not open config file."); - let mut config: Config = serde_yaml::from_reader(f) - .expect("Could not read config file."); + let mut config: Config = serde_yaml::from_reader(f).expect("Could not read config file."); + + let fps = config.processing.fps.to_string(); + let bitrate = config.processing.width * config.processing.height / 10; + + let settings: Vec = vec![ + "-pix_fmt", + "yuv420p", + "-r", + &fps, + "-c:v", + "mpeg2video", + "-g", + "1", + "-b:v", + format!("{}k", bitrate).as_str(), + "-minrate", + format!("{}k", bitrate).as_str(), + "-maxrate", + format!("{}k", bitrate).as_str(), + "-bufsize", + format!("{}k", bitrate / 2).as_str(), + "-c:a", + "s302m", + "-strict", + "-2", + "-ar", + "48000", + "-ac", + "2", + "-f", + "mpegts", + "-", + ].iter().map(|&s|s.into()).collect(); + + config.processing.settings = Some(settings); if args.playlist.is_some() { config.playlist.path = args.playlist.unwrap(); diff --git a/src/folder.rs b/src/utils/folder.rs similarity index 93% rename from src/folder.rs rename to src/utils/folder.rs index 33d365d4..1f58b4c3 100644 --- a/src/folder.rs +++ b/src/utils/folder.rs @@ -1,13 +1,14 @@ use notify::DebouncedEvent::{Create, Remove, Rename}; use notify::{watcher, RecursiveMode, Watcher}; -use rand::seq::SliceRandom; -use rand::thread_rng; -use std::ffi::OsStr; -use std::path::Path; -use std::process; -use std::sync::mpsc::{channel, Receiver, TryRecvError}; -use std::time::Duration; -use std::{thread, time}; +use rand::(seq::SliceRandom, thread_rng); +use std::{ + ffi::OsStr, + path::Path, + process, + sync::mpsc::{channel, Receiver, TryRecvError}, + thread, time, + time::Duration, +}; use walkdir::WalkDir; #[derive(Clone)] @@ -78,12 +79,12 @@ fn watch_folder(source: &mut Source, receiver: &Receiver ); } _ => (), - } + }, Err(err) => { if err == TryRecvError::Disconnected { println!("Folder watch error: {:?}", err) } - }, + } } } diff --git a/src/json.rs b/src/utils/json_reader.rs similarity index 89% rename from src/json.rs rename to src/utils/json_reader.rs index 7a0bdd44..616d3714 100644 --- a/src/json.rs +++ b/src/utils/json_reader.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use std::fs::File; use std::path::Path; -use crate::config::Config; +use crate::utils::Config; use crate::utils::{get_date, modified_time}; #[derive(Debug, Serialize, Deserialize)] @@ -12,7 +12,7 @@ pub struct Playlist { pub program: Vec, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct Program { #[serde(rename = "in")] pub seek: f32, @@ -22,7 +22,7 @@ pub struct Program { pub source: String, } -pub fn read(config: &Config) -> Playlist { +pub fn read_json(config: &Config) -> Playlist { let mut playlist_path = Path::new(&config.playlist.path).to_owned(); let start = &config.playlist.day_start; @@ -48,8 +48,6 @@ pub fn read(config: &Config) -> Playlist { ); let modify = modified_time(playlist_path.as_path().display().to_string()); - println!("{:?}", modify); - let f = File::open(playlist_path).expect("Could not open json playlist file."); let mut playlist: Playlist = serde_json::from_reader(f).expect("Could not read json playlist file."); @@ -58,7 +56,5 @@ pub fn read(config: &Config) -> Playlist { playlist.modified = Some(modify.unwrap().to_string()); } - println!("{:#?}", playlist); - playlist } diff --git a/src/utils.rs b/src/utils/mod.rs similarity index 68% rename from src/utils.rs rename to src/utils/mod.rs index af9a5216..73410ef4 100644 --- a/src/utils.rs +++ b/src/utils/mod.rs @@ -2,12 +2,17 @@ use chrono::prelude::*; use chrono::Duration; use std::fs::metadata; -pub fn get_sec() -> f64 { - let local: DateTime = Local::now(); +mod arg_parse; +mod config; +// mod folder; +mod json_reader; +mod playlist; - (local.hour() * 3600 + local.minute() * 60 + local.second() - ) as f64 + (local.nanosecond() as f64 / 1000000000.0) -} +pub use arg_parse::get_args; +pub use config::{Config, get_config}; +// pub use folder::walk; +pub use json_reader::read_json; +pub use playlist::program; // pub fn get_timestamp() -> i64 { // let local: DateTime = Local::now(); @@ -15,15 +20,22 @@ pub fn get_sec() -> f64 { // local.timestamp_millis() as i64 // } +pub fn get_sec() -> f64 { + let local: DateTime = Local::now(); + + (local.hour() * 3600 + local.minute() * 60 + local.second()) as f64 + + (local.nanosecond() as f64 / 1000000000.0) +} + pub fn get_date(seek: bool, start: f64, next: f64) -> String { let local: DateTime = Local::now(); if seek && start > get_sec() { - return (local - Duration::days(1)).format("%Y-%m-%d").to_string() + return (local - Duration::days(1)).format("%Y-%m-%d").to_string(); } if start == 0.0 && next >= 86400.0 { - return (local + Duration::days(1)).format("%Y-%m-%d").to_string() + return (local + Duration::days(1)).format("%Y-%m-%d").to_string(); } local.format("%Y-%m-%d").to_string() @@ -34,7 +46,7 @@ pub fn modified_time(path: String) -> Option> { if let Ok(time) = metadata.modified() { let date_time: DateTime = time.into(); - return Some(date_time) + return Some(date_time); } None diff --git a/src/utils/playlist.rs b/src/utils/playlist.rs new file mode 100644 index 00000000..3599546b --- /dev/null +++ b/src/utils/playlist.rs @@ -0,0 +1,36 @@ +use crate::utils::get_config; +use crate::utils::json_reader::{Program, read_json}; + +pub struct CurrentProgram { + nodes: Vec, + idx: usize, +} + +impl Iterator for CurrentProgram { + type Item = Program; + + fn next(&mut self) -> Option { + if self.idx == self.nodes.len() - 1 { + let current = self.nodes[self.idx].clone(); + self.idx = 0; + + Some(current) + } else { + let current = self.nodes[self.idx].clone(); + self.idx += 1; + + Some(current) + } + } +} + +pub fn program() -> CurrentProgram { + let config = get_config(); + + let program: Vec = read_json(&config).program; + + CurrentProgram { + nodes: program, + idx: 0, + } +}