diff --git a/assets/advanced.yml b/assets/advanced.yml index a2cc531a..c883356b 100644 --- a/assets/advanced.yml +++ b/assets/advanced.yml @@ -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={} diff --git a/docs/advanced_settings.md b/docs/advanced_settings.md index dfe5e51e..5aca7a0f 100644 --- a/docs/advanced_settings.md +++ b/docs/advanced_settings.md @@ -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.** diff --git a/ffplayout-engine/src/input/playlist.rs b/ffplayout-engine/src/input/playlist.rs index b3c24e55..dc68251b 100644 --- a/ffplayout-engine/src/input/playlist.rs +++ b/ffplayout-engine/src/input/playlist.rs @@ -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() && ¤t_list[index + 1].category == "advertisement" { - self.current_node.next_ad = true; + node.next_ad = true; } if index > 0 && index < current_list.len() && ¤t_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()) diff --git a/lib/src/filter/mod.rs b/lib/src/filter/mod.rs index 0bfc7c78..c62b69ec 100644 --- a/lib/src/filter/mod.rs +++ b/lib/src/filter/mod.rs @@ -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); } } diff --git a/lib/src/utils/advanced_config.rs b/lib/src/utils/advanced_config.rs index 24017ed3..031801ef 100644 --- a/lib/src/utils/advanced_config.rs +++ b/lib/src/utils/advanced_config.rs @@ -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);