Skip to content

Commit 100d1ee

Browse files
committed
Fixes for HW accelerated encoders to keep pix_fmt
1 parent 6f0d947 commit 100d1ee

7 files changed

Lines changed: 277 additions & 60 deletions

File tree

lib/encoders/nvenc.py

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -143,21 +143,53 @@ def generate_default_args(self):
143143
generic_kwargs["-hwaccel_output_format"] = "cuda"
144144
return generic_kwargs, advanced_kwargs
145145

146-
def generate_filtergraphs(self, software_filters, hw_smart_filters):
146+
def generate_filtergraphs(self, settings, has_sw_filters, hw_smart_filters, target_fmt="nv12"):
147147
"""
148-
Generate the required filter for enabling NVENC HW acceleration
148+
Generate the required filter for enabling NVENC/CUDA HW acceleration.
149149
150150
:return:
151151
"""
152-
filters = []
153-
if software_filters:
154-
# Software filters mean the decoder was outputting nv12 surfaces. We need to upload back to cuda first
155-
filters.append('hwupload_cuda')
152+
generic_kwargs = {}
153+
advanced_kwargs = {}
154+
hw_filter_args = []
155+
sw_filter_prefix_args = []
156+
sw_filter_suffix_args = []
157+
158+
# Check for HW accelerated decode mode
159+
# All decode methods ('cuda', 'nvdec', 'cuvid') are handled by the same
160+
# filtergraph logic and output CUDA fames. The main FFmpeg command handles the specific decoder.
161+
hw_decode = (settings.get_setting('nvenc_decoding_method') or '').lower() in ('cuda', 'nvdec', 'cuvid')
162+
163+
# Check software format to use
164+
sw_fmt = "p010le" if target_fmt == "p010" else "nv12"
165+
166+
# If we have SW filters:
167+
if has_sw_filters:
168+
# If we have SW filters and HW decode (CUDA/NVDEC) is enabled, make decoder produce SW frames
169+
if hw_decode:
170+
generic_kwargs['-hwaccel_output_format'] = sw_fmt
171+
# Add filter to upload software frames to CUDA for CUDA filters
172+
# Note, format conversion (if any - eg yuv422p10le -> p010le) happens after the software filters.
173+
# If a user applies a custom software filter that does not support the pix_fmt, then will need to prefix it with 'format=p010le'
174+
sw_filter_suffix_args.append(f'format={sw_fmt}|cuda,hwupload_cuda')
175+
# If we have no software filters:
176+
else:
177+
# Add hwupload filter that can handle when the frame was decoded in software or hardware
178+
hw_filter_args.append(f'format={sw_fmt}|cuda,hwupload_cuda')
179+
180+
# Loop over any HW smart filters to be applied and add them as required.
156181
for smart_filter in hw_smart_filters:
157182
if smart_filter.get('scale'):
158183
scale_values = smart_filter.get('scale')
159-
filters.append('scale_cuda={}:-1'.format(scale_values[0]))
160-
return filters
184+
hw_filter_args.append('scale_cuda={}:-1'.format(scale_values["width"]))
185+
186+
return {
187+
"generic_kwargs": generic_kwargs,
188+
"advanced_kwargs": advanced_kwargs,
189+
"hw_filter_args": hw_filter_args,
190+
"sw_filter_prefix_args": sw_filter_prefix_args,
191+
"sw_filter_suffix_args": sw_filter_suffix_args,
192+
}
161193

162194
def encoder_details(self, encoder):
163195
hardware_devices = list_available_cuda_devices()

lib/encoders/qsv.py

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -88,32 +88,52 @@ def generate_default_args(self):
8888
}
8989
return generic_kwargs, advanced_kwargs
9090

91-
def generate_filtergraphs(self, settings, software_filters, hw_smart_filters):
91+
def generate_filtergraphs(self, settings, has_sw_filters, hw_smart_filters, target_fmt="nv12"):
9292
"""
9393
Generate the required filter for enabling QSV HW acceleration
9494
9595
:return:
9696
"""
9797
generic_kwargs = {}
9898
advanced_kwargs = {}
99-
filter_args = []
100-
# If we are using software filters, then disable qsv surfaces.
101-
# Instead, output software frames
102-
if software_filters:
103-
# If we are decoding with QSV, then output to software frames
104-
if settings.get_setting('qsv_decoding_method') in ['qsv']:
105-
generic_kwargs['-hwaccel_output_format'] = 'nv12'
99+
hw_filter_args = []
100+
sw_filter_prefix_args = []
101+
sw_filter_suffix_args = []
102+
103+
# Check if we are decoding with QSV
104+
hw_decode = settings.get_setting('qsv_decoding_method') in ['qsv']
105+
# Check software format to use
106+
sw_fmt = "p010le" if target_fmt == "p010" else "nv12"
107+
108+
# If we have SW filters:
109+
if has_sw_filters:
110+
# If we have SW filters and HW decode is enabled, make decoder produce SW frames
111+
if hw_decode:
112+
generic_kwargs['-hwaccel_output_format'] = sw_fmt
106113
# Add filter to upload software frames to QSV for QSV filters
107-
filter_args.append('hwupload=extra_hw_frames=64,format=qsv')
108-
# If we have no software filters, but we are decoding in software, then we still need to upload surfaces to QSV
109-
elif settings.get_setting('qsv_decoding_method') not in ['qsv']:
110-
filter_args.append('hwupload=extra_hw_frames=64,format=qsv')
114+
# Note, format conversion (if any - eg yuv422p10le -> p010le) happens after the software filters.
115+
# If a user applies a custom software filter that does not support the pix_fmt, then will need to prefix it with 'format=p010le'
116+
sw_filter_suffix_args.append(
117+
f'format={sw_fmt}|qsv,hwupload=extra_hw_frames=64,format=qsv,vpp_qsv=format={target_fmt}')
118+
# If we have no software filters:
119+
else:
120+
# Add hwupload filter that can handle when the frame was decoded in software or hardware
121+
hw_filter_args.append(f'format={sw_fmt}|qsv,hwupload=extra_hw_frames=64,format=qsv,vpp_qsv=format={target_fmt}')
122+
111123
# Loop over any HW smart filters to be applied and add them as required.
112124
for smart_filter in hw_smart_filters:
113125
if smart_filter.get('scale'):
114126
scale_values = smart_filter.get('scale')
115-
filter_args.append('scale_qsv=w={}:h={}'.format(scale_values[0], scale_values[1]))
116-
return generic_kwargs, advanced_kwargs, filter_args
127+
hw_filter_args.append('scale_qsv=w={}:h=-1'.format(scale_values["width"]))
128+
129+
# Return built args
130+
return {
131+
"generic_kwargs": generic_kwargs,
132+
"advanced_kwargs": advanced_kwargs,
133+
"hw_filter_args": hw_filter_args,
134+
"sw_filter_prefix_args": sw_filter_prefix_args,
135+
"sw_filter_suffix_args": sw_filter_suffix_args,
136+
}
117137

