no global PlayoutStatus
This commit is contained in:
parent
c239e952b8
commit
21d656131b
@ -4,7 +4,7 @@ use simplelog::*;
|
||||
|
||||
pub mod v_drawtext;
|
||||
|
||||
use crate::utils::{get_delta, is_close, GlobalConfig, Media};
|
||||
use crate::utils::{get_delta, is_close, GlobalConfig, Media, PlayoutStatus};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Filters {
|
||||
@ -290,7 +290,7 @@ fn realtime_filter(
|
||||
chain: &mut Filters,
|
||||
config: &GlobalConfig,
|
||||
codec_type: String,
|
||||
json_date: &String
|
||||
playout_stat: &PlayoutStatus,
|
||||
) {
|
||||
// this realtime filter is important for HLS output to stay in sync
|
||||
|
||||
@ -302,7 +302,7 @@ fn realtime_filter(
|
||||
|
||||
if config.out.mode.to_lowercase() == "hls".to_string() {
|
||||
let mut speed_filter = format!("{t}realtime=speed=1");
|
||||
let (delta, _) = get_delta(&node.begin.unwrap(), &json_date, true);
|
||||
let (delta, _) = get_delta(&node.begin.unwrap(), &playout_stat, true);
|
||||
let duration = node.out - node.seek;
|
||||
|
||||
if delta < 0.0 {
|
||||
@ -317,7 +317,7 @@ fn realtime_filter(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filter_chains(node: &mut Media, json_date: &String) -> Vec<String> {
|
||||
pub fn filter_chains(node: &mut Media, playout_stat: &PlayoutStatus) -> Vec<String> {
|
||||
let config = GlobalConfig::global();
|
||||
|
||||
let mut filters = Filters::new();
|
||||
@ -355,12 +355,12 @@ pub fn filter_chains(node: &mut Media, json_date: &String) -> Vec<String> {
|
||||
add_text(node, &mut filters, &config);
|
||||
fade(node, &mut filters, "video".into());
|
||||
overlay(node, &mut filters, &config);
|
||||
realtime_filter(node, &mut filters, &config, "video".into(), &json_date);
|
||||
realtime_filter(node, &mut filters, &config, "video".into(), &playout_stat);
|
||||
|
||||
add_loudnorm(node, &mut filters, &config);
|
||||
fade(node, &mut filters, "audio".into());
|
||||
audio_volume(&mut filters, &config);
|
||||
realtime_filter(node, &mut filters, &config, "audio".into(), &json_date);
|
||||
realtime_filter(node, &mut filters, &config, "audio".into(), &playout_stat);
|
||||
|
||||
let mut filter_cmd = vec![];
|
||||
let mut filter_str: String = "".to_string();
|
||||
|
@ -12,7 +12,7 @@ use std::{
|
||||
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::utils::{get_sec, GlobalConfig, Media};
|
||||
use crate::utils::{get_sec, GlobalConfig, Media, PlayoutStatus};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Source {
|
||||
@ -20,10 +20,15 @@ pub struct Source {
|
||||
pub nodes: Arc<Mutex<Vec<Media>>>,
|
||||
current_node: Media,
|
||||
index: Arc<Mutex<usize>>,
|
||||
playout_stat: PlayoutStatus,
|
||||
}
|
||||
|
||||
impl Source {
|
||||
pub fn new(current_list: Arc<Mutex<Vec<Media>>>, global_index: Arc<Mutex<usize>>) -> Self {
|
||||
pub fn new(
|
||||
current_list: Arc<Mutex<Vec<Media>>>,
|
||||
global_index: Arc<Mutex<usize>>,
|
||||
playout_stat: PlayoutStatus,
|
||||
) -> Self {
|
||||
let config = GlobalConfig::global();
|
||||
let mut media_list = vec![];
|
||||
let mut index: usize = 0;
|
||||
@ -69,6 +74,7 @@ impl Source {
|
||||
nodes: current_list,
|
||||
current_node: Media::new(0, "".to_string(), false),
|
||||
index: global_index,
|
||||
playout_stat,
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,7 +91,10 @@ impl Source {
|
||||
}
|
||||
|
||||
fn sort(&mut self) {
|
||||
self.nodes.lock().unwrap().sort_by(|d1, d2| d1.source.cmp(&d2.source));
|
||||
self.nodes
|
||||
.lock()
|
||||
.unwrap()
|
||||
.sort_by(|d1, d2| d1.source.cmp(&d2.source));
|
||||
let mut index: usize = 0;
|
||||
|
||||
for item in self.nodes.lock().unwrap().iter_mut() {
|
||||
@ -104,7 +113,7 @@ impl Iterator for Source {
|
||||
let i = *self.index.lock().unwrap();
|
||||
self.current_node = self.nodes.lock().unwrap()[i].clone();
|
||||
self.current_node.add_probe();
|
||||
self.current_node.add_filter(&"".to_string());
|
||||
self.current_node.add_filter(&self.playout_stat);
|
||||
self.current_node.begin = Some(get_sec());
|
||||
|
||||
*self.index.lock().unwrap() += 1;
|
||||
@ -121,7 +130,7 @@ impl Iterator for Source {
|
||||
|
||||
self.current_node = self.nodes.lock().unwrap()[0].clone();
|
||||
self.current_node.add_probe();
|
||||
self.current_node.add_filter(&"".to_string());
|
||||
self.current_node.add_filter(&self.playout_stat);
|
||||
self.current_node.begin = Some(get_sec());
|
||||
|
||||
*self.index.lock().unwrap() = 1;
|
||||
|
@ -1,8 +1,10 @@
|
||||
use std::{
|
||||
fs,
|
||||
path::Path,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use serde_json::json;
|
||||
use simplelog::*;
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
@ -24,23 +26,31 @@ pub struct CurrentProgram {
|
||||
index: Arc<Mutex<usize>>,
|
||||
rt_handle: Handle,
|
||||
is_terminated: Arc<Mutex<bool>>,
|
||||
playout_stat: PlayoutStatus,
|
||||
}
|
||||
|
||||
impl CurrentProgram {
|
||||
pub fn new(
|
||||
rt_handle: Handle,
|
||||
playout_stat: PlayoutStatus,
|
||||
is_terminated: Arc<Mutex<bool>>,
|
||||
current_list: Arc<Mutex<Vec<Media>>>,
|
||||
global_index: Arc<Mutex<usize>>,
|
||||
) -> Self {
|
||||
let config = GlobalConfig::global();
|
||||
let status = PlayoutStatus::global();
|
||||
let json = read_json(None, rt_handle.clone(), is_terminated.clone(), true, 0.0);
|
||||
|
||||
*current_list.lock().unwrap() = json.program;
|
||||
*playout_stat.current_date.lock().unwrap() = json.date.clone();
|
||||
|
||||
if status.date != json.date {
|
||||
status.clone().write(json.date.clone(), 0.0)
|
||||
if *playout_stat.date.lock().unwrap() != json.date {
|
||||
let data = json!({
|
||||
"time_shift": 0.0,
|
||||
"date": json.date,
|
||||
});
|
||||
|
||||
let json: String = serde_json::to_string(&data).expect("Serialize status data failed");
|
||||
fs::write(config.general.stat_file.clone(), &json).expect("Unable to write file");
|
||||
}
|
||||
|
||||
Self {
|
||||
@ -55,6 +65,7 @@ impl CurrentProgram {
|
||||
index: global_index,
|
||||
rt_handle,
|
||||
is_terminated,
|
||||
playout_stat,
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,7 +132,7 @@ impl CurrentProgram {
|
||||
let current_time = get_sec();
|
||||
let start_sec = self.config.playlist.start_sec.unwrap();
|
||||
let target_length = self.config.playlist.length_sec.unwrap();
|
||||
let (delta, total_delta) = get_delta(¤t_time, &self.json_date, true);
|
||||
let (delta, total_delta) = get_delta(¤t_time, &self.playout_stat, true);
|
||||
let mut duration = self.current_node.out.clone();
|
||||
|
||||
if self.current_node.duration > self.current_node.out {
|
||||
@ -142,8 +153,14 @@ impl CurrentProgram {
|
||||
next_start,
|
||||
);
|
||||
|
||||
let status = PlayoutStatus::global();
|
||||
status.clone().write(json.date.clone(), 0.0);
|
||||
let data = json!({
|
||||
"time_shift": 0.0,
|
||||
"date": json.date,
|
||||
});
|
||||
|
||||
*self.playout_stat.current_date.lock().unwrap() = json.date.clone();
|
||||
let status_data: String = serde_json::to_string(&data).expect("Serialize status data failed");
|
||||
fs::write(self.config.general.stat_file.clone(), &status_data).expect("Unable to write file");
|
||||
|
||||
self.json_path = json.current_file.clone();
|
||||
self.json_mod = json.modified;
|
||||
@ -210,7 +227,7 @@ impl CurrentProgram {
|
||||
*self.index.lock().unwrap() += 1;
|
||||
|
||||
node_clone.seek = time_sec - node_clone.begin.unwrap();
|
||||
self.current_node = handle_list_init(node_clone, &self.json_date);
|
||||
self.current_node = handle_list_init(node_clone, &self.playout_stat);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -245,7 +262,7 @@ impl Iterator for CurrentProgram {
|
||||
self.init_clip();
|
||||
} else {
|
||||
let mut current_time = get_sec();
|
||||
let (_, total_delta) = get_delta(¤t_time, &self.json_date, true);
|
||||
let (_, total_delta) = get_delta(¤t_time, &self.playout_stat, true);
|
||||
let mut duration = DUMMY_LEN;
|
||||
|
||||
if DUMMY_LEN > total_delta {
|
||||
@ -261,7 +278,7 @@ impl Iterator for CurrentProgram {
|
||||
media.duration = duration;
|
||||
media.out = duration;
|
||||
|
||||
self.current_node = gen_source(media, &self.json_date);
|
||||
self.current_node = gen_source(media, &self.playout_stat);
|
||||
self.nodes.lock().unwrap().push(self.current_node.clone());
|
||||
*self.index.lock().unwrap() = self.nodes.lock().unwrap().len();
|
||||
}
|
||||
@ -285,7 +302,7 @@ impl Iterator for CurrentProgram {
|
||||
self.nodes.lock().unwrap()[index].clone(),
|
||||
&self.config,
|
||||
is_last,
|
||||
&self.json_date,
|
||||
&self.playout_stat,
|
||||
);
|
||||
self.last_next_ad();
|
||||
*self.index.lock().unwrap() += 1;
|
||||
@ -300,7 +317,7 @@ impl Iterator for CurrentProgram {
|
||||
self.check_for_next_playlist();
|
||||
let (_, total_delta) = get_delta(
|
||||
&self.config.playlist.start_sec.unwrap(),
|
||||
&self.json_date,
|
||||
&self.playout_stat,
|
||||
true,
|
||||
);
|
||||
|
||||
@ -319,12 +336,12 @@ impl Iterator for CurrentProgram {
|
||||
}
|
||||
self.current_node.duration = duration;
|
||||
self.current_node.out = duration;
|
||||
self.current_node = gen_source(self.current_node.clone(), &self.json_date);
|
||||
self.current_node = gen_source(self.current_node.clone(), &self.playout_stat);
|
||||
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.json_date);
|
||||
self.current_node.add_filter(&self.playout_stat);
|
||||
|
||||
*self.index.lock().unwrap() += 1;
|
||||
|
||||
@ -332,7 +349,8 @@ impl Iterator for CurrentProgram {
|
||||
}
|
||||
|
||||
*self.index.lock().unwrap() = 0;
|
||||
self.current_node = gen_source(self.nodes.lock().unwrap()[0].clone(), &self.json_date);
|
||||
self.current_node =
|
||||
gen_source(self.nodes.lock().unwrap()[0].clone(), &self.playout_stat);
|
||||
self.last_next_ad();
|
||||
self.current_node.last_ad = last_ad;
|
||||
|
||||
@ -343,12 +361,17 @@ impl Iterator for CurrentProgram {
|
||||
}
|
||||
}
|
||||
|
||||
fn timed_source(node: Media, config: &GlobalConfig, last: bool, json_date: &String) -> Media {
|
||||
fn timed_source(
|
||||
node: Media,
|
||||
config: &GlobalConfig,
|
||||
last: bool,
|
||||
playout_stat: &PlayoutStatus,
|
||||
) -> Media {
|
||||
// prepare input clip
|
||||
// check begin and length from clip
|
||||
// return clip only if we are in 24 hours time range
|
||||
|
||||
let (delta, total_delta) = get_delta(&node.begin.unwrap(), &json_date, true);
|
||||
let (delta, total_delta) = get_delta(&node.begin.unwrap(), &playout_stat, true);
|
||||
let mut new_node = node.clone();
|
||||
new_node.process = Some(false);
|
||||
|
||||
@ -369,7 +392,7 @@ fn timed_source(node: Media, config: &GlobalConfig, last: bool, json_date: &Stri
|
||||
|| !config.playlist.length.contains(":")
|
||||
{
|
||||
// when we are in the 24 hour range, get the clip
|
||||
new_node = gen_source(node, &json_date);
|
||||
new_node = gen_source(node, &playout_stat);
|
||||
new_node.process = Some(true);
|
||||
} else if total_delta <= 0.0 {
|
||||
info!("Begin is over play time, skip: {}", node.source);
|
||||
@ -380,7 +403,7 @@ fn timed_source(node: Media, config: &GlobalConfig, last: bool, json_date: &Stri
|
||||
new_node
|
||||
}
|
||||
|
||||
fn gen_source(mut node: Media, json_date: &String) -> Media {
|
||||
fn gen_source(mut node: Media, playout_stat: &PlayoutStatus) -> Media {
|
||||
if Path::new(&node.source).is_file() {
|
||||
node.add_probe();
|
||||
node.cmd = Some(seek_and_length(
|
||||
@ -389,7 +412,7 @@ fn gen_source(mut node: Media, json_date: &String) -> Media {
|
||||
node.out,
|
||||
node.duration,
|
||||
));
|
||||
node.add_filter(&json_date);
|
||||
node.add_filter(&playout_stat);
|
||||
} else {
|
||||
if node.source.chars().count() == 0 {
|
||||
warn!(
|
||||
@ -402,17 +425,17 @@ fn gen_source(mut node: Media, json_date: &String) -> Media {
|
||||
let (source, cmd) = gen_dummy(node.out - node.seek);
|
||||
node.source = source;
|
||||
node.cmd = Some(cmd);
|
||||
node.add_filter(&json_date);
|
||||
node.add_filter(&playout_stat);
|
||||
}
|
||||
|
||||
node
|
||||
}
|
||||
|
||||
fn handle_list_init(mut node: Media, json_date: &String) -> Media {
|
||||
fn handle_list_init(mut node: Media, playout_stat: &PlayoutStatus) -> 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
|
||||
|
||||
let (_, total_delta) = get_delta(&node.begin.unwrap(), &json_date, true);
|
||||
let (_, total_delta) = get_delta(&node.begin.unwrap(), &playout_stat, true);
|
||||
let mut out = node.out;
|
||||
|
||||
if node.out - node.seek > total_delta {
|
||||
@ -421,7 +444,7 @@ fn handle_list_init(mut node: Media, json_date: &String) -> Media {
|
||||
|
||||
node.out = out;
|
||||
|
||||
let new_node = gen_source(node, &json_date);
|
||||
let new_node = gen_source(node, &playout_stat);
|
||||
new_node
|
||||
}
|
||||
|
||||
|
50
src/main.rs
50
src/main.rs
@ -1,6 +1,13 @@
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
{fs, fs::File},
|
||||
};
|
||||
|
||||
extern crate log;
|
||||
extern crate simplelog;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use simplelog::*;
|
||||
use tokio::runtime::Builder;
|
||||
|
||||
@ -11,34 +18,65 @@ mod utils;
|
||||
|
||||
use crate::output::{player, write_hls};
|
||||
use crate::utils::{
|
||||
init_config, init_logging, init_status, run_rpc, validate_ffmpeg, GlobalConfig, PlayerControl,
|
||||
init_config, init_logging, run_rpc, validate_ffmpeg, GlobalConfig, PlayerControl,
|
||||
PlayoutStatus, ProcessControl,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct StatusData {
|
||||
time_shift: f64,
|
||||
date: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
init_config();
|
||||
let config = GlobalConfig::global();
|
||||
let play_control = PlayerControl::new();
|
||||
let _ = PlayoutStatus::new();
|
||||
let playout_stat = PlayoutStatus::new();
|
||||
let proc_control = ProcessControl::new();
|
||||
|
||||
if !PathBuf::from(config.general.stat_file.clone()).exists() {
|
||||
let data = json!({
|
||||
"time_shift": 0.0,
|
||||
"date": "".to_string(),
|
||||
});
|
||||
|
||||
let json: String = serde_json::to_string(&data).expect("Serialize status data failed");
|
||||
fs::write(config.general.stat_file.clone(), &json).expect("Unable to write file");
|
||||
} else {
|
||||
let stat_file = File::options()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.open(&config.general.stat_file)
|
||||
.expect("Could not open status file");
|
||||
|
||||
let data: StatusData =
|
||||
serde_json::from_reader(stat_file).expect("Could not read status file.");
|
||||
|
||||
*playout_stat.time_shift.lock().unwrap() = data.time_shift;
|
||||
*playout_stat.date.lock().unwrap() = data.date;
|
||||
}
|
||||
|
||||
let runtime = Builder::new_multi_thread().enable_all().build().unwrap();
|
||||
let rt_handle = runtime.handle();
|
||||
|
||||
let logging = init_logging(rt_handle.clone(), proc_control.is_terminated.clone());
|
||||
CombinedLogger::init(logging).unwrap();
|
||||
|
||||
init_status();
|
||||
validate_ffmpeg();
|
||||
|
||||
if config.rpc_server.enable {
|
||||
rt_handle.spawn(run_rpc(play_control.clone(), proc_control.clone()));
|
||||
rt_handle.spawn(run_rpc(
|
||||
play_control.clone(),
|
||||
playout_stat.clone(),
|
||||
proc_control.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
if config.out.mode.to_lowercase() == "hls".to_string() {
|
||||
write_hls(rt_handle, play_control, proc_control);
|
||||
write_hls(rt_handle, play_control, playout_stat, proc_control);
|
||||
} else {
|
||||
player(rt_handle, play_control, proc_control);
|
||||
player(rt_handle, play_control, playout_stat, proc_control);
|
||||
}
|
||||
|
||||
info!("Playout done...");
|
||||
|
@ -24,12 +24,13 @@ use tokio::runtime::Handle;
|
||||
|
||||
use crate::output::source_generator;
|
||||
use crate::utils::{
|
||||
sec_to_time, stderr_reader, GlobalConfig, PlayerControl, ProcessControl,
|
||||
sec_to_time, stderr_reader, GlobalConfig, PlayerControl, PlayoutStatus, ProcessControl,
|
||||
};
|
||||
|
||||
pub fn write_hls(
|
||||
rt_handle: &Handle,
|
||||
play_control: PlayerControl,
|
||||
playout_stat: PlayoutStatus,
|
||||
proc_control: ProcessControl,
|
||||
) {
|
||||
let config = GlobalConfig::global();
|
||||
@ -41,6 +42,7 @@ pub fn write_hls(
|
||||
config.clone(),
|
||||
play_control.current_list.clone(),
|
||||
play_control.index.clone(),
|
||||
playout_stat,
|
||||
proc_control.is_terminated.clone(),
|
||||
);
|
||||
|
||||
|
@ -24,7 +24,7 @@ pub use hls::write_hls;
|
||||
|
||||
use crate::input::{file_worker, ingest_server, CurrentProgram, Source};
|
||||
use crate::utils::{
|
||||
sec_to_time, stderr_reader, GlobalConfig, Media, PlayerControl, ProcessControl,
|
||||
sec_to_time, stderr_reader, GlobalConfig, Media, PlayerControl, PlayoutStatus, ProcessControl,
|
||||
};
|
||||
|
||||
pub fn source_generator(
|
||||
@ -32,6 +32,7 @@ pub fn source_generator(
|
||||
config: GlobalConfig,
|
||||
current_list: Arc<Mutex<Vec<Media>>>,
|
||||
index: Arc<Mutex<usize>>,
|
||||
playout_stat: PlayoutStatus,
|
||||
is_terminated: Arc<Mutex<bool>>,
|
||||
) -> (Box<dyn Iterator<Item = Media>>, Arc<Mutex<bool>>) {
|
||||
let mut init_playlist: Arc<Mutex<bool>> = Arc::new(Mutex::new(false));
|
||||
@ -46,7 +47,7 @@ pub fn source_generator(
|
||||
|
||||
info!("Playout in folder mode.");
|
||||
|
||||
let folder_source = Source::new(current_list, index);
|
||||
let folder_source = Source::new(current_list, index, playout_stat);
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
let mut watchman = watcher(sender, Duration::from_secs(2)).unwrap();
|
||||
@ -64,6 +65,7 @@ pub fn source_generator(
|
||||
info!("Playout in playlist mode");
|
||||
let program = CurrentProgram::new(
|
||||
rt_handle.clone(),
|
||||
playout_stat,
|
||||
is_terminated.clone(),
|
||||
current_list,
|
||||
index,
|
||||
@ -84,6 +86,7 @@ pub fn source_generator(
|
||||
pub fn player(
|
||||
rt_handle: &Handle,
|
||||
play_control: PlayerControl,
|
||||
playout_stat: PlayoutStatus,
|
||||
proc_control: ProcessControl,
|
||||
) {
|
||||
let config = GlobalConfig::global();
|
||||
@ -99,6 +102,7 @@ pub fn player(
|
||||
config.clone(),
|
||||
play_control.current_list.clone(),
|
||||
play_control.index.clone(),
|
||||
playout_stat,
|
||||
proc_control.is_terminated.clone(),
|
||||
);
|
||||
|
||||
|
@ -29,6 +29,9 @@ pub struct GlobalConfig {
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct General {
|
||||
pub stop_threshold: f64,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub stat_file: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
@ -146,7 +149,8 @@ impl GlobalConfig {
|
||||
Err(err) => {
|
||||
println!(
|
||||
"{:?} doesn't exists!\n{}\n\nSystem error: {err}",
|
||||
config_path, "Put \"ffplayout.yml\" in \"/etc/playout/\" or beside the executable!"
|
||||
config_path,
|
||||
"Put \"ffplayout.yml\" in \"/etc/playout/\" or beside the executable!"
|
||||
);
|
||||
process::exit(0x0100);
|
||||
}
|
||||
@ -154,6 +158,10 @@ impl GlobalConfig {
|
||||
|
||||
let mut config: GlobalConfig =
|
||||
serde_yaml::from_reader(f).expect("Could not read config file.");
|
||||
config.general.stat_file = env::temp_dir()
|
||||
.join("ffplayout_status.json")
|
||||
.display()
|
||||
.to_string();
|
||||
let fps = config.processing.fps.to_string();
|
||||
let bitrate = config.processing.width * config.processing.height / 10;
|
||||
config.playlist.start_sec = Some(time_to_sec(&config.playlist.day_start));
|
||||
|
@ -3,9 +3,7 @@ use chrono::Duration;
|
||||
use ffprobe::{ffprobe, Format, Stream};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
env::temp_dir,
|
||||
fs,
|
||||
fs::{metadata, File},
|
||||
fs::metadata,
|
||||
io::{BufRead, BufReader, Error},
|
||||
path::Path,
|
||||
process::exit,
|
||||
@ -14,7 +12,6 @@ use std::{
|
||||
time,
|
||||
time::UNIX_EPOCH,
|
||||
};
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
use jsonrpc_http_server::CloseHandle;
|
||||
use process_control::Terminator;
|
||||
@ -106,59 +103,21 @@ impl Drop for ProcessControl {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PlayoutStatus {
|
||||
pub time_shift: f64,
|
||||
pub date: String,
|
||||
pub time_shift: Arc<Mutex<f64>>,
|
||||
pub date: Arc<Mutex<String>>,
|
||||
pub current_date: Arc<Mutex<String>>,
|
||||
}
|
||||
|
||||
impl PlayoutStatus {
|
||||
pub fn new() -> Self {
|
||||
let stat_file = temp_dir().join("ffplayout_status.json");
|
||||
|
||||
let mut data: PlayoutStatus = Self {
|
||||
time_shift: 0.0,
|
||||
date: "".to_string(),
|
||||
};
|
||||
|
||||
if !stat_file.exists() {
|
||||
|
||||
} else {
|
||||
let file = File::options()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.open(&stat_file.display().to_string())
|
||||
.expect("Could not open status file");
|
||||
|
||||
data = serde_json::from_reader(file).expect("Could not read status file.");
|
||||
}
|
||||
|
||||
data
|
||||
}
|
||||
|
||||
pub fn write(mut self, date: String, time_shift: f64) {
|
||||
let stat_file = temp_dir().join("ffplayout_status.json");
|
||||
|
||||
self.date = date.clone();
|
||||
self.time_shift = time_shift.clone();
|
||||
|
||||
if let Ok (json) = serde_json::to_string(&self) {
|
||||
if let Err(e) = fs::write(stat_file, &json) {
|
||||
error!("Unable to write status file: {e}")
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
pub fn global() -> &'static PlayoutStatus {
|
||||
STATUS_CELL.get().expect("Config is not initialized")
|
||||
Self {
|
||||
time_shift: Arc::new(Mutex::new(0.0)),
|
||||
date: Arc::new(Mutex::new(String::new())),
|
||||
current_date: Arc::new(Mutex::new(String::new())),
|
||||
}
|
||||
}
|
||||
|
||||
static STATUS_CELL: OnceCell<PlayoutStatus> = OnceCell::new();
|
||||
|
||||
pub fn init_status() {
|
||||
let status = PlayoutStatus::new();
|
||||
STATUS_CELL.set(status).unwrap();
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -242,9 +201,9 @@ impl Media {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_filter(&mut self, json_date: &String) {
|
||||
pub fn add_filter(&mut self, playout_stat: &PlayoutStatus) {
|
||||
let mut node = self.clone();
|
||||
self.filter = Some(filter_chains(&mut node, &json_date))
|
||||
self.filter = Some(filter_chains(&mut node, &playout_stat))
|
||||
}
|
||||
}
|
||||
|
||||
@ -378,10 +337,8 @@ pub fn is_close(a: f64, b: f64, to: f64) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_delta(begin: &f64, json_date: &String, shift: bool) -> (f64, f64) {
|
||||
pub fn get_delta(begin: &f64, playout_stat: &PlayoutStatus, shift: bool) -> (f64, f64) {
|
||||
let config = GlobalConfig::global();
|
||||
let status = PlayoutStatus::global();
|
||||
|
||||
let mut current_time = get_sec();
|
||||
let start = config.playlist.start_sec.unwrap();
|
||||
let length = time_to_sec(&config.playlist.length);
|
||||
@ -409,9 +366,15 @@ pub fn get_delta(begin: &f64, json_date: &String, shift: bool) -> (f64, f64) {
|
||||
total_delta = target_length + start - current_time;
|
||||
}
|
||||
|
||||
if shift && json_date == &status.date && status.time_shift != 0.0 {
|
||||
current_delta -= status.time_shift;
|
||||
total_delta -= status.time_shift;
|
||||
println!("current_delta: {current_delta}");
|
||||
println!("shift: {}", *playout_stat.time_shift.lock().unwrap());
|
||||
|
||||
if shift
|
||||
&& *playout_stat.current_date.lock().unwrap() == *playout_stat.date.lock().unwrap()
|
||||
&& *playout_stat.time_shift.lock().unwrap() != 0.0
|
||||
{
|
||||
current_delta -= *playout_stat.time_shift.lock().unwrap();
|
||||
total_delta -= *playout_stat.time_shift.lock().unwrap();
|
||||
}
|
||||
|
||||
(current_delta, total_delta)
|
||||
|
@ -1,3 +1,4 @@
|
||||
use std::fs;
|
||||
use serde_json::{json, Map};
|
||||
|
||||
use jsonrpc_http_server::jsonrpc_core::{IoHandler, Params, Value};
|
||||
@ -7,7 +8,8 @@ use jsonrpc_http_server::{
|
||||
use simplelog::*;
|
||||
|
||||
use crate::utils::{
|
||||
get_delta, get_sec, sec_to_time, GlobalConfig, Media, PlayerControl, PlayoutStatus, ProcessControl,
|
||||
get_delta, get_sec, sec_to_time, GlobalConfig, Media, PlayerControl, PlayoutStatus,
|
||||
ProcessControl,
|
||||
};
|
||||
|
||||
fn get_media_map(media: Media) -> Value {
|
||||
@ -42,13 +44,20 @@ fn get_data_map(config: &GlobalConfig, media: Media) -> Map<String, Value> {
|
||||
data_map
|
||||
}
|
||||
|
||||
pub async fn run_rpc(play_control: PlayerControl, proc_control: ProcessControl) {
|
||||
pub async fn run_rpc(
|
||||
play_control: PlayerControl,
|
||||
playout_stat: PlayoutStatus,
|
||||
proc_control: ProcessControl,
|
||||
) {
|
||||
let config = GlobalConfig::global();
|
||||
let mut io = IoHandler::default();
|
||||
let play = play_control.clone();
|
||||
let stat = playout_stat.clone();
|
||||
let proc = proc_control.clone();
|
||||
|
||||
io.add_sync_method("player", move |params: Params| {
|
||||
let stat_file = config.general.stat_file.clone();
|
||||
|
||||
match params {
|
||||
Params::Map(map) => {
|
||||
if map.contains_key("control") && map["control"] == "next".to_string() {
|
||||
@ -57,7 +66,6 @@ pub async fn run_rpc(play_control: PlayerControl, proc_control: ProcessControl)
|
||||
if let Ok(_) = decoder.terminate() {
|
||||
info!("Move to next clip");
|
||||
let index = *play.index.lock().unwrap();
|
||||
let status = PlayoutStatus::global();
|
||||
|
||||
if index < play.current_list.lock().unwrap().len() {
|
||||
let mut data_map = Map::new();
|
||||
@ -65,8 +73,18 @@ pub async fn run_rpc(play_control: PlayerControl, proc_control: ProcessControl)
|
||||
play.current_list.lock().unwrap()[index].clone();
|
||||
media.add_probe();
|
||||
|
||||
let (delta, _) = get_delta(&media.begin.unwrap_or(0.0), &status.date, false);
|
||||
status.clone().write(status.date.clone(), delta);
|
||||
let (delta, _) =
|
||||
get_delta(&media.begin.unwrap_or(0.0), &stat, false);
|
||||
|
||||
let data = json!({
|
||||
"time_shift": delta,
|
||||
"date": *stat.current_date.lock().unwrap(),
|
||||
});
|
||||
|
||||
let status_data: String = serde_json::to_string(&data)
|
||||
.expect("Serialize status data failed");
|
||||
fs::write(stat_file, &status_data)
|
||||
.expect("Unable to write file");
|
||||
|
||||
data_map.insert(
|
||||
"operation".to_string(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user