support animated logo, #183
This commit is contained in:
parent
1f27cc6b51
commit
a83f81bef2
@ -179,8 +179,8 @@ fn extend_video(node: &mut Media, chain: &mut Filters) {
|
||||
if let Some(video_duration) = node
|
||||
.probe
|
||||
.as_ref()
|
||||
.and_then(|p| p.video_streams.as_ref())
|
||||
.and_then(|v| v[0].duration.as_ref())
|
||||
.and_then(|p| p.video_streams.get(0))
|
||||
.and_then(|v| v.duration.as_ref())
|
||||
.and_then(|v| v.parse::<f64>().ok())
|
||||
{
|
||||
if node.out - node.seek > video_duration - node.seek + 0.1 && node.duration >= node.out {
|
||||
@ -215,9 +215,8 @@ fn add_audio(node: &mut Media, chain: &mut Filters) {
|
||||
if node
|
||||
.probe
|
||||
.as_ref()
|
||||
.and_then(|p| p.audio_streams.as_ref())
|
||||
.unwrap_or(&vec![])
|
||||
.is_empty()
|
||||
.and_then(|p| p.audio_streams.get(0))
|
||||
.is_none()
|
||||
&& !Path::new(&node.audio).is_file()
|
||||
{
|
||||
warn!("Clip <b><magenta>{}</></b> has no audio!", node.source);
|
||||
@ -237,8 +236,9 @@ fn extend_audio(node: &mut Media, chain: &mut Filters) {
|
||||
};
|
||||
|
||||
if let Some(audio_duration) = probe
|
||||
.and_then(|p| p.audio_streams)
|
||||
.and_then(|a| a[0].duration.clone())
|
||||
.as_ref()
|
||||
.and_then(|p| p.audio_streams.get(0))
|
||||
.and_then(|a| a.duration.clone())
|
||||
.and_then(|a| a.parse::<f64>().ok())
|
||||
{
|
||||
if node.out - node.seek > audio_duration - node.seek + 0.1 && node.duration >= node.out {
|
||||
@ -250,12 +250,11 @@ fn extend_audio(node: &mut Media, chain: &mut Filters) {
|
||||
/// Add single pass loudnorm filter to audio line.
|
||||
fn add_loudnorm(node: &mut Media, chain: &mut Filters, config: &PlayoutConfig) {
|
||||
if config.processing.add_loudnorm
|
||||
&& !node
|
||||
&& node
|
||||
.probe
|
||||
.as_ref()
|
||||
.and_then(|p| p.audio_streams.as_ref())
|
||||
.unwrap_or(&vec![])
|
||||
.is_empty()
|
||||
.and_then(|p| p.audio_streams.get(0))
|
||||
.is_none()
|
||||
{
|
||||
let loud_filter = a_loudnorm::filter_node(config);
|
||||
chain.add_filter(&loud_filter, "audio");
|
||||
@ -331,13 +330,11 @@ pub fn filter_chains(
|
||||
let mut filters = Filters::new();
|
||||
|
||||
if let Some(probe) = node.probe.as_ref() {
|
||||
if probe.audio_streams.is_some() && !Path::new(&node.audio).is_file() {
|
||||
if probe.audio_streams.get(0).is_some() && !Path::new(&node.audio).is_file() {
|
||||
filters.audio_map = "0:a".to_string();
|
||||
}
|
||||
|
||||
if let Some(v_streams) = &probe.video_streams.as_ref() {
|
||||
let v_stream = &v_streams[0];
|
||||
|
||||
if let Some(v_stream) = &probe.video_streams.get(0) {
|
||||
let aspect = aspect_calc(&v_stream.display_aspect_ratio, config);
|
||||
let frame_per_sec = fps_calc(&v_stream.r_frame_rate);
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::utils::PlayoutConfig;
|
||||
|
||||
/// Overlay Filter
|
||||
@ -8,14 +6,18 @@ use crate::utils::PlayoutConfig;
|
||||
pub fn filter_node(config: &PlayoutConfig, add_tail: bool) -> String {
|
||||
let mut logo_chain = String::new();
|
||||
|
||||
if config.processing.add_logo && Path::new(&config.processing.logo).is_file() {
|
||||
if !config.processing.add_logo {
|
||||
return logo_chain;
|
||||
}
|
||||
|
||||
if let Some(fps) = config.processing.logo_fps.clone() {
|
||||
let opacity = format!(
|
||||
"format=rgba,colorchannelmixer=aa={}",
|
||||
config.processing.logo_opacity
|
||||
);
|
||||
let logo_loop = "loop=loop=-1:size=1:start=0";
|
||||
let pts = format!("setpts=N/({fps}*TB)");
|
||||
logo_chain = format!(
|
||||
"null[v];movie={},{logo_loop},{opacity}",
|
||||
"null[v];movie={}:loop=0,{pts},{opacity}",
|
||||
config.processing.logo
|
||||
);
|
||||
|
||||
@ -24,7 +26,7 @@ pub fn filter_node(config: &PlayoutConfig, add_tail: bool) -> String {
|
||||
format!("[l];[v][l]{}:shortest=1", config.processing.logo_filter).as_str(),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
logo_chain
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use std::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use shlex::split;
|
||||
|
||||
use crate::utils::{free_tcp_socket, home_dir, time_to_sec};
|
||||
use crate::utils::{free_tcp_socket, home_dir, time_to_sec, MediaProbe};
|
||||
use crate::vec_strings;
|
||||
|
||||
pub const DUMMY_LEN: f64 = 60.0;
|
||||
@ -89,6 +89,10 @@ pub struct Processing {
|
||||
pub fps: f64,
|
||||
pub add_logo: bool,
|
||||
pub logo: String,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub logo_fps: Option<String>,
|
||||
|
||||
pub logo_scale: String,
|
||||
pub logo_opacity: f32,
|
||||
pub logo_filter: String,
|
||||
@ -221,6 +225,19 @@ impl PlayoutConfig {
|
||||
config.playlist.length_sec = Some(86400.0);
|
||||
}
|
||||
|
||||
config.processing.logo_fps = None;
|
||||
|
||||
if Path::new(&config.processing.logo).is_file() {
|
||||
if let Some(fps) = MediaProbe::new(&config.processing.logo)
|
||||
.video_streams
|
||||
.get(0)
|
||||
.and_then(|v| v.r_frame_rate.split_once('/'))
|
||||
.map(|f| f.0)
|
||||
{
|
||||
config.processing.logo_fps = Some(fps.to_string());
|
||||
};
|
||||
}
|
||||
|
||||
// We set the decoder settings here, so we only define them ones.
|
||||
let mut settings = vec_strings![
|
||||
"-pix_fmt",
|
||||
|
@ -151,6 +151,12 @@ impl ProcessControl {
|
||||
}
|
||||
}
|
||||
|
||||
// impl Drop for ProcessControl {
|
||||
// fn drop(&mut self) {
|
||||
// self.kill_all()
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Global player control, to get infos about current clip etc.
|
||||
#[derive(Clone)]
|
||||
pub struct PlayerControl {
|
||||
|
@ -166,8 +166,8 @@ where
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct MediaProbe {
|
||||
pub format: Option<Format>,
|
||||
pub audio_streams: Option<Vec<Stream>>,
|
||||
pub video_streams: Option<Vec<Stream>>,
|
||||
pub audio_streams: Vec<Stream>,
|
||||
pub video_streams: Vec<Stream>,
|
||||
}
|
||||
|
||||
impl MediaProbe {
|
||||
@ -194,16 +194,8 @@ impl MediaProbe {
|
||||
|
||||
MediaProbe {
|
||||
format: Some(obj.format),
|
||||
audio_streams: if !a_stream.is_empty() {
|
||||
Some(a_stream)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
video_streams: if !v_stream.is_empty() {
|
||||
Some(v_stream)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
audio_streams: a_stream,
|
||||
video_streams: v_stream,
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
@ -213,8 +205,8 @@ impl MediaProbe {
|
||||
|
||||
MediaProbe {
|
||||
format: None,
|
||||
audio_streams: None,
|
||||
video_streams: None,
|
||||
audio_streams: vec![],
|
||||
video_streams: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -468,11 +460,12 @@ pub fn seek_and_length(node: &Media) -> Vec<String> {
|
||||
|
||||
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)
|
||||
if !audio_probe.audio_streams.is_empty()
|
||||
&& audio_probe.audio_streams[0]
|
||||
.duration
|
||||
.clone()
|
||||
.and_then(|d| d.parse::<f64>().ok())
|
||||
> Some(node.out - node.seek)
|
||||
{
|
||||
cut_audio = true;
|
||||
}
|
||||
@ -593,7 +586,7 @@ pub fn is_remote(path: &str) -> bool {
|
||||
///
|
||||
/// Check if input is a remote source, or from storage and see if it exists.
|
||||
pub fn valid_source(source: &str) -> bool {
|
||||
if is_remote(source) && MediaProbe::new(source).video_streams.is_some() {
|
||||
if is_remote(source) && !MediaProbe::new(source).video_streams.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user