fix ad checker and right logo fade, adjust overlay filter for easier HW support

This commit is contained in:
jb-alvarado 2024-04-18 16:09:49 +02:00
parent 528861db60
commit 1a3d0f7839
5 changed files with 103 additions and 46 deletions

View File

@ -14,7 +14,7 @@ decoder:
fade_in: # fade=in:st=0:d=0.5
fade_out: # fade=out:st={}:d=1.0
overlay_logo_scale: # scale={}
overlay_logo: # null[v];movie={}:loop=0,setpts=N/(FRAME_RATE*TB),format=rgba,colorchannelmixer=aa={}{}[l];[v][l]overlay={}:shortest=1
overlay_logo: # null[l];[v][l]overlay={}:shortest=1
overlay_logo_fade_in: # fade=in:st=0:d=1.0:alpha=1
overlay_logo_fade_out: # fade=out:st={}:d=1.0:alpha=1
tpad: # tpad=stop_mode=add:stop_duration={}

View File

@ -12,7 +12,7 @@ For changing this settings you need to have knowledge about hardware encoding wi
### Example config
Here an example with Intel QuickSync:
##### Here an example with Intel QuickSync:
```YAML
help: Changing these settings is for advanced users only! There will be no support or guarantee that ffplayout will be stable after changing them.
@ -30,10 +30,10 @@ decoder:
set_dar: 'null' # setdar=dar={}
fade_in: 'null' # fade=in:st=0:d=0.5
fade_out: 'null' # fade=out:st={}:d=1.0
overlay_logo_scale: scale_qsv={}
overlay_logo: null[v];movie={}:loop=0,setpts=N/(FRAME_RATE*TB),format=rgba,colorchannelmixer=aa={}{},hwupload=extra_hw_frames=64,format=qsv[l];[v][l]overlay_qsv={}:shortest=1
overlay_logo_fade_in: 'null' # fade=in:st=0:d=1.0:alpha=1
overlay_logo_fade_out: 'null' # fade=out:st={}:d=1.0:alpha=1
overlay_logo_scale: 'null'
overlay_logo_fade_in: fade=in:st=0:d=1.0 # fade=in:st=0:d=1.0:alpha=1
overlay_logo_fade_out: fade=out:st={}:d=1.0 # fade=out:st={}:d=1.0:alpha=1
overlay_logo: hwupload=extra_hw_frames=64,format=qsv[l];[v][l]overlay_qsv={}:shortest=1
tpad: 'null' # tpad=stop_mode=add:stop_duration={}
drawtext_from_file: hwdownload,format=nv12,drawtext=text='{}':{}{} # drawtext=text='{}':{}{}
drawtext_from_zmq: hwdownload,format=nv12,zmq=b=tcp\\://'{}',drawtext@dyntext={} # zmq=b=tcp\\\\://'{}',drawtext@dyntext={}
@ -50,6 +50,43 @@ ingest:
input_param: -hwaccel qsv -init_hw_device qsv=hw -filter_hw_device hw -hwaccel_output_format qsv
```
##### Here an example with Nvidia HW processing
```YAML
help: Changing these settings is for advanced users only! There will be no support or guarantee that it will be stable after changing them.
decoder:
input_param: -thread_queue_size 1024 -hwaccel_device 0 -hwaccel cuvid -hwaccel_output_format cuda
# output_param get also applied to ingest instance.
output_param: -c:v h264_nvenc -preset p2 -tune ll -b:v 50000k -minrate 50000k -maxrate 50000k -bufsize 25000k -c:a s302m -strict -2 -sample_fmt s16 -ar 48000 -ac 2
filters:
deinterlace: 'null'
pad_scale_w: 'null' # scale={}:-1
pad_scale_h: 'null' # scale=-1:{}
pad_video: 'null' # pad=max(iw\\,ih*({0}/{1})):ow/({0}/{1}):(ow-iw)/2:(oh-ih)/2
fps: 'null' # fps={}
scale: scale_cuda={}:{}:interp_algo=lanczos:force_original_aspect_ratio=decrease # scale={}:{}
set_dar: 'null' # setdar=dar={}
fade_in: hwdownload,format=nv12,fade=in:st=0:d=0.5,format=nv12,hwupload_cuda # fade=in:st=0:d=0.5
fade_out: hwdownload,format=nv12,fade=out:st={}:d=1.0,format=nv12,hwupload_cuda # fade=out:st={}:d=1.0
overlay_logo_scale: 'null' # scale={}
overlay_logo_fade_in: fade=in:st=0:d=1.0 # fade=in:st=0:d=1.0:alpha=1
overlay_logo_fade_out: fade=out:st={}:d=1.0 # fade=out:st={}:d=1.0:alpha=1
overlay_logo: format=nv12,hwupload_cuda[l];[v][l]overlay_cuda=W-w-12:12:shortest=1,hwdownload,format=nv12
tpad: # tpad=stop_mode=add:stop_duration={}
drawtext_from_file: # drawtext=text='{}':{}{}
drawtext_from_zmq: # zmq=b=tcp\\\\://'{}',drawtext@dyntext={}
aevalsrc: # aevalsrc=0:channel_layout=stereo:duration={}:sample_rate=48000
afade_in: # afade=in:st=0:d=0.5
afade_out: # afade=out:st={}:d=1.0
apad: # apad=whole_dur={}
volume: # volume={}
split: # split={}{}
encoder:
input_param:
ingest:
input_param: -thread_queue_size 1024 -hwaccel_device 0 -hwaccel cuvid -hwaccel_output_format cuda
```
---
**At the moment this function is _experimental_, if you think you found a bug: check full decoder/encoder/ingest command with ffmpeg in terminal. When there the command works you can open a bug report issue.**