118138
def encoder_details(self, encoder):
119139
provides = self.provides()
@@ -196,7 +216,8 @@ def get_qsv_decoding_method_form_settings(self):
196216
"label": "Enable HW Accelerated Decoding",
197217
"description": "Warning: Ensure your device supports decoding the source video codec or it will fail.\n"
198218
"This enables full hardware transcode with QSV, using only GPU memory for the entire video transcode.\n"
199-
"If filters are configured in the plugin, decoder will output NV12 software surfaces which are slightly slower.",
219+
"If filters are configured in the plugin, decoder will output NV12 or P010LE software surfaces to\n"
220+
"those filters which will be slightly slower.",
200221
"sub_setting": True,
201222
"input_type": "select",
202223
"select_options": [

lib/encoders/vaapi.py

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,51 @@ def generate_default_args(self):
122122

123123
return generic_kwargs, advanced_kwargs
124124

125-
def generate_filtergraphs(self):
125+
def generate_filtergraphs(self, settings, has_sw_filters, hw_smart_filters, target_fmt="nv12"):
126126
"""
127127
Generate the required filter for enabling VAAPI HW acceleration
128128
129129
:return:
130130
"""
131-
return ["format=nv12|vaapi,hwupload"]
131+
generic_kwargs = {}
132+
advanced_kwargs = {}
133+
hw_filter_args = []
134+
sw_filter_prefix_args = []
135+
sw_filter_suffix_args = []
136+
137+
# Check if we are decoding with VAAPI
138+
hw_decode = settings.get_setting('vaapi_enabled_hw_decoding')
139+
# Check software format to use
140+
sw_fmt = "p010le" if target_fmt == "p010" else "nv12"
141+
142+
# If we have SW filters:
143+
if has_sw_filters:
144+
# If we have SW filters and HW decode is enabled, make decoder produce SW frames
145+
if hw_decode:
146+
generic_kwargs['-hwaccel_output_format'] = sw_fmt
147+
# Add filter to upload software frames to VAAPI for VAAPI filters
148+
# Note, format conversion (if any - eg yuv422p10le -> p010le) happens after the software filters.
149+
# If a user applies a custom software filter that does not support the pix_fmt, then will need to prefix it with 'format=p010le'
150+
sw_filter_suffix_args.append(f'format={sw_fmt}|vaapi,hwupload')
151+
# If we have no software filters:
152+
else:
153+
# Add hwupload filter that can handle when the frame was decoded in software or hardware
154+
hw_filter_args.append(f'format={sw_fmt}|vaapi,hwupload')
155+
156+
# Loop over any HW smart filters to be applied and add them as required.
157+
for smart_filter in hw_smart_filters:
158+
if smart_filter.get('scale'):
159+
scale_values = smart_filter.get('scale')
160+
hw_filter_args.append('scale_vaapi=w={}:h=-1'.format(scale_values["width"]))
161+
162+
# Return built args
163+
return {
164+
"generic_kwargs": generic_kwargs,
165+
"advanced_kwargs": advanced_kwargs,
166+
"hw_filter_args": hw_filter_args,
167+
"sw_filter_prefix_args": sw_filter_prefix_args,
168+
"sw_filter_suffix_args": sw_filter_suffix_args,
169+
}
132170

133171
def encoder_details(self, encoder):
134172
provides = self.provides()
@@ -215,10 +253,14 @@ def get_vaapi_device_form_settings(self):
215253
return values
216254

217255
def get_vaapi_enabled_hw_decoding_form_settings(self):
256+
# TODO: Change this to a select dropdown like the others
218257
values = {
219258
"label": "Enable HW Decoding",
259+
"description": "Warning: Ensure your device supports decoding the source video codec or it will fail.\n"
260+
"This enables full hardware transcode with VAAPI, using only GPU memory for the entire video transcode.\n"
261+
"If filters are configured in the plugin, decoder will output NV12 or P010LE software surfaces to\n"
262+
"those filters which will be slightly slower.",
220263
"sub_setting": True,
221-
"description": "Will fallback to software decoding and hardware encoding when the input codec is not supported.",
222264
}
223265
if self.settings.get_setting('mode') not in ['standard']:
224266
values["display"] = "hidden"

lib/ffmpeg

Submodule ffmpeg updated 1 file

0 commit comments

Comments
 (0)