seek in on validation

This commit is contained in:
jb-alvarado 2023-11-21 10:39:43 +01:00
parent ea83160ba6
commit 85619c0633
4 changed files with 59 additions and 47 deletions

View File

@ -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(())
}

View File

@ -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

View File

@ -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:?}"))
}
}

View File

@ -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(),);
}