View File

@ -186,19 +186,19 @@ impl CurrentProgram {
}
// Check if last and/or next clip is a advertisement.
fn last_next_ad(&mut self) {
fn last_next_ad(&mut self, node: &mut Media) {
let index = self.player_control.current_index.load(Ordering::SeqCst);
let current_list = self.player_control.current_list.lock().unwrap();
if index + 1 < current_list.len() && &current_list[index + 1].category == "advertisement" {
self.current_node.next_ad = true;
node.next_ad = true;
}
if index > 0
&& index < current_list.len()
&& &current_list[index - 1].category == "advertisement"
{
self.current_node.last_ad = true;
node.last_ad = true;
}
}
@ -259,10 +259,7 @@ impl CurrentProgram {
if !self.playout_stat.list_init.load(Ordering::SeqCst) {
let time_sec = self.get_current_time();
let index = self
.player_control
.current_index
.fetch_add(1, Ordering::SeqCst);
let index = self.player_control.current_index.load(Ordering::SeqCst);
let nodes = self.player_control.current_list.lock().unwrap();
let last_index = nodes.len() - 1;
@ -277,6 +274,12 @@ impl CurrentProgram {
node_clone.seek += time_sec
- (node_clone.begin.unwrap() - *self.playout_stat.time_shift.lock().unwrap());
self.last_next_ad(&mut node_clone);
self.player_control
.current_index
.fetch_add(1, Ordering::SeqCst);
self.current_node = handle_list_init(
&self.config,
node_clone,
@ -306,6 +309,8 @@ impl CurrentProgram {
media.duration = total_delta;
media.out = total_delta;
self.last_next_ad(&mut media);
self.current_node = gen_source(
&self.config,
media,
@ -319,7 +324,6 @@ impl CurrentProgram {
.lock()
.unwrap()
.push(self.current_node.clone());
self.last_next_ad();
self.current_node.last_ad = self.last_node_ad;
self.current_node
@ -386,6 +390,8 @@ impl Iterator for CurrentProgram {
media.duration = total_delta;
media.out = total_delta;
self.last_next_ad(&mut media);
self.current_node = gen_source(
&self.config,
media,
@ -395,8 +401,6 @@ impl Iterator for CurrentProgram {
);
}
self.last_next_ad();
return Some(self.current_node.clone());
}
@ -408,7 +412,7 @@ impl Iterator for CurrentProgram {
let mut is_last = false;
let index = self.player_control.current_index.load(Ordering::SeqCst);
let node_list = self.player_control.current_list.lock().unwrap();
let node = node_list[index].clone();
let mut node = node_list[index].clone();
let last_index = node_list.len() - 1;
drop(node_list);
@ -417,6 +421,8 @@ impl Iterator for CurrentProgram {
is_last = true
}
self.last_next_ad(&mut node);
self.current_node = timed_source(
node,
&self.config,
@ -426,7 +432,6 @@ impl Iterator for CurrentProgram {
last_index,
);
self.last_next_ad();
self.player_control
.current_index
.fetch_add(1, Ordering::SeqCst);
@ -450,7 +455,7 @@ impl Iterator for CurrentProgram {
// Get first clip from next playlist.
let c_list = self.player_control.current_list.lock().unwrap();
let first_node = c_list[0].clone();
let mut first_node = c_list[0].clone();
drop(c_list);
@ -459,6 +464,9 @@ impl Iterator for CurrentProgram {
}
self.player_control.current_index.store(0, Ordering::SeqCst);
self.last_next_ad(&mut first_node);
first_node.last_ad = self.last_node_ad;
self.current_node = gen_source(
&self.config,
first_node,
@ -466,8 +474,7 @@ impl Iterator for CurrentProgram {
&self.player_control,
0,
);
self.last_next_ad();
self.current_node.last_ad = self.last_node_ad;
self.player_control.current_index.store(1, Ordering::SeqCst);
Some(self.current_node.clone())

View File

@ -349,35 +349,21 @@ fn overlay(node: &mut Media, chain: &mut Filters, config: &PlayoutConfig) {
&& Path::new(&config.processing.logo).is_file()
&& &node.category != "advertisement"
{
let mut scale = String::new();
if !config.processing.logo_scale.is_empty() {
scale = match &ADVANCED_CONFIG.decoder.filters.overlay_logo_scale {
Some(logo_scale) => {
custom_format(&format!(",{logo_scale}"), &[&config.processing.logo_scale])
}
None => format!(",scale={}", config.processing.logo_scale),
}
}
let mut logo_chain = match &ADVANCED_CONFIG.decoder.filters.overlay_logo {
Some(overlay) => custom_format(overlay, &[
&config.processing.logo.replace('\\', "/").replace(':', "\\\\:"),
&config.processing.logo_opacity.to_string(),
&scale,
&config.processing.logo_position,
]),
None => format!(
"null[v];movie={}:loop=0,setpts=N/(FRAME_RATE*TB),format=rgba,colorchannelmixer=aa={}{scale}[l];[v][l]overlay={}:shortest=1",
config.processing.logo.replace('\\', "/").replace(':', "\\\\:"), config.processing.logo_opacity, config.processing.logo_position
)
};
let mut logo_chain = format!(
"null[v];movie={}:loop=0,setpts=N/(FRAME_RATE*TB),format=rgba,colorchannelmixer=aa={}",
config
.processing
.logo
.replace('\\', "/")
.replace(':', "\\\\:"),
config.processing.logo_opacity,
);
if node.last_ad {
match &ADVANCED_CONFIG.decoder.filters.overlay_logo_fade_in {
Some(fade_in) => logo_chain.push_str(&format!(",{fade_in}")),
None => logo_chain.push_str(",fade=in:st=0:d=1.0:alpha=1"),
}
};
}
if node.next_ad {
@ -391,6 +377,30 @@ fn overlay(node: &mut Media, chain: &mut Filters, config: &PlayoutConfig) {
}
}
if !config.processing.logo_scale.is_empty() {
match &ADVANCED_CONFIG.decoder.filters.overlay_logo_scale {
Some(logo_scale) => logo_chain.push_str(&custom_format(
&format!(",{logo_scale}"),
&[&config.processing.logo_scale],
)),
None => logo_chain.push_str(&format!(",scale={}", config.processing.logo_scale)),
}
}
match &ADVANCED_CONFIG.decoder.filters.overlay_logo {
Some(overlay) => {
if !overlay.starts_with(',') {
logo_chain.push_str(",");
}
logo_chain.push_str(&custom_format(overlay, &[&config.processing.logo_position]))
}
None => logo_chain.push_str(&format!(
"[l];[v][l]overlay={}:shortest=1",
config.processing.logo_position
)),
};
chain.add_filter(&logo_chain, 0, Video);
}
}

View File

@ -80,7 +80,10 @@ impl AdvancedConfig {
}
if let Ok(f) = File::open(&config_path) {
config = serde_yaml::from_reader(f).expect("Could not read advanced config file");
config = match serde_yaml::from_reader(f) {
Ok(yaml) => yaml,
Err(_) => AdvancedConfig::default(),
};
if let Some(input_parm) = &config.decoder.input_param {
config.decoder.input_cmd = split(input_parm);