From daa6fc3a4a8e2c98768702dad71aeacc0b2984f8 Mon Sep 17 00:00:00 2001 From: yajrendrag Date: Sun, 15 Mar 2026 20:37:23 -0600 Subject: [PATCH] [video_transcoder] 0.1.18 --- changelog.md | 7 +++++++ info.json | 2 +- lib/encoders/qsv.py | 30 ++++++++++++++++++++++++++++-- lib/encoders/vaapi.py | 30 ++++++++++++++++++++++++++++-- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index bc78eb2..f5a3742 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,11 @@ +**0.1.18** +- adds similar logic for qsv and vaapi as was done for nvenc in 0.1.17 +- adds new qsv_safe_decode and vaapi_safe_decode options to the respective encoders +- adds new paths in their respective define_filtergraph functions to move the frames off the GPU to avoid h/w accel failure due to midstream colorspace changes +- adds '-reinit_filter 0' option to the ffmpeg command line when the x_safe_option is enabled so that ffmpeg can ignore the filter change that would have resulted from the colorspace change +- the combination of these allows ffmpeg to successfully process the file rather than failing + **0.1.17** - adds nvenc_safe_decode option to allow plugin to avoid an error caused by colorspace changes in the file - adds new path in the define_filtergraph function to move the frames off the GPU to avoid h/w accel failure due to midstream colorspace changes diff --git a/info.json b/info.json index 4078728..9c29d34 100644 --- a/info.json +++ b/info.json @@ -12,5 +12,5 @@ "on_worker_process": 1 }, "tags": "video,ffmpeg", - "version": "0.1.17" + "version": "0.1.18" } diff --git a/lib/encoders/qsv.py b/lib/encoders/qsv.py index 473fad2..03043f5 100644 --- a/lib/encoders/qsv.py +++ b/lib/encoders/qsv.py @@ -150,6 +150,7 @@ def provides(self): def options(self): return { "qsv_decoding_method": "cpu", + "qsv_safe_decode": False, "qsv_preset": "slow", "qsv_encoder_ratecontrol_method": "LA_ICQ", "qsv_constant_quantizer_scale": "25", @@ -176,6 +177,10 @@ def generate_default_args(self): "-hwaccel": "qsv", "-hwaccel_output_format": "qsv", }) + + if self.settings.get_setting('qsv_safe_decode'): + generic_kwargs["-reinit_filter"] = "0" + return generic_kwargs, advanced_kwargs def generate_filtergraphs(self, current_filter_args, smart_filters, encoder_name): @@ -238,13 +243,22 @@ def generate_filtergraphs(self, current_filter_args, smart_filters, encoder_name # Upload to hw frames at the end of the filter end_chain = start_chain + ["hwupload=extra_hw_frames=64", "format=qsv", f"vpp_qsv=format={target_fmt}"] end_filter_args.append(",".join(end_chain)) + elif self.settings.get_setting('qsv_safe_decode'): + # HW decode + safe mode: force SW frames to avoid hwaccel reconfigure + generic_kwargs['-hwaccel_output_format'] = target_fmt + start_chain = [f"format={target_fmt}"] + if enc_supports_hdr and target_color_config.get('apply_color_params'): + start_chain.append(target_color_config['setparams_filter']) + start_filter_args.append(",".join(start_chain)) + end_chain = start_chain + ["hwupload=extra_hw_frames=64", "format=qsv", f"vpp_qsv=format={target_fmt}"] + end_filter_args.append(",".join(end_chain)) else: - # Add hwupload filter that can handle when the frame was decoded in software or hardware + # Pure HW path - frames stay in QSV memory chain = [f"format={target_fmt}|qsv", "hwupload=extra_hw_frames=64", "format=qsv", f"vpp_qsv=format={target_fmt}"] end_filter_args.append(",".join(chain)) - + # Add the smart filters to the end end_filter_args += hw_smart_filters @@ -415,6 +429,18 @@ def get_qsv_decoding_method_form_settings(self): values["display"] = "hidden" return values + def get_qsv_safe_decode_form_settings(self): + values = { + "label": "Safe decode mode", + "description": "Forces CPU-side frame handling to prevent failures on files with " + "inconsistent color space metadata (common in WEBDL sources).\n" + "Slightly slower due to GPU->CPU->GPU round-trip per frame.", + "sub_setting": True, + } + if self.settings.get_setting('mode') not in ['standard'] or self.settings.get_setting('qsv_decoding_method') != "qsv": + values["display"] = "hidden" + return values + def get_qsv_preset_form_settings(self): values = { "label": "Encoder quality preset", diff --git a/lib/encoders/vaapi.py b/lib/encoders/vaapi.py index 646aa6c..88c4fd2 100644 --- a/lib/encoders/vaapi.py +++ b/lib/encoders/vaapi.py @@ -1,3 +1,4 @@ + #!/usr/bin/env python3 # -*- coding: utf-8 -*- @@ -89,6 +90,7 @@ def options(self): return { "vaapi_device": "none", "vaapi_decoding_method": "cpu", + "vaapi_safe_decode": False, "vaapi_encoder_ratecontrol_method": "ICQ", "vaapi_constant_quantizer_scale": "25", "vaapi_constant_quality_scale": "23", @@ -141,6 +143,9 @@ def generate_default_args(self): } advanced_kwargs = {} + if self.settings.get_setting('vaapi_safe_decode'): + generic_kwargs["-reinit_filter"] = "0" + return generic_kwargs, advanced_kwargs def generate_filtergraphs(self, current_filter_args, smart_filters, encoder_name): @@ -203,11 +208,20 @@ def generate_filtergraphs(self, current_filter_args, smart_filters, encoder_name # Upload to hw frames at the end of the filter end_chain = start_chain + ["hwupload"] end_filter_args.append(",".join(end_chain)) + elif self.settings.get_setting('vaapi_safe_decode'): + # HW decode + safe mode: force SW frames to avoid hwaccel reconfigure + generic_kwargs['-hwaccel_output_format'] = target_fmt + start_chain = [f"format={target_fmt}"] + if enc_supports_hdr and target_color_config.get('apply_color_params'): + start_chain.append(target_color_config['setparams_filter']) + start_filter_args.append(",".join(start_chain)) + end_chain = start_chain + ["hwupload"] + end_filter_args.append(",".join(end_chain)) else: - # Add hwupload filter that can handle when the frame was decoded in software or hardware + # Pure HW path - frames stay in VAAPI memory chain = [f"format={target_fmt}|vaapi", "hwupload"] end_filter_args.append(",".join(chain)) - + # Add the smart filters to the end end_filter_args += hw_smart_filters @@ -382,6 +396,18 @@ def get_vaapi_decoding_method_form_settings(self): values["display"] = "hidden" return values + def get_vaapi_safe_decode_form_settings(self): + values = { + "label": "Safe decode mode", + "description": "Forces CPU-side frame handling to prevent failures on files with " + "inconsistent color space metadata (common in WEBDL sources).\n" + "Slightly slower due to GPU->CPU->GPU round-trip per frame.", + "sub_setting": True, + } + if self.settings.get_setting('mode') not in ['standard'] or self.settings.get_setting('vaapi_decoding_method') != "vaapi": + values["display"] = "hidden" + return values + def get_vaapi_encoder_ratecontrol_method_form_settings(self): values = { "label": "Encoder ratecontrol method",