support animated logo, #183

This commit is contained in:
jb-alvarado 2022-08-29 22:24:09 +02:00
parent 1f27cc6b51
commit a83f81bef2
5 changed files with 57 additions and 42 deletions

View File

@ -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);

View File

@ -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
}

View File

@ -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",

View File

@ -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 {

View File

@ -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;
}