seek in on validation
This commit is contained in:
parent
ea83160ba6
commit
85619c0633
@ -19,9 +19,9 @@ use ffplayout::{
|
||||
};
|
||||
|
||||
use ffplayout_lib::utils::{
|
||||
folder::fill_filler_list, generate_playlist, get_date, import::import_file, init_logging,
|
||||
is_remote, send_mail, test_tcp_port, validate_ffmpeg, validate_playlist, JsonPlaylist,
|
||||
OutputMode::*, PlayerControl, PlayoutStatus, ProcessControl,
|
||||
errors::ProcError, folder::fill_filler_list, generate_playlist, get_date, import::import_file,
|
||||
init_logging, is_remote, send_mail, test_tcp_port, validate_ffmpeg, validate_playlist,
|
||||
JsonPlaylist, OutputMode::*, PlayerControl, PlayoutStatus, ProcessControl,
|
||||
};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
@ -44,7 +44,7 @@ struct StatusData {
|
||||
/// we save the time difference, so we stay in sync.
|
||||
///
|
||||
/// When file not exists we create it, and when it exists we get its values.
|
||||
fn status_file(stat_file: &str, playout_stat: &PlayoutStatus) {
|
||||
fn status_file(stat_file: &str, playout_stat: &PlayoutStatus) -> Result<(), ProcError> {
|
||||
debug!("Start ffplayout v{VERSION}, status file path: <b><magenta>{stat_file}</></b>");
|
||||
|
||||
if !PathBuf::from(stat_file).exists() {
|
||||
@ -53,23 +53,19 @@ fn status_file(stat_file: &str, playout_stat: &PlayoutStatus) {
|
||||
"date": String::new(),
|
||||
});
|
||||
|
||||
let json: String = serde_json::to_string(&data).expect("Serialize status data failed");
|
||||
let json: String = serde_json::to_string(&data)?;
|
||||
if let Err(e) = fs::write(stat_file, json) {
|
||||
error!("Unable to write to status file <b><magenta>{stat_file}</></b>: {e}");
|
||||
};
|
||||
} else {
|
||||
let stat_file = File::options()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.open(stat_file)
|
||||
.expect("Could not open status file");
|
||||
|
||||
let data: StatusData =
|
||||
serde_json::from_reader(stat_file).expect("Could not read status file.");
|
||||
let stat_file = File::options().read(true).write(false).open(stat_file)?;
|
||||
let data: StatusData = serde_json::from_reader(stat_file)?;
|
||||
|
||||
*playout_stat.time_shift.lock().unwrap() = data.time_shift;
|
||||
*playout_stat.date.lock().unwrap() = data.date;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set fake time for debugging.
|
||||
@ -88,14 +84,14 @@ fn fake_time(args: &Args) {
|
||||
/// Main function.
|
||||
/// Here we check the command line arguments and start the player.
|
||||
/// We also start a JSON RPC server if enabled.
|
||||
fn main() {
|
||||
fn main() -> Result<(), ProcError> {
|
||||
let args = get_args();
|
||||
|
||||
// use fake time function only in debugging mode
|
||||
#[cfg(debug_assertions)]
|
||||
fake_time(&args);
|
||||
|
||||
let mut config = get_config(args.clone());
|
||||
let mut config = get_config(args.clone())?;
|
||||
let play_control = PlayerControl::new();
|
||||
let playout_stat = PlayoutStatus::new();
|
||||
let proc_control = ProcessControl::new();
|
||||
@ -116,7 +112,7 @@ fn main() {
|
||||
}
|
||||
|
||||
let logging = init_logging(&config, Some(proc_ctl1), Some(messages.clone()));
|
||||
CombinedLogger::init(logging).unwrap();
|
||||
CombinedLogger::init(logging)?;
|
||||
|
||||
if let Err(e) = validate_ffmpeg(&mut config) {
|
||||
error!("{e}");
|
||||
@ -179,16 +175,9 @@ fn main() {
|
||||
let f = File::options()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.open(&playlist_path)
|
||||
.expect("Could not open json playlist file.");
|
||||
.open(&playlist_path)?;
|
||||
|
||||
let playlist: JsonPlaylist = match serde_json::from_reader(f) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
error!("{e:?}");
|
||||
exit(1)
|
||||
}
|
||||
};
|
||||
let playlist: JsonPlaylist = serde_json::from_reader(f)?;
|
||||
|
||||
validate_playlist(playlist, Arc::new(AtomicBool::new(false)), config);
|
||||
|
||||
@ -205,7 +194,7 @@ fn main() {
|
||||
thread::spawn(move || run_server(config_clone1, play_ctl1, play_stat, proc_ctl2));
|
||||
}
|
||||
|
||||
status_file(&config.general.stat_file, &playout_stat);
|
||||
status_file(&config.general.stat_file, &playout_stat)?;
|
||||
|
||||
debug!(
|
||||
"Use config: <b><magenta>{}</></b>",
|
||||
@ -233,4 +222,6 @@ fn main() {
|
||||
}
|
||||
|
||||
drop(msg);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
path::{Path, PathBuf},
|
||||
process::exit,
|
||||
};
|
||||
|
||||
use regex::Regex;
|
||||
@ -15,23 +14,22 @@ pub use arg_parse::Args;
|
||||
use ffplayout_lib::{
|
||||
filter::Filters,
|
||||
utils::{
|
||||
config::Template, get_sec, parse_log_level_filter, sec_to_time, time_to_sec, Media,
|
||||
OutputMode::*, PlayoutConfig, PlayoutStatus, ProcessMode::*,
|
||||
config::Template, errors::ProcError, get_sec, parse_log_level_filter, sec_to_time,
|
||||
time_to_sec, Media, OutputMode::*, PlayoutConfig, PlayoutStatus, ProcessMode::*,
|
||||
},
|
||||
vec_strings,
|
||||
};
|
||||
|
||||
/// Read command line arguments, and override the config with them.
|
||||
pub fn get_config(args: Args) -> PlayoutConfig {
|
||||
pub fn get_config(args: Args) -> Result<PlayoutConfig, ProcError> {
|
||||
let cfg_path = match args.channel {
|
||||
Some(c) => {
|
||||
let path = PathBuf::from(format!("/etc/ffplayout/{c}.yml"));
|
||||
|
||||
if !path.is_file() {
|
||||
println!(
|
||||
return Err(ProcError::Custom(format!(
|
||||
"Config file \"{c}\" under \"/etc/ffplayout/\" not found.\n\nCheck arguments!"
|
||||
);
|
||||
exit(1)
|
||||
)));
|
||||
}
|
||||
|
||||
Some(path)
|
||||
@ -53,17 +51,9 @@ pub fn get_config(args: Args) -> PlayoutConfig {
|
||||
let f = File::options()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.open(template_file)
|
||||
.expect("JSON template file");
|
||||
.open(template_file)?;
|
||||
|
||||
let mut template: Template = match serde_json::from_reader(f) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
error!("Template file not readable! {e}");
|
||||
|
||||
exit(1)
|
||||
}
|
||||
};
|
||||
let mut template: Template = serde_json::from_reader(f)?;
|
||||
|
||||
template.sources.sort_by(|d1, d2| d1.start.cmp(&d2.start));
|
||||
|
||||
@ -135,7 +125,7 @@ pub fn get_config(args: Args) -> PlayoutConfig {
|
||||
config.processing.volume = volume;
|
||||
}
|
||||
|
||||
config
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Format ingest and HLS logging output
|
||||
|
@ -12,6 +12,8 @@ pub enum ProcError {
|
||||
Custom(String),
|
||||
#[display(fmt = "Regex compile error {}", _0)]
|
||||
Regex(String),
|
||||
#[display(fmt = "Thread error {}", _0)]
|
||||
Thread(String),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for ProcError {
|
||||
@ -25,3 +27,21 @@ impl From<regex::Error> for ProcError {
|
||||
Self::Regex(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<log::SetLoggerError> for ProcError {
|
||||
fn from(err: log::SetLoggerError) -> Self {
|
||||
Self::Custom(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for ProcError {
|
||||
fn from(err: serde_json::Error) -> Self {
|
||||
Self::Custom(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<dyn std::any::Any + std::marker::Send>> for ProcError {
|
||||
fn from(err: Box<dyn std::any::Any + std::marker::Send>) -> Self {
|
||||
Self::Thread(format!("{err:?}"))
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use std::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use regex::Regex;
|
||||
@ -16,7 +17,12 @@ use crate::utils::{
|
||||
JsonPlaylist, Media, OutputMode::Null, PlayoutConfig, FFMPEG_IGNORE_ERRORS, IMAGE_FORMAT,
|
||||
};
|
||||
|
||||
/// check if ffmpeg can read the file and apply filter to it.
|
||||
/// Validate a single media file.
|
||||
///
|
||||
/// - Check if file exists
|
||||
/// - Check if ffmpeg can read the file
|
||||
/// - Check if Metadata exists
|
||||
/// - Check if the file is not silent
|
||||
fn check_media(
|
||||
mut node: Media,
|
||||
pos: usize,
|
||||
@ -32,6 +38,10 @@ fn check_media(
|
||||
|
||||
if config.logging.detect_silence {
|
||||
process_length = 15.0;
|
||||
let seek = node.duration / 4.0;
|
||||
|
||||
// Seek in file, to prevent false silence detection on intros without sound.
|
||||
enc_cmd.append(&mut vec_strings!["-ss", seek]);
|
||||
}
|
||||
|
||||
node.add_probe();
|
||||
@ -44,7 +54,7 @@ fn check_media(
|
||||
)));
|
||||
}
|
||||
|
||||
// take care, that no seek and length command is added.
|
||||
// Take care, that no seek and length command is added.
|
||||
node.seek = 0.0;
|
||||
node.out = node.duration;
|
||||
|
||||
@ -161,6 +171,7 @@ pub fn validate_playlist(
|
||||
length += begin;
|
||||
|
||||
debug!("Validate playlist from: <yellow>{date}</>");
|
||||
let timer = Instant::now();
|
||||
|
||||
for (index, item) in playlist.program.iter().enumerate() {
|
||||
if is_terminated.load(Ordering::SeqCst) {
|
||||
@ -197,5 +208,5 @@ pub fn validate_playlist(
|
||||
);
|
||||
}
|
||||
|
||||
debug!("Validation done...");
|
||||
debug!("Validation done, in {:.3?} ...", timer.elapsed(),);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user