add controller functions: next, back, reset
This commit is contained in:
parent
21d656131b
commit
1bb910d3da
@ -4,7 +4,7 @@ use simplelog::*;
|
||||
|
||||
pub mod v_drawtext;
|
||||
|
||||
use crate::utils::{get_delta, is_close, GlobalConfig, Media, PlayoutStatus};
|
||||
use crate::utils::{get_delta, is_close, GlobalConfig, Media};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Filters {
|
||||
@ -122,7 +122,7 @@ fn scale(width: i64, height: i64, aspect: f64, chain: &mut Filters, config: &Glo
|
||||
}
|
||||
|
||||
fn fade(node: &mut Media, chain: &mut Filters, codec_type: String) {
|
||||
let mut t = "".to_string();
|
||||
let mut t = String::new();
|
||||
|
||||
if codec_type == "audio".to_string() {
|
||||
t = "a".to_string()
|
||||
@ -290,11 +290,10 @@ fn realtime_filter(
|
||||
chain: &mut Filters,
|
||||
config: &GlobalConfig,
|
||||
codec_type: String,
|
||||
playout_stat: &PlayoutStatus,
|
||||
) {
|
||||
// this realtime filter is important for HLS output to stay in sync
|
||||
|
||||
let mut t = "".to_string();
|
||||
let mut t = String::new();
|
||||
|
||||
if codec_type == "audio".to_string() {
|
||||
t = "a".to_string()
|
||||
@ -302,7 +301,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(), &playout_stat, true);
|
||||
let (delta, _) = get_delta(&node.begin.unwrap());
|
||||
let duration = node.out - node.seek;
|
||||
|
||||
if delta < 0.0 {
|
||||
@ -317,7 +316,7 @@ fn realtime_filter(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filter_chains(node: &mut Media, playout_stat: &PlayoutStatus) -> Vec<String> {
|
||||
pub fn filter_chains(node: &mut Media) -> Vec<String> {
|
||||
let config = GlobalConfig::global();
|
||||
|
||||
let mut filters = Filters::new();
|
||||
@ -355,15 +354,15 @@ pub fn filter_chains(node: &mut Media, playout_stat: &PlayoutStatus) -> Vec<Stri
|
||||
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(), &playout_stat);
|
||||
realtime_filter(node, &mut filters, &config, "video".into());
|
||||
|
||||
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(), &playout_stat);
|
||||
realtime_filter(node, &mut filters, &config, "audio".into());
|
||||
|
||||
let mut filter_cmd = vec![];
|
||||
let mut filter_str: String = "".to_string();
|
||||
let mut filter_str: String = String::new();
|
||||
let mut filter_map: Vec<String> = vec![];
|
||||
|
||||
if filters.video_chain.is_some() {
|
||||
|
@ -12,7 +12,7 @@ use std::{
|
||||
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::utils::{get_sec, GlobalConfig, Media, PlayoutStatus};
|
||||
use crate::utils::{get_sec, GlobalConfig, Media};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Source {
|
||||
@ -20,14 +20,12 @@ 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>>,
|
||||
playout_stat: PlayoutStatus,
|
||||
) -> Self {
|
||||
let config = GlobalConfig::global();
|
||||
let mut media_list = vec![];
|
||||
@ -72,9 +70,8 @@ impl Source {
|
||||
Self {
|
||||
config: config.clone(),
|
||||
nodes: current_list,
|
||||
current_node: Media::new(0, "".to_string(), false),
|
||||
current_node: Media::new(0, String::new(), false),
|
||||
index: global_index,
|
||||
playout_stat,
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +110,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(&self.playout_stat);
|
||||
self.current_node.add_filter();
|
||||
self.current_node.begin = Some(get_sec());
|
||||
|
||||
*self.index.lock().unwrap() += 1;
|
||||
@ -130,7 +127,7 @@ impl Iterator for Source {
|
||||
|
||||
self.current_node = self.nodes.lock().unwrap()[0].clone();
|
||||
self.current_node.add_probe();
|
||||
self.current_node.add_filter(&self.playout_stat);
|
||||
self.current_node.add_filter();
|
||||
self.current_node.begin = Some(get_sec());
|
||||
|
||||
*self.index.lock().unwrap() = 1;
|
||||
|
@ -2,6 +2,8 @@ use std::{
|
||||
fs,
|
||||
path::Path,
|
||||
sync::{Arc, Mutex},
|
||||
thread::sleep,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use serde_json::json;
|
||||
@ -22,7 +24,6 @@ pub struct CurrentProgram {
|
||||
json_date: String,
|
||||
pub nodes: Arc<Mutex<Vec<Media>>>,
|
||||
current_node: Media,
|
||||
pub init: Arc<Mutex<bool>>,
|
||||
index: Arc<Mutex<usize>>,
|
||||
rt_handle: Handle,
|
||||
is_terminated: Arc<Mutex<bool>>,
|
||||
@ -60,8 +61,7 @@ impl CurrentProgram {
|
||||
json_path: json.current_file,
|
||||
json_date: json.date,
|
||||
nodes: current_list,
|
||||
current_node: Media::new(0, "".to_string(), false),
|
||||
init: Arc::new(Mutex::new(true)),
|
||||
current_node: Media::new(0, String::new(), false),
|
||||
index: global_index,
|
||||
rt_handle,
|
||||
is_terminated,
|
||||
@ -115,7 +115,7 @@ impl CurrentProgram {
|
||||
"Playlist <b><magenta>{}</></b> not exists!",
|
||||
self.json_path.clone().unwrap()
|
||||
);
|
||||
let mut media = Media::new(0, "".to_string(), false);
|
||||
let mut media = Media::new(0, String::new(), false);
|
||||
media.begin = Some(get_sec());
|
||||
media.duration = DUMMY_LEN;
|
||||
media.out = DUMMY_LEN;
|
||||
@ -123,7 +123,7 @@ impl CurrentProgram {
|
||||
self.json_path = None;
|
||||
*self.nodes.lock().unwrap() = vec![media.clone()];
|
||||
self.current_node = media;
|
||||
*self.init.lock().unwrap() = true;
|
||||
*self.playout_stat.list_init.lock().unwrap() = true;
|
||||
*self.index.lock().unwrap() = 0;
|
||||
}
|
||||
}
|
||||
@ -132,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.playout_stat, true);
|
||||
let (delta, total_delta) = get_delta(¤t_time);
|
||||
let mut duration = self.current_node.out.clone();
|
||||
|
||||
if self.current_node.duration > self.current_node.out {
|
||||
@ -159,8 +159,10 @@ impl CurrentProgram {
|
||||
});
|
||||
|
||||
*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");
|
||||
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;
|
||||
@ -169,7 +171,7 @@ impl CurrentProgram {
|
||||
*self.index.lock().unwrap() = 0;
|
||||
|
||||
if json.current_file.is_none() {
|
||||
*self.init.lock().unwrap() = true;
|
||||
*self.playout_stat.list_init.lock().unwrap() = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -203,11 +205,19 @@ impl CurrentProgram {
|
||||
}
|
||||
|
||||
fn get_current_clip(&mut self) {
|
||||
let time_sec = self.get_current_time();
|
||||
let mut time_sec = self.get_current_time();
|
||||
|
||||
if *self.playout_stat.current_date.lock().unwrap() == *self.playout_stat.date.lock().unwrap()
|
||||
&& *self.playout_stat.time_shift.lock().unwrap() != 0.0
|
||||
{
|
||||
let shift = *self.playout_stat.time_shift.lock().unwrap();
|
||||
info!("Shift playlist start for <yellow>{shift}</> seconds");
|
||||
time_sec += shift;
|
||||
}
|
||||
|
||||
for (i, item) in self.nodes.lock().unwrap().iter_mut().enumerate() {
|
||||
if item.begin.unwrap() + item.out - item.seek > time_sec {
|
||||
*self.init.lock().unwrap() = false;
|
||||
*self.playout_stat.list_init.lock().unwrap() = false;
|
||||
*self.index.lock().unwrap() = i;
|
||||
|
||||
break;
|
||||
@ -218,7 +228,7 @@ impl CurrentProgram {
|
||||
fn init_clip(&mut self) {
|
||||
self.get_current_clip();
|
||||
|
||||
if !*self.init.lock().unwrap() {
|
||||
if !*self.playout_stat.list_init.lock().unwrap() {
|
||||
let time_sec = self.get_current_time();
|
||||
let index = *self.index.lock().unwrap();
|
||||
|
||||
@ -227,7 +237,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.playout_stat);
|
||||
self.current_node = handle_list_init(node_clone);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -236,7 +246,7 @@ impl Iterator for CurrentProgram {
|
||||
type Item = Media;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if *self.init.lock().unwrap() {
|
||||
if *self.playout_stat.list_init.lock().unwrap() {
|
||||
debug!("Playlist init");
|
||||
self.check_update(true);
|
||||
|
||||
@ -244,7 +254,7 @@ impl Iterator for CurrentProgram {
|
||||
self.init_clip();
|
||||
}
|
||||
|
||||
if *self.init.lock().unwrap() {
|
||||
if *self.playout_stat.list_init.lock().unwrap() {
|
||||
// on init load playlist, could be not long enough,
|
||||
// so we check if we can take the next playlist already,
|
||||
// or we fill the gap with a dummy.
|
||||
@ -262,23 +272,23 @@ impl Iterator for CurrentProgram {
|
||||
self.init_clip();
|
||||
} else {
|
||||
let mut current_time = get_sec();
|
||||
let (_, total_delta) = get_delta(¤t_time, &self.playout_stat, true);
|
||||
let (_, total_delta) = get_delta(¤t_time);
|
||||
let mut duration = DUMMY_LEN;
|
||||
|
||||
if DUMMY_LEN > total_delta {
|
||||
duration = total_delta;
|
||||
*self.init.lock().unwrap() = false;
|
||||
*self.playout_stat.list_init.lock().unwrap() = false;
|
||||
}
|
||||
|
||||
if self.config.playlist.start_sec.unwrap() > current_time {
|
||||
current_time += self.config.playlist.length_sec.unwrap() + 1.0;
|
||||
}
|
||||
let mut media = Media::new(0, "".to_string(), false);
|
||||
let mut media = Media::new(0, String::new(), false);
|
||||
media.begin = Some(current_time);
|
||||
media.duration = duration;
|
||||
media.out = duration;
|
||||
|
||||
self.current_node = gen_source(media, &self.playout_stat);
|
||||
self.current_node = gen_source(media);
|
||||
self.nodes.lock().unwrap().push(self.current_node.clone());
|
||||
*self.index.lock().unwrap() = self.nodes.lock().unwrap().len();
|
||||
}
|
||||
@ -315,11 +325,7 @@ impl Iterator for CurrentProgram {
|
||||
let last_playlist = self.json_path.clone();
|
||||
let last_ad = self.current_node.last_ad.clone();
|
||||
self.check_for_next_playlist();
|
||||
let (_, total_delta) = get_delta(
|
||||
&self.config.playlist.start_sec.unwrap(),
|
||||
&self.playout_stat,
|
||||
true,
|
||||
);
|
||||
let (_, total_delta) = get_delta(&self.config.playlist.start_sec.unwrap());
|
||||
|
||||
if last_playlist == self.json_path
|
||||
&& total_delta.abs() > self.config.general.stop_threshold
|
||||
@ -327,7 +333,7 @@ impl Iterator for CurrentProgram {
|
||||
// Test if playlist is to early finish,
|
||||
// and if we have to fill it with a placeholder.
|
||||
let index = *self.index.lock().unwrap();
|
||||
self.current_node = Media::new(index, "".to_string(), false);
|
||||
self.current_node = Media::new(index, String::new(), false);
|
||||
self.current_node.begin = Some(get_sec());
|
||||
let mut duration = total_delta.abs();
|
||||
|
||||
@ -336,12 +342,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.playout_stat);
|
||||
self.current_node = gen_source(self.current_node.clone());
|
||||
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.playout_stat);
|
||||
self.current_node.add_filter();
|
||||
|
||||
*self.index.lock().unwrap() += 1;
|
||||
|
||||
@ -350,7 +356,7 @@ impl Iterator for CurrentProgram {
|
||||
|
||||
*self.index.lock().unwrap() = 0;
|
||||
self.current_node =
|
||||
gen_source(self.nodes.lock().unwrap()[0].clone(), &self.playout_stat);
|
||||
gen_source(self.nodes.lock().unwrap()[0].clone());
|
||||
self.last_next_ad();
|
||||
self.current_node.last_ad = last_ad;
|
||||
|
||||
@ -371,14 +377,26 @@ fn timed_source(
|
||||
// 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(), &playout_stat, true);
|
||||
let (delta, total_delta) = get_delta(&node.begin.unwrap());
|
||||
let mut shifted_delta = delta;
|
||||
let mut new_node = node.clone();
|
||||
new_node.process = Some(false);
|
||||
|
||||
if config.playlist.length.contains(":") {
|
||||
debug!("Delta: <yellow>{delta:.3}</>");
|
||||
debug!("Total delta: <yellow>{total_delta:.3}</>");
|
||||
let sync = check_sync(delta);
|
||||
if *playout_stat.current_date.lock().unwrap() == *playout_stat.date.lock().unwrap()
|
||||
&& *playout_stat.time_shift.lock().unwrap() != 0.0
|
||||
{
|
||||
sleep(Duration::from_millis(300));
|
||||
shifted_delta = delta - *playout_stat.time_shift.lock().unwrap();
|
||||
|
||||
debug!("Delta: <yellow>{shifted_delta:.3}</>, shifted: <yellow>{delta:.3}</>");
|
||||
} else {
|
||||
debug!("Delta: <yellow>{shifted_delta:.3}</>");
|
||||
}
|
||||
|
||||
debug!("Total time remaining: <yellow>{total_delta:.3}</>");
|
||||
|
||||
let sync = check_sync(shifted_delta);
|
||||
|
||||
if !sync {
|
||||
new_node.cmd = None;
|
||||
@ -392,7 +410,7 @@ fn timed_source(
|
||||
|| !config.playlist.length.contains(":")
|
||||
{
|
||||
// when we are in the 24 hour range, get the clip
|
||||
new_node = gen_source(node, &playout_stat);
|
||||
new_node = gen_source(node);
|
||||
new_node.process = Some(true);
|
||||
} else if total_delta <= 0.0 {
|
||||
info!("Begin is over play time, skip: {}", node.source);
|
||||
@ -403,7 +421,7 @@ fn timed_source(
|
||||
new_node
|
||||
}
|
||||
|
||||
fn gen_source(mut node: Media, playout_stat: &PlayoutStatus) -> Media {
|
||||
fn gen_source(mut node: Media) -> Media {
|
||||
if Path::new(&node.source).is_file() {
|
||||
node.add_probe();
|
||||
node.cmd = Some(seek_and_length(
|
||||
@ -412,7 +430,7 @@ fn gen_source(mut node: Media, playout_stat: &PlayoutStatus) -> Media {
|
||||
node.out,
|
||||
node.duration,
|
||||
));
|
||||
node.add_filter(&playout_stat);
|
||||
node.add_filter();
|
||||
} else {
|
||||
if node.source.chars().count() == 0 {
|
||||
warn!(
|
||||
@ -425,17 +443,17 @@ fn gen_source(mut node: Media, playout_stat: &PlayoutStatus) -> Media {
|
||||
let (source, cmd) = gen_dummy(node.out - node.seek);
|
||||
node.source = source;
|
||||
node.cmd = Some(cmd);
|
||||
node.add_filter(&playout_stat);
|
||||
node.add_filter();
|
||||
}
|
||||
|
||||
node
|
||||
}
|
||||
|
||||
fn handle_list_init(mut node: Media, playout_stat: &PlayoutStatus) -> Media {
|
||||
fn handle_list_init(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
|
||||
|
||||
let (_, total_delta) = get_delta(&node.begin.unwrap(), &playout_stat, true);
|
||||
let (_, total_delta) = get_delta(&node.begin.unwrap());
|
||||
let mut out = node.out;
|
||||
|
||||
if node.out - node.seek > total_delta {
|
||||
@ -444,7 +462,7 @@ fn handle_list_init(mut node: Media, playout_stat: &PlayoutStatus) -> Media {
|
||||
|
||||
node.out = out;
|
||||
|
||||
let new_node = gen_source(node, &playout_stat);
|
||||
let new_node = gen_source(node);
|
||||
new_node
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ fn main() {
|
||||
if !PathBuf::from(config.general.stat_file.clone()).exists() {
|
||||
let data = json!({
|
||||
"time_shift": 0.0,
|
||||
"date": "".to_string(),
|
||||
"date": String::new(),
|
||||
});
|
||||
|
||||
let json: String = serde_json::to_string(&data).expect("Serialize status data failed");
|
||||
|
@ -29,7 +29,7 @@ pub fn output(log_format: String) -> process::Child {
|
||||
);
|
||||
|
||||
let mut filter: String = "null,".to_string();
|
||||
filter.push_str(v_drawtext::filter_node(&mut Media::new(0, "".to_string(), false)).as_str());
|
||||
filter.push_str(v_drawtext::filter_node(&mut Media::new(0, String::new(), false)).as_str());
|
||||
enc_filter = vec!["-vf".to_string(), filter];
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ pub fn write_hls(
|
||||
let dec_settings = config.out.clone().output_cmd.unwrap();
|
||||
let ff_log_format = format!("level+{}", config.logging.ffmpeg_level.to_lowercase());
|
||||
|
||||
let (get_source, _) = source_generator(
|
||||
let get_source = source_generator(
|
||||
rt_handle,
|
||||
config.clone(),
|
||||
play_control.current_list.clone(),
|
||||
|
@ -34,9 +34,7 @@ pub fn source_generator(
|
||||
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));
|
||||
|
||||
) -> Box<dyn Iterator<Item = Media>> {
|
||||
let get_source = match config.processing.clone().mode.as_str() {
|
||||
"folder" => {
|
||||
let path = config.storage.path.clone();
|
||||
@ -47,7 +45,7 @@ pub fn source_generator(
|
||||
|
||||
info!("Playout in folder mode.");
|
||||
|
||||
let folder_source = Source::new(current_list, index, playout_stat);
|
||||
let folder_source = Source::new(current_list, index);
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
let mut watchman = watcher(sender, Duration::from_secs(2)).unwrap();
|
||||
@ -70,7 +68,6 @@ pub fn source_generator(
|
||||
current_list,
|
||||
index,
|
||||
);
|
||||
init_playlist = program.init.clone();
|
||||
|
||||
Box::new(program) as Box<dyn Iterator<Item = Media>>
|
||||
}
|
||||
@ -80,7 +77,7 @@ pub fn source_generator(
|
||||
}
|
||||
};
|
||||
|
||||
(get_source, init_playlist)
|
||||
get_source
|
||||
}
|
||||
|
||||
pub fn player(
|
||||
@ -96,8 +93,9 @@ pub fn player(
|
||||
let server_is_running: Arc<Mutex<bool>> = Arc::new(Mutex::new(false));
|
||||
let mut buffer: [u8; 65088] = [0; 65088];
|
||||
let mut live_on = false;
|
||||
let playlist_init = playout_stat.list_init.clone();
|
||||
|
||||
let (get_source, init_playlist) = source_generator(
|
||||
let get_source = source_generator(
|
||||
rt_handle,
|
||||
config.clone(),
|
||||
play_control.current_list.clone(),
|
||||
@ -209,7 +207,7 @@ pub fn player(
|
||||
|
||||
live_on = true;
|
||||
|
||||
*init_playlist.lock().unwrap() = true;
|
||||
*playlist_init.lock().unwrap() = true;
|
||||
}
|
||||
|
||||
if let Ok(receive) = ingest_receiver.try_recv() {
|
||||
|
@ -32,7 +32,7 @@ pub fn output(log_format: String) -> process::Child {
|
||||
);
|
||||
|
||||
let mut filter: String = "[0:v]null,".to_string();
|
||||
filter.push_str(v_drawtext::filter_node(&mut Media::new(0, "".to_string(), false)).as_str());
|
||||
filter.push_str(v_drawtext::filter_node(&mut Media::new(0, String::new(), false)).as_str());
|
||||
|
||||
if config.out.preview {
|
||||
filter.push_str(",split=2[v_out1][v_out2]");
|
||||
|
@ -23,7 +23,7 @@ pub struct Playlist {
|
||||
|
||||
impl Playlist {
|
||||
fn new(date: String, start: f64) -> Self {
|
||||
let mut media = Media::new(0, "".to_string(), false);
|
||||
let mut media = Media::new(0, String::new(), false);
|
||||
media.begin = Some(start);
|
||||
media.duration = DUMMY_LEN;
|
||||
media.out = DUMMY_LEN;
|
||||
@ -31,7 +31,7 @@ impl Playlist {
|
||||
date,
|
||||
start_sec: Some(start),
|
||||
current_file: None,
|
||||
modified: Some("".to_string()),
|
||||
modified: Some(String::new()),
|
||||
program: vec![media],
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use chrono::prelude::*;
|
||||
use chrono::Duration;
|
||||
use ffprobe::{ffprobe, Format, Stream};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fs,
|
||||
fs::metadata,
|
||||
io::{BufRead, BufReader, Error},
|
||||
path::Path,
|
||||
@ -17,6 +17,8 @@ use jsonrpc_http_server::CloseHandle;
|
||||
use process_control::Terminator;
|
||||
use regex::Regex;
|
||||
use simplelog::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
mod arg_parse;
|
||||
mod config;
|
||||
@ -108,6 +110,7 @@ pub struct PlayoutStatus {
|
||||
pub time_shift: Arc<Mutex<f64>>,
|
||||
pub date: Arc<Mutex<String>>,
|
||||
pub current_date: Arc<Mutex<String>>,
|
||||
pub list_init: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
impl PlayoutStatus {
|
||||
@ -116,6 +119,7 @@ impl PlayoutStatus {
|
||||
time_shift: Arc::new(Mutex::new(0.0)),
|
||||
date: Arc::new(Mutex::new(String::new())),
|
||||
current_date: Arc::new(Mutex::new(String::new())),
|
||||
list_init: Arc::new(Mutex::new(true)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -131,7 +135,7 @@ impl PlayerControl {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current_media: Arc::new(Mutex::new(None)),
|
||||
current_list: Arc::new(Mutex::new(vec![Media::new(0, "".to_string(), false)])),
|
||||
current_list: Arc::new(Mutex::new(vec![Media::new(0, String::new(), false)])),
|
||||
index: Arc::new(Mutex::new(0)),
|
||||
}
|
||||
}
|
||||
@ -175,7 +179,7 @@ impl Media {
|
||||
seek: 0.0,
|
||||
out: duration,
|
||||
duration: duration,
|
||||
category: "".to_string(),
|
||||
category: String::new(),
|
||||
source: src.clone(),
|
||||
cmd: Some(vec!["-i".to_string(), src]),
|
||||
filter: Some(vec![]),
|
||||
@ -201,9 +205,9 @@ impl Media {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_filter(&mut self, playout_stat: &PlayoutStatus) {
|
||||
pub fn add_filter(&mut self) {
|
||||
let mut node = self.clone();
|
||||
self.filter = Some(filter_chains(&mut node, &playout_stat))
|
||||
self.filter = Some(filter_chains(&mut node))
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,6 +274,21 @@ impl MediaProbe {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_status(date: String, shift: f64) {
|
||||
let config = GlobalConfig::global();
|
||||
let stat_file = config.general.stat_file.clone();
|
||||
|
||||
let data = json!({
|
||||
"time_shift": shift,
|
||||
"date": date,
|
||||
});
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
// pub fn get_timestamp() -> i64 {
|
||||
// let local: DateTime<Local> = Local::now();
|
||||
|
||||
@ -337,13 +356,13 @@ pub fn is_close(a: f64, b: f64, to: f64) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_delta(begin: &f64, playout_stat: &PlayoutStatus, shift: bool) -> (f64, f64) {
|
||||
pub fn get_delta(begin: &f64) -> (f64, f64) {
|
||||
let config = GlobalConfig::global();
|
||||
let mut current_time = get_sec();
|
||||
let start = config.playlist.start_sec.unwrap();
|
||||
let length = time_to_sec(&config.playlist.length);
|
||||
let mut target_length = 86400.0;
|
||||
let mut total_delta;
|
||||
let total_delta;
|
||||
|
||||
if length > 0.0 && length != target_length {
|
||||
target_length = length
|
||||
@ -366,17 +385,6 @@ pub fn get_delta(begin: &f64, playout_stat: &PlayoutStatus, shift: bool) -> (f64
|
||||
total_delta = target_length + start - current_time;
|
||||
}
|
||||
|
||||
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,15 +1,16 @@
|
||||
use std::fs;
|
||||
use serde_json::{json, Map};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use jsonrpc_http_server::jsonrpc_core::{IoHandler, Params, Value};
|
||||
use jsonrpc_http_server::{
|
||||
hyper, AccessControlAllowOrigin, DomainsValidation, Response, RestApi, ServerBuilder,
|
||||
};
|
||||
use process_control::Terminator;
|
||||
use serde_json::{json, Map};
|
||||
use simplelog::*;
|
||||
|
||||
use crate::utils::{
|
||||
get_delta, get_sec, sec_to_time, GlobalConfig, Media, PlayerControl, PlayoutStatus,
|
||||
ProcessControl,
|
||||
get_delta, get_sec, sec_to_time, write_status, GlobalConfig, Media, PlayerControl,
|
||||
PlayoutStatus, ProcessControl,
|
||||
};
|
||||
|
||||
fn get_media_map(media: Media) -> Value {
|
||||
@ -44,6 +45,19 @@ fn get_data_map(config: &GlobalConfig, media: Media) -> Map<String, Value> {
|
||||
data_map
|
||||
}
|
||||
|
||||
fn kill_decoder(terminator: Arc<Mutex<Option<Terminator>>>) -> Result<(), String> {
|
||||
match &*terminator.lock().unwrap() {
|
||||
Some(decoder) => unsafe {
|
||||
if let Err(e) = decoder.terminate() {
|
||||
return Err(format!("Terminate decoder: {e}"));
|
||||
}
|
||||
},
|
||||
None => return Err("No decoder terminator found".to_string()),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn run_rpc(
|
||||
play_control: PlayerControl,
|
||||
playout_stat: PlayoutStatus,
|
||||
@ -52,56 +66,36 @@ pub async fn run_rpc(
|
||||
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() {
|
||||
if let Some(decoder) = &*proc.decoder_term.lock().unwrap() {
|
||||
unsafe {
|
||||
if let Ok(_) = decoder.terminate() {
|
||||
info!("Move to next clip");
|
||||
let index = *play.index.lock().unwrap();
|
||||
if let Ok(_) = kill_decoder(proc.decoder_term.clone()) {
|
||||
info!("Move to next clip");
|
||||
let index = *play.index.lock().unwrap();
|
||||
|
||||
if index < play.current_list.lock().unwrap().len() {
|
||||
let mut data_map = Map::new();
|
||||
let mut media =
|
||||
play.current_list.lock().unwrap()[index].clone();
|
||||
media.add_probe();
|
||||
if index < play.current_list.lock().unwrap().len() {
|
||||
let mut data_map = Map::new();
|
||||
let mut media = play.current_list.lock().unwrap()[index].clone();
|
||||
media.add_probe();
|
||||
|
||||
let (delta, _) =
|
||||
get_delta(&media.begin.unwrap_or(0.0), &stat, false);
|
||||
let (delta, _) = get_delta(&media.begin.unwrap_or(0.0));
|
||||
*playout_stat.time_shift.lock().unwrap() = delta;
|
||||
write_status(playout_stat.current_date.lock().unwrap().clone(), delta);
|
||||
|
||||
let data = json!({
|
||||
"time_shift": delta,
|
||||
"date": *stat.current_date.lock().unwrap(),
|
||||
});
|
||||
data_map.insert("operation".to_string(), json!("Move to next clip"));
|
||||
data_map.insert("media".to_string(), get_media_map(media));
|
||||
|
||||
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(),
|
||||
json!("Move to next clip"),
|
||||
);
|
||||
data_map.insert("media".to_string(), get_media_map(media));
|
||||
|
||||
return Ok(Value::Object(data_map));
|
||||
}
|
||||
}
|
||||
return Ok(Value::Object(data_map));
|
||||
}
|
||||
}
|
||||
return Ok(Value::String(format!("Move failed")));
|
||||
return Ok(Value::String("Move failed".to_string()));
|
||||
}
|
||||
|
||||
if map.contains_key("control") && map["control"] == "back".to_string() {
|
||||
if let Some(decoder) = &*proc.decoder_term.lock().unwrap() {
|
||||
if let Ok(_) = kill_decoder(proc.decoder_term.clone()) {
|
||||
let index = *play.index.lock().unwrap();
|
||||
|
||||
if index > 1 && play.current_list.lock().unwrap().len() > 1 {
|
||||
@ -110,17 +104,32 @@ pub async fn run_rpc(
|
||||
let mut media = play.current_list.lock().unwrap()[index - 2].clone();
|
||||
*play.index.lock().unwrap() = index - 2;
|
||||
media.add_probe();
|
||||
|
||||
let (delta, _) = get_delta(&media.begin.unwrap_or(0.0));
|
||||
*playout_stat.time_shift.lock().unwrap() = delta;
|
||||
write_status(playout_stat.current_date.lock().unwrap().clone(), delta);
|
||||
|
||||
data_map.insert("operation".to_string(), json!("Move to last clip"));
|
||||
data_map.insert("media".to_string(), get_media_map(media));
|
||||
|
||||
unsafe {
|
||||
if let Ok(_) = decoder.terminate() {
|
||||
return Ok(Value::Object(data_map));
|
||||
}
|
||||
}
|
||||
return Ok(Value::Object(data_map));
|
||||
}
|
||||
}
|
||||
return Ok(Value::String(format!("Move failed")));
|
||||
return Ok(Value::String("Move failed".to_string()));
|
||||
}
|
||||
|
||||
if map.contains_key("control") && map["control"] == "reset".to_string() {
|
||||
*playout_stat.date.lock().unwrap() = String::new();
|
||||
*playout_stat.time_shift.lock().unwrap() = 0.0;
|
||||
*playout_stat.list_init.lock().unwrap() = true;
|
||||
|
||||
write_status(String::new().clone(), 0.0);
|
||||
|
||||
if let Err(e) = kill_decoder(proc.decoder_term.clone()) {
|
||||
error!("{e}");
|
||||
}
|
||||
|
||||
return Ok(Value::String("Reset playout to original state".to_string()));
|
||||
}
|
||||
|
||||
if map.contains_key("media") && map["media"] == "current".to_string() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user