support separate audio (#113), convert to string instead of String::from()
This commit is contained in:
parent
c41cb4396f
commit
fc43a1dca8
@ -12,7 +12,7 @@ use simplelog::*;
|
|||||||
|
|
||||||
use ffplayout_lib::utils::{
|
use ffplayout_lib::utils::{
|
||||||
check_sync, gen_dummy, get_delta, get_sec, is_close, is_remote, json_serializer::read_json,
|
check_sync, gen_dummy, get_delta, get_sec, is_close, is_remote, json_serializer::read_json,
|
||||||
loop_image, loop_input, modified_time, seek_and_length, valid_source, Media, MediaProbe,
|
loop_filler, loop_image, modified_time, seek_and_length, valid_source, Media, MediaProbe,
|
||||||
PlayoutConfig, PlayoutStatus, DUMMY_LEN, IMAGE_FORMAT,
|
PlayoutConfig, PlayoutStatus, DUMMY_LEN, IMAGE_FORMAT,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -460,8 +460,6 @@ fn gen_source(
|
|||||||
mut node: Media,
|
mut node: Media,
|
||||||
filter_chain: &Arc<Mutex<Vec<String>>>,
|
filter_chain: &Arc<Mutex<Vec<String>>>,
|
||||||
) -> Media {
|
) -> Media {
|
||||||
let duration = node.out - node.seek;
|
|
||||||
|
|
||||||
if valid_source(&node.source) {
|
if valid_source(&node.source) {
|
||||||
node.add_probe();
|
node.add_probe();
|
||||||
|
|
||||||
@ -472,17 +470,12 @@ fn gen_source(
|
|||||||
.filter(|c| IMAGE_FORMAT.contains(&c.as_str()))
|
.filter(|c| IMAGE_FORMAT.contains(&c.as_str()))
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
let cmd = loop_image(&node.source, duration);
|
node.cmd = Some(loop_image(&node));
|
||||||
node.cmd = Some(cmd);
|
|
||||||
} else {
|
} else {
|
||||||
node.cmd = Some(seek_and_length(
|
node.cmd = Some(seek_and_length(&node));
|
||||||
node.source.clone(),
|
|
||||||
node.seek,
|
|
||||||
node.out,
|
|
||||||
node.duration,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let duration = node.out - node.seek;
|
||||||
let probe = MediaProbe::new(&config.storage.filler_clip);
|
let probe = MediaProbe::new(&config.storage.filler_clip);
|
||||||
|
|
||||||
if node.source.is_empty() {
|
if node.source.is_empty() {
|
||||||
@ -499,9 +492,8 @@ fn gen_source(
|
|||||||
.filter(|c| IMAGE_FORMAT.contains(&c.as_str()))
|
.filter(|c| IMAGE_FORMAT.contains(&c.as_str()))
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
let cmd = loop_image(&config.storage.filler_clip, duration);
|
|
||||||
node.source = config.storage.filler_clip.clone();
|
node.source = config.storage.filler_clip.clone();
|
||||||
node.cmd = Some(cmd);
|
node.cmd = Some(loop_image(&node));
|
||||||
node.probe = Some(probe);
|
node.probe = Some(probe);
|
||||||
} else if let Some(length) = probe
|
} else if let Some(length) = probe
|
||||||
.clone()
|
.clone()
|
||||||
@ -510,9 +502,11 @@ fn gen_source(
|
|||||||
.and_then(|d| d.parse::<f64>().ok())
|
.and_then(|d| d.parse::<f64>().ok())
|
||||||
{
|
{
|
||||||
// create placeholder from config filler.
|
// create placeholder from config filler.
|
||||||
let cmd = loop_input(&config.storage.filler_clip, length, duration);
|
|
||||||
node.source = config.storage.filler_clip.clone();
|
node.source = config.storage.filler_clip.clone();
|
||||||
node.cmd = Some(cmd);
|
node.duration = length;
|
||||||
|
node.out = duration;
|
||||||
|
node.cmd = Some(loop_filler(&node));
|
||||||
node.probe = Some(probe);
|
node.probe = Some(probe);
|
||||||
} else {
|
} else {
|
||||||
// create colored placeholder.
|
// create colored placeholder.
|
||||||
@ -578,12 +572,7 @@ fn handle_list_end(
|
|||||||
node.source
|
node.source
|
||||||
);
|
);
|
||||||
node.out = out;
|
node.out = out;
|
||||||
node.cmd = Some(seek_and_length(
|
node.cmd = Some(seek_and_length(&node));
|
||||||
node.source.clone(),
|
|
||||||
node.seek,
|
|
||||||
node.out,
|
|
||||||
node.duration,
|
|
||||||
));
|
|
||||||
|
|
||||||
node.process = Some(false);
|
node.process = Some(false);
|
||||||
|
|
||||||
|
@ -92,9 +92,10 @@ pub fn player(
|
|||||||
}
|
}
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Play for <yellow>{}</>: <b><magenta>{}</></b>",
|
"Play for <yellow>{}</>: <b><magenta>{} {}</></b>",
|
||||||
sec_to_time(node.out - node.seek),
|
sec_to_time(node.out - node.seek),
|
||||||
node.source
|
node.source,
|
||||||
|
node.audio
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut filter = node.filter.unwrap();
|
let mut filter = node.filter.unwrap();
|
||||||
|
@ -10,7 +10,7 @@ pub mod ingest_filter;
|
|||||||
pub mod v_drawtext;
|
pub mod v_drawtext;
|
||||||
pub mod v_overlay;
|
pub mod v_overlay;
|
||||||
|
|
||||||
use crate::utils::{get_delta, is_close, Media, PlayoutConfig};
|
use crate::utils::{get_delta, is_close, Media, MediaProbe, PlayoutConfig};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Filters {
|
struct Filters {
|
||||||
@ -218,6 +218,7 @@ fn add_audio(node: &mut Media, chain: &mut Filters) {
|
|||||||
.and_then(|p| p.audio_streams.as_ref())
|
.and_then(|p| p.audio_streams.as_ref())
|
||||||
.unwrap_or(&vec![])
|
.unwrap_or(&vec![])
|
||||||
.is_empty()
|
.is_empty()
|
||||||
|
&& !Path::new(&node.audio).is_file()
|
||||||
{
|
{
|
||||||
warn!("Clip <b><magenta>{}</></b> has no audio!", node.source);
|
warn!("Clip <b><magenta>{}</></b> has no audio!", node.source);
|
||||||
let audio = format!(
|
let audio = format!(
|
||||||
@ -229,11 +230,15 @@ fn add_audio(node: &mut Media, chain: &mut Filters) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn extend_audio(node: &mut Media, chain: &mut Filters) {
|
fn extend_audio(node: &mut Media, chain: &mut Filters) {
|
||||||
if let Some(audio_duration) = node
|
let probe = if Path::new(&node.audio).is_file() {
|
||||||
.probe
|
Some(MediaProbe::new(&node.audio))
|
||||||
.as_ref()
|
} else {
|
||||||
.and_then(|p| p.audio_streams.as_ref())
|
node.probe.clone()
|
||||||
.and_then(|a| a[0].duration.as_ref())
|
};
|
||||||
|
|
||||||
|
if let Some(audio_duration) = probe
|
||||||
|
.and_then(|p| p.audio_streams)
|
||||||
|
.and_then(|a| a[0].duration.clone())
|
||||||
.and_then(|a| a.parse::<f64>().ok())
|
.and_then(|a| a.parse::<f64>().ok())
|
||||||
{
|
{
|
||||||
if node.out - node.seek > audio_duration - node.seek + 0.1 && node.duration >= node.out {
|
if node.out - node.seek > audio_duration - node.seek + 0.1 && node.duration >= node.out {
|
||||||
@ -323,7 +328,7 @@ pub fn filter_chains(
|
|||||||
let mut filters = Filters::new();
|
let mut filters = Filters::new();
|
||||||
|
|
||||||
if let Some(probe) = node.probe.as_ref() {
|
if let Some(probe) = node.probe.as_ref() {
|
||||||
if probe.audio_streams.is_some() {
|
if probe.audio_streams.is_some() && !Path::new(&node.audio).is_file() {
|
||||||
filters.audio_map = "0:a".to_string();
|
filters.audio_map = "0:a".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! vec_strings {
|
macro_rules! vec_strings {
|
||||||
($($str:expr),*) => ({
|
($($str:expr),*) => ({
|
||||||
vec![$(String::from($str),)*] as Vec<String>
|
vec![$($str.to_string(),)*] as Vec<String>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,7 @@ fn loop_playlist(
|
|||||||
duration: item.duration,
|
duration: item.duration,
|
||||||
category: item.category.clone(),
|
category: item.category.clone(),
|
||||||
source: item.source.clone(),
|
source: item.source.clone(),
|
||||||
|
audio: item.audio.clone(),
|
||||||
cmd: item.cmd.clone(),
|
cmd: item.cmd.clone(),
|
||||||
probe: item.probe.clone(),
|
probe: item.probe.clone(),
|
||||||
process: Some(true),
|
process: Some(true),
|
||||||
|
@ -54,6 +54,9 @@ pub struct Media {
|
|||||||
#[serde(deserialize_with = "null_string")]
|
#[serde(deserialize_with = "null_string")]
|
||||||
pub source: String,
|
pub source: String,
|
||||||
|
|
||||||
|
#[serde(default, deserialize_with = "null_string")]
|
||||||
|
pub audio: String,
|
||||||
|
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
pub cmd: Option<Vec<String>>,
|
pub cmd: Option<Vec<String>>,
|
||||||
|
|
||||||
@ -98,6 +101,7 @@ impl Media {
|
|||||||
duration,
|
duration,
|
||||||
category: String::new(),
|
category: String::new(),
|
||||||
source: src.clone(),
|
source: src.clone(),
|
||||||
|
audio: String::new(),
|
||||||
cmd: Some(vec!["-i".to_string(), src]),
|
cmd: Some(vec!["-i".to_string(), src]),
|
||||||
filter: Some(vec![]),
|
filter: Some(vec![]),
|
||||||
probe,
|
probe,
|
||||||
@ -395,31 +399,79 @@ pub fn check_sync(config: &PlayoutConfig, delta: f64) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Loop image until target duration is reached.
|
/// Loop image until target duration is reached.
|
||||||
pub fn loop_image(source: &str, duration: f64) -> Vec<String> {
|
pub fn loop_image(node: &Media) -> Vec<String> {
|
||||||
info!("Loop image <b><magenta>{source}</></b>, total duration: <yellow>{duration:.2}</>");
|
let duration = node.out - node.seek;
|
||||||
|
let mut source_cmd: Vec<String> = vec_strings!["-loop", "1", "-i", node.source.clone()];
|
||||||
|
|
||||||
vec_strings!["-loop", "1", "-i", source, "-t", duration.to_string()]
|
info!(
|
||||||
}
|
"Loop image <b><magenta>{}</></b>, total duration: <yellow>{duration:.2}</>",
|
||||||
|
node.source
|
||||||
|
);
|
||||||
|
|
||||||
/// Loop source until target duration is reached.
|
if Path::new(&node.audio).is_file() {
|
||||||
pub fn loop_input(source: &str, source_duration: f64, target_duration: f64) -> Vec<String> {
|
if node.seek > 0.0 {
|
||||||
let loop_count = (target_duration / source_duration).ceil() as i32;
|
source_cmd.append(&mut vec_strings!["-ss", node.seek])
|
||||||
let mut cmd = vec![];
|
}
|
||||||
|
|
||||||
if loop_count > 1 {
|
source_cmd.append(&mut vec_strings!["-i", node.audio.clone()]);
|
||||||
info!("Loop <b><magenta>{source}</></b> <yellow>{loop_count}</> times, total duration: <yellow>{target_duration:.2}</>");
|
|
||||||
|
|
||||||
cmd.append(&mut vec_strings!["-stream_loop", loop_count.to_string()]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.append(&mut vec_strings![
|
source_cmd.append(&mut vec_strings!["-t", duration]);
|
||||||
"-i",
|
|
||||||
source,
|
|
||||||
"-t",
|
|
||||||
target_duration.to_string()
|
|
||||||
]);
|
|
||||||
|
|
||||||
cmd
|
source_cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loop filler until target duration is reached.
|
||||||
|
pub fn loop_filler(node: &Media) -> Vec<String> {
|
||||||
|
let loop_count = (node.out / node.duration).ceil() as i32;
|
||||||
|
let mut source_cmd = vec![];
|
||||||
|
|
||||||
|
if loop_count > 1 {
|
||||||
|
info!("Loop <b><magenta>{}</></b> <yellow>{loop_count}</> times, total duration: <yellow>{:.2}</>", node.source, node.out);
|
||||||
|
|
||||||
|
source_cmd.append(&mut vec_strings!["-stream_loop", loop_count]);
|
||||||
|
}
|
||||||
|
|
||||||
|
source_cmd.append(&mut vec_strings!["-i", node.source, "-t", node.out]);
|
||||||
|
|
||||||
|
source_cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set clip seek in and length value.
|
||||||
|
pub fn seek_and_length(node: &Media) -> Vec<String> {
|
||||||
|
let mut source_cmd = vec![];
|
||||||
|
let mut cut_audio = false;
|
||||||
|
|
||||||
|
if node.seek > 0.0 {
|
||||||
|
source_cmd.append(&mut vec_strings!["-ss", node.seek])
|
||||||
|
}
|
||||||
|
|
||||||
|
source_cmd.append(&mut vec_strings!["-i", node.source.clone()]);
|
||||||
|
|
||||||
|
if Path::new(&node.audio).is_file() {
|
||||||
|
let audio_probe = MediaProbe::new(&node.audio);
|
||||||
|
|
||||||
|
if node.seek > 0.0 {
|
||||||
|
source_cmd.append(&mut vec_strings!["-ss", node.seek])
|
||||||
|
}
|
||||||
|
|
||||||
|
source_cmd.append(&mut vec_strings!["-i", node.audio.clone()]);
|
||||||
|
|
||||||
|
if audio_probe
|
||||||
|
.audio_streams
|
||||||
|
.and_then(|a| a[0].duration.clone())
|
||||||
|
.and_then(|d| d.parse::<f64>().ok())
|
||||||
|
> Some(node.out - node.seek)
|
||||||
|
{
|
||||||
|
cut_audio = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.duration > node.out || cut_audio {
|
||||||
|
source_cmd.append(&mut vec_strings!["-t", node.out - node.seek]);
|
||||||
|
}
|
||||||
|
|
||||||
|
source_cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a dummy clip as a placeholder for missing video files.
|
/// Create a dummy clip as a placeholder for missing video files.
|
||||||
@ -446,23 +498,6 @@ pub fn gen_dummy(config: &PlayoutConfig, duration: f64) -> (String, Vec<String>)
|
|||||||
(source, cmd)
|
(source, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set clip seek in and length value.
|
|
||||||
pub fn seek_and_length(src: String, seek: f64, out: f64, duration: f64) -> Vec<String> {
|
|
||||||
let mut source_cmd: Vec<String> = vec![];
|
|
||||||
|
|
||||||
if seek > 0.0 {
|
|
||||||
source_cmd.append(&mut vec!["-ss".to_string(), format!("{seek}")])
|
|
||||||
}
|
|
||||||
|
|
||||||
source_cmd.append(&mut vec!["-i".to_string(), src]);
|
|
||||||
|
|
||||||
if duration > out {
|
|
||||||
source_cmd.append(&mut vec!["-t".to_string(), format!("{}", out - seek)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
source_cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prepare output parameters
|
/// Prepare output parameters
|
||||||
///
|
///
|
||||||
/// seek for multiple outputs and add mapping for it
|
/// seek for multiple outputs and add mapping for it
|
||||||
|
Loading…
x
Reference in New Issue
Block a user