From: James Hutchinson Date: Wed, 6 Aug 2025 10:18:15 +0000 (+0100) Subject: transcode: fix VAAPI deinterlace mode handling for software decode/encode profiles X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=33066c476068792eacbbd9748eaaaf73fe8bf829;p=thirdparty%2Ftvheadend.git transcode: fix VAAPI deinterlace mode handling for software decode/encode profiles Move 'deinterlace_vaapi_mode' from the VAAPI codec profile to the generic Main Video Codec Profile, allowing this setting to be applied when using VAAPI hardware deinterlacing with software-based transcode profiles such as libx264. This fixes a bug where 'deinterlace_vaapi_mode' was left uninitialized for non-VAAPI codec profiles, resulting in invalid filter strings like 'deinterlace_vaapi=mode=21867:rate=2:auto=0' and filter graph setup failures. The patch also: - Adds validation for the mode value (range 0–4) - Dynamically enables/disables the VAAPI mode field in the WebUI based on encoder and decoder settings - Consolidates enum and mode list generation logic under 'profile_video_class.c' This ensures that VAAPI deinterlacing can be correctly configured and used across hybrid transcode profiles, improving compatibility and user control. Fixes: #1878 --- diff --git a/src/transcoding/codec/codecs/libs/vaapi.c b/src/transcoding/codec/codecs/libs/vaapi.c index 8452d1379..1cc77fae3 100644 --- a/src/transcoding/codec/codecs/libs/vaapi.c +++ b/src/transcoding/codec/codecs/libs/vaapi.c @@ -84,12 +84,6 @@ #define VAAPI_ENC_B_REFERENCE_I_P 1 #define VAAPI_ENC_B_REFERENCE_I_P_B 2 -#define VAAPI_DEINT_MODE_DEFAULT 0 -#define VAAPI_DEINT_MODE_BOB 1 -#define VAAPI_DEINT_MODE_WEAVE 2 -#define VAAPI_DEINT_MODE_MADI 3 -#define VAAPI_DEINT_MODE_MCDI 4 - #define UI_CODEC_AVAILABLE_OFFSET 0 #define UI_MAX_B_FRAMES_OFFSET 1 #define UI_MAX_QUALITY_OFFSET 4 @@ -140,19 +134,6 @@ rc_mode_get_list( void *o, const char *lang ) return strtab2htsmsg(tab, 1, lang); } -static htsmsg_t * -deinterlace_vaapi_mode_get_list( void *o, const char *lang ) -{ - static const struct strtab tab[] = { - { N_("Default"), VAAPI_DEINT_MODE_DEFAULT }, - { N_("Bob Deinterlacing"), VAAPI_DEINT_MODE_BOB }, - { N_("Weave Deinterlacing"), VAAPI_DEINT_MODE_WEAVE }, - { N_("Motion Adaptive Deinterlacing (MADI)"), VAAPI_DEINT_MODE_MADI }, - { N_("Motion Compensated Deinterlacing (MCDI)"), VAAPI_DEINT_MODE_MCDI }, - }; - return strtab2htsmsg(tab, 1, lang); -} - // h264 static htsmsg_t * @@ -488,19 +469,6 @@ static const codec_profile_class_t codec_profile_vaapi_class = { .off = offsetof(tvh_codec_profile_vaapi_t, bit_rate_scale_factor), .def.d = 0, }, - { - .type = PT_INT, - .id = "deinterlace_vaapi_mode", - .name = N_("VAAPI Deinterlace mode"), - .desc = N_("Mode to use for VAAPI Deinterlacing. " - "'Default' selects the most advanced deinterlacer, i.e. the mode appearing last in this list. " - "Tip: MADI and MCDI usually yield the smoothest results, especially when used with field rate output."), - .group = 2, - .opts = PO_ADVANCED, - .off = offsetof(tvh_codec_profile_vaapi_t, deinterlace_vaapi_mode), - .list = deinterlace_vaapi_mode_get_list, - .def.i = VAAPI_DEINT_MODE_DEFAULT, - }, { .type = PT_INT, .id = "hw_denoise", // Don't change diff --git a/src/transcoding/codec/internals.h b/src/transcoding/codec/internals.h index 408c6fedb..f60b77be4 100644 --- a/src/transcoding/codec/internals.h +++ b/src/transcoding/codec/internals.h @@ -136,6 +136,13 @@ #define DEINT_AUTO_OFF 0 #define DEINT_AUTO_ON 1 +#define VAAPI_DEINT_MODE_DEFAULT 0 +#define VAAPI_DEINT_MODE_BOB 1 +#define VAAPI_DEINT_MODE_WEAVE 2 +#define VAAPI_DEINT_MODE_MADI 3 +#define VAAPI_DEINT_MODE_MCDI 4 + + /* codec_profile_class ====================================================== */ uint32_t @@ -326,6 +333,19 @@ typedef struct tvh_codec_profile_video { */ int deinterlace_enable_auto; + /** + * VAAPI Deinterlace mode [deinterlace_vaapi mode parameter] + * https://ffmpeg.org/doxygen/6.1/vf__deinterlace__vaapi_8c.html + * @note + * int: + * 0 - Default: Use the highest-numbered (and therefore most advanced) deinterlacing algorithm + * 1 - Use the bob deinterlacing algorithm + * 2 - Use the weave deinterlacing algorithm + * 3 - Use the motion adaptive deinterlacing algorithm + * 4 - Use the motion compensated deinterlacing algorithm + */ + int deinterlace_vaapi_mode; + int height; /** * SW or HW scaling mode (applies for decoding) @@ -436,18 +456,6 @@ typedef struct { * 2 - AMD */ int platform; -/** - * VAAPI Deinterlace mode [deinterlace_vaapi mode parameter] - * https://ffmpeg.org/doxygen/6.1/vf__deinterlace__vaapi_8c.html - * @note - * int: - * 0 - Default: Use the highest-numbered (and therefore most advanced) deinterlacing algorithm - * 1 - Use the bob deinterlacing algorithm - * 2 - Use the weave deinterlacing algorithm - * 3 - Use the motion adaptive deinterlacing algorithm - * 4 - Use the motion compensated deinterlacing algorithm - */ - int deinterlace_vaapi_mode; int loop_filter_level; int loop_filter_sharpness; diff --git a/src/transcoding/codec/profile_video_class.c b/src/transcoding/codec/profile_video_class.c index bb500c54a..20f87dd6e 100644 --- a/src/transcoding/codec/profile_video_class.c +++ b/src/transcoding/codec/profile_video_class.c @@ -51,6 +51,21 @@ hwaccel_get_list( void *o, const char *lang ) return strtab2htsmsg(tab, 1, lang); } +#if ENABLE_VAAPI +static htsmsg_t * +deinterlace_vaapi_mode_get_list( void *o, const char *lang ) +{ + static const struct strtab tab[] = { + { N_("Default"), VAAPI_DEINT_MODE_DEFAULT }, + { N_("Bob Deinterlacing"), VAAPI_DEINT_MODE_BOB }, + { N_("Weave Deinterlacing"), VAAPI_DEINT_MODE_WEAVE }, + { N_("Motion Adaptive Deinterlacing (MADI)"), VAAPI_DEINT_MODE_MADI }, + { N_("Motion Compensated Deinterlacing (MCDI)"), VAAPI_DEINT_MODE_MCDI }, + }; + return strtab2htsmsg(tab, 1, lang); +} +#endif + static htsmsg_t * deinterlace_field_rate_get_list( void *o, const char *lang ) { @@ -263,6 +278,21 @@ const codec_profile_class_t codec_profile_video_class = { .set = codec_profile_video_class_deinterlace_set, .def.i = 1, }, +#if ENABLE_VAAPI + { + .type = PT_INT, + .id = "deinterlace_vaapi_mode", + .name = N_("VAAPI Deinterlace mode"), + .desc = N_("Mode to use for VAAPI Deinterlacing. " + "'Default' selects the most advanced deinterlacer, i.e. the mode appearing last in this list. " + "Tip: MADI and MCDI usually yield the smoothest results, especially when used with field rate output."), + .group = 2, + .opts = PO_ADVANCED, + .off = offsetof(TVHVideoCodecProfile, deinterlace_vaapi_mode), + .list = deinterlace_vaapi_mode_get_list, + .def.i = VAAPI_DEINT_MODE_DEFAULT, + }, +#endif { .type = PT_INT, .id = "deinterlace_field_rate", diff --git a/src/transcoding/transcode/hwaccels/vaapi.c b/src/transcoding/transcode/hwaccels/vaapi.c index c15ddfe85..9174b7e70 100644 --- a/src/transcoding/transcode/hwaccels/vaapi.c +++ b/src/transcoding/transcode/hwaccels/vaapi.c @@ -699,8 +699,9 @@ vaapi_get_deint_filter(AVCodecContext *avctx, char *filter, size_t filter_len) // Map user selected rate (0=frame,1=field) to VAAPI rate (1=frame,2=field) int rate = (((TVHVideoCodecProfile *)ctx->profile)->deinterlace_field_rate == 1) ? 2 : 1; - int enable_auto = ((TVHVideoCodecProfile *)ctx->profile)->deinterlace_enable_auto; - int mode = ((tvh_codec_profile_vaapi_t *)ctx->profile)->deinterlace_vaapi_mode; + int enable_auto = (((TVHVideoCodecProfile *)ctx->profile)->deinterlace_enable_auto == 1) ? 1 : 0; + int mode = ((TVHVideoCodecProfile *)ctx->profile)->deinterlace_vaapi_mode; + mode = (mode < 0 || mode > 4) ? 0 : mode; // check we have valid deinterlace_vaapi mode if (str_snprintf(filter, filter_len, "deinterlace_vaapi=mode=%d:rate=%d:auto=%d", mode, rate, enable_auto)) { diff --git a/src/webui/static/app/codec.js b/src/webui/static/app/codec.js index 4378a5854..cf2cc658f 100644 --- a/src/webui/static/app/codec.js +++ b/src/webui/static/app/codec.js @@ -28,6 +28,26 @@ function genericCBRvsVBR(form) { function update_hwaccel_details(form) { form.findField('hwaccel_details').setDisabled(!form.findField('hwaccel').getValue()); + update_deinterlace_vaapi_mode(form); +} + +function update_deinterlace_vaapi_mode(form) { + if (form.findField('deinterlace_vaapi_mode')) { + const hwaccel = form.findField('hwaccel').getValue(); + const deinterlace = form.findField('deinterlace').getValue(); + const hwaccelDetails = form.findField('hwaccel_details').getValue(); // 0=AUTO, 1=VAAPI, 2=NVDEC, 3=MMAL + + const disableDeintVaapiMode = !hwaccel || !deinterlace || (hwaccelDetails !== 0 && hwaccelDetails !== 1); + form.findField('deinterlace_vaapi_mode').setDisabled(disableDeintVaapiMode); + } +} + +function update_deinterlace_details(form) { + if (form.findField('deinterlace_field_rate') && form.findField('deinterlace_enable_auto')) { + form.findField('deinterlace_field_rate').setDisabled(!form.findField('deinterlace').getValue()); + form.findField('deinterlace_enable_auto').setDisabled(!form.findField('deinterlace').getValue()); + } + update_deinterlace_vaapi_mode(form); } function enable_hwaccels_details(form) { @@ -37,6 +57,16 @@ function enable_hwaccels_details(form) { form.findField('hwaccel').on('check', function(checkbox, value) { update_hwaccel_details(form); }); + // on hwaccel_details change + form.findField('hwaccel_details').on('select', function(combo, record, index) { + update_deinterlace_vaapi_mode(form); + }); + } + if (form.findField('deinterlace')) { + // on deinterlace change + form.findField('deinterlace').on('check', function(checkbox, value) { + update_deinterlace_details(form); + }); } } @@ -49,10 +79,8 @@ function updateHWFilters(form) { form.findField('hw_denoise').setDisabled(!form.findField('hwaccel').getValue()); form.findField('hw_sharpness').setDisabled(!form.findField('hwaccel').getValue()); form.findField('hwaccel_details').setDisabled(!form.findField('hwaccel').getValue()); - form.findField('deinterlace_field_rate').setDisabled(!form.findField('deinterlace').getValue()); - form.findField('deinterlace_enable_auto').setDisabled(!form.findField('deinterlace').getValue()); - form.findField('deinterlace_vaapi_mode').setDisabled(!form.findField('hwaccel').getValue() || !form.findField('deinterlace').getValue()); } + update_deinterlace_details(form); } function checkBFrameQuality(low_power_field, desired_b_depth_field, b_reference_field, quality_field, ui_value, uilp_value) { @@ -234,17 +262,22 @@ function update_vaapi_ui(form) { form.findField('low_power').on('check', function(checkbox, value) { updateLowPower(form); }); - + // on hwaccel change if (form.findField('hwaccel')) form.findField('hwaccel').on('check', function(checkbox, value) { updateHWFilters(form); }); - + + // on hwaccel_details change + form.findField('hwaccel_details').on('select', function(combo, record, index) { + update_deinterlace_vaapi_mode(form); + }); + // on deinterlace change if (form.findField('deinterlace')) form.findField('deinterlace').on('check', function(checkbox, value) { - updateHWFilters(form); + update_deinterlace_details(form); }); // on desired_b_depth change