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::{
|
||||
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,
|
||||
};
|
||||
|
||||
@ -460,8 +460,6 @@ fn gen_source(
|
||||
mut node: Media,
|
||||
filter_chain: &Arc<Mutex<Vec<String>>>,
|
||||
) -> Media {
|
||||
let duration = node.out - node.seek;
|
||||
|
||||
if valid_source(&node.source) {
|
||||
node.add_probe();
|
||||
|
||||
@ -472,17 +470,12 @@ fn gen_source(
|
||||
.filter(|c| IMAGE_FORMAT.contains(&c.as_str()))
|
||||
.is_some()
|
||||
{
|
||||
let cmd = loop_image(&node.source, duration);
|
||||
node.cmd = Some(cmd);
|
||||
node.cmd = Some(loop_image(&node));
|
||||
} else {
|
||||
node.cmd = Some(seek_and_length(
|
||||
node.source.clone(),
|
||||
node.seek,
|
||||
node.out,
|
||||
node.duration,
|
||||
));
|
||||
node.cmd = Some(seek_and_length(&node));
|
||||
}
|
||||
} else {
|
||||
let duration = node.out - node.seek;
|
||||
let probe = MediaProbe::new(&config.storage.filler_clip);
|
||||
|
||||
if node.source.is_empty() {
|
||||
@ -499,9 +492,8 @@ fn gen_source(
|
||||
.filter(|c| IMAGE_FORMAT.contains(&c.as_str()))
|
||||
.is_some()
|
||||
{
|
||||
let cmd = loop_image(&config.storage.filler_clip, duration);
|
||||
node.source = config.storage.filler_clip.clone();
|
||||
node.cmd = Some(cmd);
|
||||
node.cmd = Some(loop_image(&node));
|
||||
node.probe = Some(probe);
|
||||
} else if let Some(length) = probe
|
||||
.clone()
|
||||
@ -510,9 +502,11 @@ fn gen_source(
|
||||
.and_then(|d| d.parse::<f64>().ok())
|
||||
{
|
||||
// create placeholder from config filler.
|
||||
let cmd = loop_input(&config.storage.filler_clip, length, duration);
|
||||
|
||||
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);
|
||||
} else {
|
||||
// create colored placeholder.
|
||||
@ -578,12 +572,7 @@ fn handle_list_end(
|
||||
node.source
|
||||
);
|
||||
node.out = out;
|
||||
node.cmd = Some(seek_and_length(
|
||||
node.source.clone(),
|
||||
node.seek,
|
||||
node.out,
|
||||
node.duration,
|
||||
));
|
||||
node.cmd = Some(seek_and_length(&node));
|
||||
|
||||
node.process = Some(false);
|
||||
|
||||
|
@ -92,9 +92,10 @@ pub fn player(
|
||||
}
|
||||
|
||||
info!(
|
||||
"Play for <yellow>{}</>: <b><magenta>{}</></b>",
|
||||
"Play for <yellow>{}</>: <b><magenta>{} {}</></b>",
|
||||
sec_to_time(node.out - node.seek),
|
||||
node.source
|
||||
node.source,
|
||||
node.audio
|
||||
);
|
||||
|
||||
let mut filter = node.filter.unwrap();
|
||||
|
@ -10,7 +10,7 @@ pub mod ingest_filter;
|
||||
pub mod v_drawtext;
|
||||
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)]
|
||||
struct Filters {
|
||||
@ -218,6 +218,7 @@ fn add_audio(node: &mut Media, chain: &mut Filters) {
|
||||
.and_then(|p| p.audio_streams.as_ref())
|
||||
.unwrap_or(&vec![])
|
||||
.is_empty()
|
||||
&& !Path::new(&node.audio).is_file()
|
||||
{
|
||||
warn!("Clip <b><magenta>{}</></b> has no audio!", node.source);
|
||||
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) {
|
||||
if let Some(audio_duration) = node
|
||||
.probe
|
||||
.as_ref()
|
||||
.and_then(|p| p.audio_streams.as_ref())
|
||||
.and_then(|a| a[0].duration.as_ref())
|
||||
let probe = if Path::new(&node.audio).is_file() {
|
||||
Some(MediaProbe::new(&node.audio))
|
||||
} else {
|
||||
node.probe.clone()
|
||||
};
|
||||
|
||||
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())
|
||||
{
|
||||
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();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#[macro_export]
|
||||
macro_rules! vec_strings {
|
||||
($($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,
|
||||
category: item.category.clone(),
|
||||
source: item.source.clone(),
|
||||
audio: item.audio.clone(),
|
||||
cmd: item.cmd.clone(),
|
||||
probe: item.probe.clone(),
|
||||
process: Some(true),
|
||||
|
@ -54,6 +54,9 @@ pub struct Media {
|
||||
#[serde(deserialize_with = "null_string")]
|
||||
pub source: String,
|
||||
|
||||
#[serde(default, deserialize_with = "null_string")]
|
||||
pub audio: String,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub cmd: Option<Vec<String>>,
|
||||
|
||||
@ -98,6 +101,7 @@ impl Media {
|
||||
duration,
|
||||
category: String::new(),
|
||||
source: src.clone(),
|
||||
audio: String::new(),
|
||||
cmd: Some(vec!["-i".to_string(), src]),
|
||||
filter: Some(vec![]),
|
||||
probe,
|
||||
@ -395,31 +399,79 @@ pub fn check_sync(config: &PlayoutConfig, delta: f64) -> bool {
|
||||
}
|
||||
|
||||
/// Loop image until target duration is reached.
|
||||
pub fn loop_image(source: &str, duration: f64) -> Vec<String> {
|
||||
info!("Loop image <b><magenta>{source}</></b>, total duration: <yellow>{duration:.2}</>");
|
||||
pub fn loop_image(node: &Media) -> Vec<String> {
|
||||
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.
|
||||
pub fn loop_input(source: &str, source_duration: f64, target_duration: f64) -> Vec<String> {
|
||||
let loop_count = (target_duration / source_duration).ceil() as i32;
|
||||
let mut cmd = vec![];
|
||||
if Path::new(&node.audio).is_file() {
|
||||
if node.seek > 0.0 {
|
||||
source_cmd.append(&mut vec_strings!["-ss", node.seek])
|
||||
}
|
||||
|
||||
if loop_count > 1 {
|
||||
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()]);
|
||||
source_cmd.append(&mut vec_strings!["-i", node.audio.clone()]);
|
||||
}
|
||||
|
||||
cmd.append(&mut vec_strings![
|
||||
"-i",
|
||||
source,
|
||||
"-t",
|
||||
target_duration.to_string()
|
||||
]);
|
||||
source_cmd.append(&mut vec_strings!["-t", duration]);
|
||||
|
||||
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.
|
||||
@ -446,23 +498,6 @@ pub fn gen_dummy(config: &PlayoutConfig, duration: f64) -> (String, Vec<String>)
|
||||
(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
|
||||
///
|
||||
/// seek for multiple outputs and add mapping for it
|
||||
|
Loading…
x
Reference in New Issue
Block a user