]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
transcode: fix VAAPI deinterlace mode handling for software decode/encode profiles
authorJames Hutchinson <jahutchinson99@googlemail.com>
Wed, 6 Aug 2025 10:18:15 +0000 (11:18 +0100)
committerFlole <Flole998@users.noreply.github.com>
Fri, 15 Aug 2025 21:01:47 +0000 (23:01 +0200)
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
src/transcoding/codec/codecs/libs/vaapi.c
src/transcoding/codec/internals.h
src/transcoding/codec/profile_video_class.c
src/transcoding/transcode/hwaccels/vaapi.c
src/webui/static/app/codec.js

index 8452d1379570f442826c3c860bc8b33f15b7e127..1cc77fae36d15dbefe88c82034d1f00c751a4483 100644 (file)
 #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
index 408c6fedb6dc4460262cb6507df512cff6a74834..f60b77be425ce074cc8556c523876bf2dcd9923e 100644 (file)
 #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;
index bb500c54a861e880e7dcc05a16666967f8b022b7..20f87dd6e7b0580e678a57a07ac96b523bb83914 100644 (file)
@@ -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",
index c15ddfe85d7b4dbdc1e1ab034a94169494104cde..9174b7e707f6c56dcccbd712a6ff570be613c8b6 100644 (file)
@@ -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)) {
index 4378a5854d8af652b34746caeffe380c19a09a7a..cf2cc658fa16339004083ccf558ffa076ad66bd2 100644 (file)
@@ -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