summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gtk/src/callbacks.c30
-rw-r--r--gtk/src/ghb-3.12.ui63
-rw-r--r--gtk/src/ghb-3.14.ui63
-rw-r--r--gtk/src/ghb.ui74
-rw-r--r--gtk/src/hb-backend.c12
-rw-r--r--gtk/src/makedeps.py1
-rw-r--r--libhb/avfilter.c11
-rw-r--r--libhb/builtin_presets.h8
-rw-r--r--libhb/comb_detect.c1560
-rw-r--r--libhb/common.c25
-rw-r--r--libhb/common.h2
-rw-r--r--libhb/decomb.c1736
-rw-r--r--libhb/decomb.h25
-rw-r--r--libhb/hb.c1
-rw-r--r--libhb/internal.h1
-rw-r--r--libhb/libhb_presets.list2
-rw-r--r--libhb/param.c55
-rw-r--r--libhb/param.h2
-rw-r--r--libhb/preset.c185
-rw-r--r--libhb/preset_builtin.json4
-rw-r--r--libhb/preset_template.json2
-rw-r--r--libhb/work.c69
-rw-r--r--macosx/English.lproj/HBPictureViewController.xib77
-rw-r--r--macosx/HBFilters+UIAdditions.h9
-rw-r--r--macosx/HBFilters+UIAdditions.m34
-rw-r--r--macosx/HBFilters.h3
-rw-r--r--macosx/HBFilters.m61
-rw-r--r--macosx/HBJob+HBJobConversion.m13
-rw-r--r--test/test.c99
-rw-r--r--win/CS/HandBrake.ApplicationServices/HandBrake.ApplicationServices.csproj1
-rw-r--r--win/CS/HandBrake.ApplicationServices/Interop/HbLib/hb_filter_ids.cs25
-rw-r--r--win/CS/HandBrake.ApplicationServices/Interop/Json/Presets/HBPreset.cs10
-rw-r--r--win/CS/HandBrake.ApplicationServices/Interop/Model/Encoding/CombDetect.cs34
-rw-r--r--win/CS/HandBrake.ApplicationServices/Interop/Model/Encoding/Decomb.cs11
-rw-r--r--win/CS/HandBrakeWPF/Converters/EnumComboConverter.cs13
-rw-r--r--win/CS/HandBrakeWPF/Services/Encode/Factories/EncodeFactory.cs13
-rw-r--r--win/CS/HandBrakeWPF/Services/Encode/Model/EncodeTask.cs12
-rw-r--r--win/CS/HandBrakeWPF/Services/Presets/Factories/JsonPresetFactory.cs36
-rw-r--r--win/CS/HandBrakeWPF/ViewModels/FiltersViewModel.cs53
-rw-r--r--win/CS/HandBrakeWPF/Views/FiltersView.xaml64
-rw-r--r--win/CS/HandBrakeWPF/Views/Styles/Styles.xaml4
41 files changed, 2843 insertions, 1660 deletions
diff --git a/gtk/src/callbacks.c b/gtk/src/callbacks.c
index 13d2d8531..b79225df3 100644
--- a/gtk/src/callbacks.c
+++ b/gtk/src/callbacks.c
@@ -2128,6 +2128,28 @@ setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
}
G_MODULE_EXPORT void
+comb_detect_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+ ghb_widget_to_setting(ud->settings, widget);
+ ghb_check_dependency(ud, widget, NULL);
+ ghb_clear_presets_selection(ud);
+ ghb_live_reset(ud);
+
+ const char * comb_detect;
+ comb_detect = ghb_dict_get_string(ud->settings, "PictureCombDetectPreset");
+ if (strcasecmp(comb_detect, "off"))
+ {
+ const char * deint;
+ deint = ghb_dict_get_string(ud->settings, "PictureDeinterlaceFilter");
+ if (!strcasecmp(deint, "off"))
+ {
+ ghb_ui_update(ud, "PictureDeinterlaceFilter",
+ ghb_string_value("decomb"));
+ }
+ }
+}
+
+G_MODULE_EXPORT void
deint_filter_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
{
ghb_widget_to_setting(ud->settings, widget);
@@ -2137,6 +2159,14 @@ deint_filter_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
ghb_update_ui_combo_box(ud, "PictureDeinterlacePreset", NULL, FALSE);
ghb_ui_update(ud, "PictureDeinterlacePreset",
ghb_dict_get(ud->settings, "PictureDeinterlacePreset"));
+
+ const char * deint;
+ deint = ghb_dict_get_string(ud->settings, "PictureDeinterlaceFilter");
+ if (!strcasecmp(deint, "off"))
+ {
+ ghb_ui_update(ud, "PictureCombDetectPreset",
+ ghb_string_value("off"));
+ }
}
G_MODULE_EXPORT void
diff --git a/gtk/src/ghb-3.12.ui b/gtk/src/ghb-3.12.ui
index ce4d34aae..023f79c73 100644
--- a/gtk/src/ghb-3.12.ui
+++ b/gtk/src/ghb-3.12.ui
@@ -2867,6 +2867,7 @@ Players will scale the image in order to achieve the specified aspect.</property
<property name="orientation">horizontal</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="selection-mode">none</property>
<property name="column-spacing">4</property>
<property name="row-spacing">32</property>
<property name="margin-top">16</property>
@@ -2934,6 +2935,68 @@ JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity</property>
</object>
</child>
<child>
+ <object class="GtkGrid" id="comb_detect_grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="column_spacing">5</property>
+ <property name="halign">start</property>
+ <property name="valign">start</property>
+ <child>
+ <object class="GtkLabel" id="comb_detect_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Interlace Detection:</property>
+ </object>
+ <packing>
+ <property name="top_attach">0</property>
+ <property name="left_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="PictureCombDetectPreset">
+ <property name="valign">GTK_ALIGN_CENTER</property>
+ <property name="width_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="tooltip_text" translatable="yes">This filter detects interlaced frames.
+
+If a deinterlace filter is enabled, only frames that this filter finds
+to be interlaced will be deinterlaced.</property>
+ <signal name="changed" handler="comb_detect_widget_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="top_attach">0</property>
+ <property name="left_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="PictureCombDetectCustom">
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">Custom interlace detection filter string format
+
+Mode:Spatial Metric:Motion Thresh:Spatial Thresh:Mask Filter Mode:
+Block Thresh: Block Width: Block Height</property>
+ <property name="width_chars">8</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <signal name="changed" handler="setting_widget_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="left_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
<object class="GtkGrid" id="table14">
<property name="visible">True</property>
<property name="can_focus">False</property>
diff --git a/gtk/src/ghb-3.14.ui b/gtk/src/ghb-3.14.ui
index b210861e4..e9c0421a9 100644
--- a/gtk/src/ghb-3.14.ui
+++ b/gtk/src/ghb-3.14.ui
@@ -2868,6 +2868,7 @@ Players will scale the image in order to achieve the specified aspect.</property
<property name="orientation">horizontal</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="selection-mode">none</property>
<property name="column-spacing">4</property>
<property name="row-spacing">32</property>
<property name="margin-top">16</property>
@@ -2935,6 +2936,68 @@ JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity</property>
</object>
</child>
<child>
+ <object class="GtkGrid" id="comb_detect_grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="column_spacing">5</property>
+ <property name="halign">start</property>
+ <property name="valign">start</property>
+ <child>
+ <object class="GtkLabel" id="comb_detect_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Interlace Detection:</property>
+ </object>
+ <packing>
+ <property name="top_attach">0</property>
+ <property name="left_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="PictureCombDetectPreset">
+ <property name="valign">GTK_ALIGN_CENTER</property>
+ <property name="width_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="tooltip_text" translatable="yes">This filter detects interlaced frames.
+
+If a deinterlace filter is enabled, only frames that this filter finds
+to be interlaced will be deinterlaced.</property>
+ <signal name="changed" handler="comb_detect_widget_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="top_attach">0</property>
+ <property name="left_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="PictureCombDetectCustom">
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">Custom interlace detection filter string format
+
+Mode:Spatial Metric:Motion Thresh:Spatial Thresh:Mask Filter Mode:
+Block Thresh: Block Width: Block Height</property>
+ <property name="width_chars">8</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <signal name="changed" handler="setting_widget_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="left_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
<object class="GtkGrid" id="table14">
<property name="visible">True</property>
<property name="can_focus">False</property>
diff --git a/gtk/src/ghb.ui b/gtk/src/ghb.ui
index 653cca354..9e327ed53 100644
--- a/gtk/src/ghb.ui
+++ b/gtk/src/ghb.ui
@@ -2939,6 +2939,74 @@ JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity</property>
</packing>
</child>
<child>
+ <object class="GtkGrid" id="comb_detect_grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="column_spacing">5</property>
+ <property name="halign">start</property>
+ <property name="valign">start</property>
+ <child>
+ <object class="GtkLabel" id="comb_detect_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Interlace Detection:</property>
+ </object>
+ <packing>
+ <property name="top_attach">0</property>
+ <property name="left_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="PictureCombDetectPreset">
+ <property name="valign">GTK_ALIGN_CENTER</property>
+ <property name="width_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="tooltip_text" translatable="yes">This filter detects interlaced frames.
+
+If a deinterlace filter is enabled, only frames that this filter finds
+to be interlaced will be deinterlaced.</property>
+ <signal name="changed" handler="comb_detect_widget_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="top_attach">0</property>
+ <property name="left_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="PictureCombDetectCustom">
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">Custom interlace detection filter string format
+
+Mode:Spatial Metric:Motion Thresh:Spatial Thresh:Mask Filter Mode:
+Block Thresh: Block Width: Block Height</property>
+ <property name="width_chars">8</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <signal name="changed" handler="setting_widget_changed_cb" swapped="no"/>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="left_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">0</property>
+ <property name="left_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkGrid" id="table14">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -3038,7 +3106,7 @@ SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma</property>
</object>
<packing>
<property name="top_attach">0</property>
- <property name="left_attach">1</property>
+ <property name="left_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
@@ -3086,8 +3154,8 @@ If your source exhibits 'blockiness', this filter may help clean it up.</propert
</child>
</object>
<packing>
- <property name="top_attach">0</property>
- <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="left_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c
index 2a8d79bb9..4ea4d4d4b 100644
--- a/gtk/src/hb-backend.c
+++ b/gtk/src/hb-backend.c
@@ -362,6 +362,12 @@ static filter_opts_t hqdn3d_preset_opts =
};
#endif
+static filter_opts_t comb_detect_opts =
+{
+ .filter_id = HB_FILTER_COMB_DETECT,
+ .preset = TRUE
+};
+
static filter_opts_t detel_opts =
{
.filter_id = HB_FILTER_DETELECINE,
@@ -493,6 +499,12 @@ combo_name_map_t combo_name_map[] =
generic_opt_get
},
{
+ "PictureCombDetectPreset",
+ &comb_detect_opts,
+ filter_opts_set,
+ filter_opt_get
+ },
+ {
"PictureDeinterlaceFilter",
&deint_opts,
small_opts_set,
diff --git a/gtk/src/makedeps.py b/gtk/src/makedeps.py
index a0277e840..fa6463e1d 100644
--- a/gtk/src/makedeps.py
+++ b/gtk/src/makedeps.py
@@ -28,6 +28,7 @@ dep_map = (
DepEntry("VideoFramerate", "VideoFrameratePFR", "auto", True, True),
DepEntry("VideoFramerate", "VideoFramerateVFR", "auto", False, True),
DepEntry("VideoTwoPass", "VideoTurboTwoPass", "1", False, False),
+ DepEntry("PictureCombDetectPreset", "PictureCombDetectCustom", "custom", False, True),
DepEntry("PictureDeinterlaceFilter", "PictureDeinterlacePreset", "off", True, True),
DepEntry("PictureDeinterlaceFilter", "PictureDeinterlacePresetLabel", "off", True, True),
DepEntry("PictureDeinterlaceFilter", "PictureDeinterlaceCustom", "off", True, True),
diff --git a/libhb/avfilter.c b/libhb/avfilter.c
index 2e57f72d6..9aa8a316b 100644
--- a/libhb/avfilter.c
+++ b/libhb/avfilter.c
@@ -13,6 +13,7 @@
#include "libavfilter/avfilter.h"
#include "libavfilter/buffersrc.h"
#include "libavfilter/buffersink.h"
+#include "decomb.h"
/*****
@@ -508,11 +509,6 @@ static int avfilter_work( hb_filter_object_t * filter,
return HB_FILTER_OK;
}
-#define MODE_YADIF_ENABLE 1
-#define MODE_YADIF_SPATIAL 2
-#define MODE_YADIF_BOB 4
-#define MODE_YADIF_AUTO 8
-
/* Deinterlace Settings
* mode:parity
*
@@ -523,12 +519,13 @@ static int avfilter_work( hb_filter_object_t * filter,
* 1 = Enabled
* 2 = Spatial
* 4 = Bob
- * 8 = Auto
+ * 8 = Selective
*
* Parity:
* 0 = Top Field First
* 1 = Bottom Field First
* -1 = Automatic detection of field parity
+ *
*/
static hb_dict_t *
convert_deint_settings(const hb_dict_t * settings)
@@ -542,7 +539,7 @@ convert_deint_settings(const hb_dict_t * settings)
{
return hb_value_null();
}
- int automatic = !!(mode & MODE_YADIF_AUTO);
+ int automatic = !!(mode & MODE_DECOMB_SELECTIVE);
int bob = !!(mode & MODE_YADIF_BOB);
int no_spatial = !(mode & MODE_YADIF_SPATIAL);
mode = bob | (no_spatial << 1);
diff --git a/libhb/builtin_presets.h b/libhb/builtin_presets.h
index bbb6e1335..6fa988f4e 100644
--- a/libhb/builtin_presets.h
+++ b/libhb/builtin_presets.h
@@ -403,10 +403,11 @@ const char hb_builtin_presets_json[] =
" \"Mp4iPodCompatible\": false, \n"
" \"PictureAutoCrop\": true, \n"
" \"PictureBottomCrop\": 0, \n"
+" \"PictureCombDetectPreset\": \"fast\", \n"
" \"PictureDeblock\": 0, \n"
" \"PictureDeinterlaceCustom\": \"\", \n"
" \"PictureDeinterlaceFilter\": \"decomb\", \n"
-" \"PictureDeinterlacePreset\": \"fast\", \n"
+" \"PictureDeinterlacePreset\": \"default\", \n"
" \"PictureDenoiseCustom\": \"\", \n"
" \"PictureDenoiseFilter\": \"off\", \n"
" \"PictureDetelecine\": \"off\", \n"
@@ -708,6 +709,7 @@ const char hb_builtin_presets_json[] =
" \"Mp4iPodCompatible\": false, \n"
" \"PictureAutoCrop\": true, \n"
" \"PictureBottomCrop\": 0, \n"
+" \"PictureCombDetectPreset\": \"default\", \n"
" \"PictureDeblock\": 0, \n"
" \"PictureDeinterlaceCustom\": \"\", \n"
" \"PictureDeinterlaceFilter\": \"decomb\", \n"
@@ -795,6 +797,8 @@ const char hb_builtin_presets_json[] =
" \"Mp4iPodCompatible\": false, \n"
" \"PictureAutoCrop\": true, \n"
" \"PictureBottomCrop\": 0, \n"
+" \"PictureCombDetectCustom\": \"\", \n"
+" \"PictureCombDetectPreset\": \"off\", \n"
" \"PictureDARWidth\": 0, \n"
" \"PictureDeblock\": 0, \n"
" \"PictureDeblockCustom\": \"qp=0:mode=2\", \n"
@@ -857,7 +861,7 @@ const char hb_builtin_presets_json[] =
" \"x264Option\": \"\", \n"
" \"x264UseAdvancedOptions\": false\n"
" }, \n"
-" \"VersionMajor\": 12, \n"
+" \"VersionMajor\": 13, \n"
" \"VersionMicro\": 0, \n"
" \"VersionMinor\": 0\n"
" }\n"
diff --git a/libhb/comb_detect.c b/libhb/comb_detect.c
new file mode 100644
index 000000000..b9f96ee31
--- /dev/null
+++ b/libhb/comb_detect.c
@@ -0,0 +1,1560 @@
+/* comb_detect.c
+
+ Copyright (c) 2003-2016 HandBrake Team
+ This file is part of the HandBrake source code
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License v2.
+ For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
+
+*/
+
+/*****
+Parameters:
+ Mode : Spatial metric : Motion thresh : Spatial thresh : Mask Filter Mode :
+ Block thresh : Block width : Block height
+
+Defaults:
+ 3:2:3:3:2:40:16:16
+
+Original "Faster" settings:
+ 0:2:6:9:1:80:16:16
+*****/
+
+#define MODE_GAMMA 1 // Scale gamma when decombing
+#define MODE_FILTER 2 // Filter combing mask
+#define MODE_MASK 4 // Output combing masks instead of pictures
+#define MODE_COMPOSITE 8 // Overlay combing mask onto picture
+
+#define FILTER_CLASSIC 1
+#define FILTER_ERODE_DILATE 2
+
+#include "hb.h"
+#include "taskset.h"
+
+typedef struct decomb_thread_arg_s {
+ hb_filter_private_t *pv;
+ int segment;
+ int segment_start[3];
+ int segment_height[3];
+} decomb_thread_arg_t;
+
+struct hb_filter_private_s
+{
+ // comb detect parameters
+ int mode;
+ int filter_mode;
+ int spatial_metric;
+ int motion_threshold;
+ int spatial_threshold;
+ int block_threshold;
+ int block_width;
+ int block_height;
+ int * block_score;
+ int comb_check_complete;
+ int comb_check_nthreads;
+
+ float gamma_lut[256];
+
+ int comb_detect_ready;
+
+ hb_buffer_t * ref[3];
+ int ref_used[3];
+
+ /* Make buffers to store a comb masks. */
+ hb_buffer_t * mask;
+ hb_buffer_t * mask_filtered;
+ hb_buffer_t * mask_temp;
+ int mask_box_x;
+ int mask_box_y;
+ uint8_t mask_box_color;
+
+ int cpu_count;
+ int segment_height[3];
+
+ taskset_t decomb_filter_taskset; // Threads for comb detection
+ taskset_t decomb_check_taskset; // Threads for comb check
+ taskset_t mask_filter_taskset; // Threads for decomb mask filter
+ taskset_t mask_erode_taskset; // Threads for decomb mask erode
+ taskset_t mask_dilate_taskset; // Threads for decomb mask dilate
+
+ hb_buffer_list_t out_list;
+
+ // Filter statistics
+ int comb_heavy;
+ int comb_light;
+ int comb_none;
+ int frames;
+};
+
+static int comb_detect_init( hb_filter_object_t * filter,
+ hb_filter_init_t * init );
+
+static int comb_detect_work( hb_filter_object_t * filter,
+ hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out );
+
+static void comb_detect_close( hb_filter_object_t * filter );
+
+static const char comb_detect_template[] =
+ "mode=^"HB_INT_REG"$:spatial-metric=^([012])$:"
+ "motion-thresh=^"HB_INT_REG"$:spatial-thresh=^"HB_INT_REG"$:"
+ "filter-mode=^([012])$:block-thresh=^"HB_INT_REG"$:"
+ "block-width=^"HB_INT_REG"$:block-height=^"HB_INT_REG"$:"
+ "disable=^"HB_BOOL_REG"$";
+
+hb_filter_object_t hb_filter_comb_detect =
+{
+ .id = HB_FILTER_COMB_DETECT,
+ .enforce_order = 1,
+ .name = "Comb Detect",
+ .settings = NULL,
+ .init = comb_detect_init,
+ .work = comb_detect_work,
+ .close = comb_detect_close,
+ .settings_template = comb_detect_template,
+};
+
+static void draw_mask_box( hb_filter_private_t * pv )
+{
+ int x = pv->mask_box_x;
+ int y = pv->mask_box_y;
+ int box_width = pv->block_width;
+ int box_height = pv->block_height;
+ int stride;
+ uint8_t * mskp;
+
+ if (pv->mode & MODE_FILTER)
+ {
+ mskp = pv->mask_filtered->plane[0].data;
+ stride = pv->mask_filtered->plane[0].stride;
+ }
+ else
+ {
+ mskp = pv->mask->plane[0].data;
+ stride = pv->mask->plane[0].stride;
+ }
+
+
+ int block_x, block_y;
+ for (block_x = 0; block_x < box_width; block_x++)
+ {
+ mskp[ y * stride + x + block_x] = 128;
+ mskp[(y + box_height) * stride + x + block_x] = 128;
+ }
+
+ for (block_y = 0; block_y < box_height; block_y++)
+ {
+ mskp[stride * (y + block_y) + x ] = 128;
+ mskp[stride * (y + block_y) + x + box_width] = 128;
+ }
+}
+
+static void apply_mask_line( uint8_t * srcp,
+ uint8_t * mskp,
+ int width )
+{
+ int x;
+
+ for (x = 0; x < width; x++)
+ {
+ if (mskp[x] == 1)
+ {
+ srcp[x] = 255;
+ }
+ if (mskp[x] == 128)
+ {
+ srcp[x] = 128;
+ }
+ }
+}
+
+static void apply_mask(hb_filter_private_t * pv, hb_buffer_t * b)
+{
+ /* draw_boxes */
+ draw_mask_box( pv );
+
+ int pp, yy;
+ hb_buffer_t * m;
+
+ if (pv->mode & MODE_FILTER)
+ {
+ m = pv->mask_filtered;
+ }
+ else
+ {
+ m = pv->mask;
+ }
+ for (pp = 0; pp < 3; pp++)
+ {
+ uint8_t * dstp = b->plane[pp].data;
+ uint8_t * mskp = m->plane[pp].data;
+
+ for (yy = 0; yy < m->plane[pp].height; yy++)
+ {
+ if (!(pv->mode & MODE_COMPOSITE) && pp == 0)
+ {
+ memcpy(dstp, mskp, m->plane[pp].width);
+ }
+ else if (!(pv->mode & MODE_COMPOSITE))
+ {
+ memset(dstp, 128, m->plane[pp].width);
+ }
+ if (pp == 0)
+ {
+ apply_mask_line(dstp, mskp, m->plane[pp].width);
+ }
+
+ dstp += b->plane[pp].stride;
+ mskp += m->plane[pp].stride;
+ }
+ }
+}
+
+static void store_ref(hb_filter_private_t * pv, hb_buffer_t * b)
+{
+ // Free unused buffer
+ if (!pv->ref_used[0])
+ {
+ hb_buffer_close(&pv->ref[0]);
+ }
+ memmove(&pv->ref[0], &pv->ref[1], sizeof(pv->ref[0]) * 2 );
+ memmove(&pv->ref_used[0], &pv->ref_used[1], sizeof(pv->ref_used[0]) * 2 );
+ pv->ref[2] = b;
+ pv->ref_used[2] = 0;
+}
+
+static void reset_combing_results( hb_filter_private_t * pv )
+{
+ pv->comb_check_complete = 0;
+ int ii;
+ for (ii = 0; ii < pv->comb_check_nthreads; ii++)
+ {
+ pv->block_score[ii] = 0;
+ }
+}
+
+static int check_combing_results( hb_filter_private_t * pv )
+{
+ int combed = HB_COMB_NONE;
+
+ int ii;
+ for (ii = 0; ii < pv->comb_check_nthreads; ii++)
+ {
+ if (pv->block_score[ii] >= ( pv->block_threshold / 2 ))
+ {
+ if (pv->block_score[ii] <= pv->block_threshold)
+ {
+ // Indicate light combing for block_score that is between
+ // ( pv->block_threshold / 2 ) and pv->block_threshold
+ combed = HB_COMB_LIGHT;
+ pv->mask_box_color = 2;
+ }
+ else if (pv->block_score[ii] > pv->block_threshold)
+ {
+ pv->mask_box_color = 1;
+ return HB_COMB_HEAVY;
+ }
+ }
+ }
+
+ return combed;
+}
+
+static void check_filtered_combing_mask( hb_filter_private_t * pv, int segment,
+ int start, int stop )
+{
+ /* Go through the mask in X*Y blocks. If any of these windows
+ have threshold or more combed pixels, consider the whole
+ frame to be combed and send it on to be deinterlaced. */
+
+ /* Block mask threshold -- The number of pixels
+ in a block_width * block_height window of
+ he mask that need to show combing for the
+ whole frame to be seen as such. */
+ int threshold = pv->block_threshold;
+ int block_width = pv->block_width;
+ int block_height = pv->block_height;
+ int block_x, block_y;
+ int block_score = 0;
+ uint8_t * mask_p;
+ int x, y, pp;
+
+ for (pp = 0; pp < 1; pp++)
+ {
+ int stride = pv->mask_filtered->plane[pp].stride;
+ int width = pv->mask_filtered->plane[pp].width;
+
+ pv->mask_box_x = -1;
+ pv->mask_box_y = -1;
+ pv->mask_box_color = 0;
+
+ for (y = start; y < ( stop - block_height + 1 ); y = y + block_height)
+ {
+ for (x = 0; x < ( width - block_width ); x = x + block_width)
+ {
+ block_score = 0;
+
+ for (block_y = 0; block_y < block_height; block_y++)
+ {
+ int my = y + block_y;
+ mask_p = &pv->mask_filtered->plane[pp].data[my*stride + x];
+
+ for (block_x = 0; block_x < block_width; block_x++)
+ {
+ block_score += mask_p[0];
+ mask_p++;
+ }
+ }
+
+ if (pv->comb_check_complete)
+ {
+ // Some other thread found coming before this one
+ return;
+ }
+
+ if (block_score >= ( threshold / 2 ))
+ {
+ pv->mask_box_x = x;
+ pv->mask_box_y = y;
+
+ pv->block_score[segment] = block_score;
+ if (block_score > threshold)
+ {
+ pv->comb_check_complete = 1;
+ return;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void check_combing_mask( hb_filter_private_t * pv, int segment,
+ int start, int stop )
+{
+ /* Go through the mask in X*Y blocks. If any of these windows
+ have threshold or more combed pixels, consider the whole
+ frame to be combed and send it on to be deinterlaced. */
+
+ /* Block mask threshold -- The number of pixels
+ in a block_width * block_height window of
+ he mask that need to show combing for the
+ whole frame to be seen as such. */
+ int threshold = pv->block_threshold;
+ int block_width = pv->block_width;
+ int block_height = pv->block_height;
+ int block_x, block_y;
+ int block_score = 0;
+ uint8_t * mask_p;
+ int x, y, pp;
+
+ for (pp = 0; pp < 1; pp++)
+ {
+ int stride = pv->mask->plane[pp].stride;
+ int width = pv->mask->plane[pp].width;
+
+ for (y = start; y < (stop - block_height + 1); y = y + block_height)
+ {
+ for (x = 0; x < (width - block_width); x = x + block_width)
+ {
+ block_score = 0;
+
+ for (block_y = 0; block_y < block_height; block_y++)
+ {
+ int mask_y = y + block_y;
+ mask_p = &pv->mask->plane[pp].data[mask_y * stride + x];
+
+ for (block_x = 0; block_x < block_width; block_x++)
+ {
+ /* We only want to mark a pixel in a block as combed
+ if the adjacent pixels are as well. Got to
+ handle the sides separately. */
+ if ((x + block_x) == 0)
+ {
+ block_score += mask_p[0] & mask_p[1];
+ }
+ else if ((x + block_x) == (width -1))
+ {
+ block_score += mask_p[-1] & mask_p[0];
+ }
+ else
+ {
+ block_score += mask_p[-1] & mask_p[0] & mask_p[1];
+ }
+
+ mask_p++;
+ }
+ }
+
+ if (pv->comb_check_complete)
+ {
+ // Some other thread found coming before this one
+ return;
+ }
+
+ if (block_score >= ( threshold / 2 ))
+ {
+ pv->mask_box_x = x;
+ pv->mask_box_y = y;
+
+ pv->block_score[segment] = block_score;
+ if (block_score > threshold)
+ {
+ pv->comb_check_complete = 1;
+ return;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void build_gamma_lut( hb_filter_private_t * pv )
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ {
+ pv->gamma_lut[i] = pow( ( (float)i / (float)255 ), 2.2f );
+ }
+}
+
+static void detect_gamma_combed_segment( hb_filter_private_t * pv,
+ int segment_start, int segment_stop )
+{
+ /* A mish-mash of various comb detection tricks
+ picked up from neuron2's Decomb plugin for
+ AviSynth and tritical's IsCombedT and
+ IsCombedTIVTC plugins. */
+
+ /* Comb scoring algorithm */
+ /* Motion threshold */
+ float mthresh = (float)pv->motion_threshold / (float)255;
+ /* Spatial threshold */
+ float athresh = (float)pv->spatial_threshold / (float)255;
+ float athresh6 = 6 *athresh;
+
+ /* One pas for Y, one pass for U, one pass for V */
+ int pp;
+ for (pp = 0; pp < 1; pp++)
+ {
+ int x, y;
+ int stride = pv->ref[0]->plane[pp].stride;
+ int width = pv->ref[0]->plane[pp].width;
+ int height = pv->ref[0]->plane[pp].height;
+
+ /* Comb detection has to start at y = 2 and end at
+ y = height - 2, because it needs to examine
+ 2 pixels above and 2 below the current pixel. */
+ if (segment_start < 2)
+ segment_start = 2;
+ if (segment_stop > height - 2)
+ segment_stop = height - 2;
+
+ for (y = segment_start; y < segment_stop; y++)
+ {
+ /* These are just to make the buffer locations easier to read. */
+ int up_2 = -2 * stride ;
+ int up_1 = -1 * stride;
+ int down_1 = stride;
+ int down_2 = 2 * stride;
+
+ /* We need to examine a column of 5 pixels
+ in the prev, cur, and next frames. */
+ uint8_t * prev = &pv->ref[0]->plane[pp].data[y * stride];
+ uint8_t * cur = &pv->ref[1]->plane[pp].data[y * stride];
+ uint8_t * next = &pv->ref[2]->plane[pp].data[y * stride];
+ uint8_t * mask = &pv->mask->plane[pp].data[y * stride];
+
+ memset(mask, 0, stride);
+
+ for (x = 0; x < width; x++)
+ {
+ float up_diff, down_diff;
+ up_diff = pv->gamma_lut[cur[0]] - pv->gamma_lut[cur[up_1]];
+ down_diff = pv->gamma_lut[cur[0]] - pv->gamma_lut[cur[down_1]];
+
+ if (( up_diff > athresh && down_diff > athresh ) ||
+ ( up_diff < -athresh && down_diff < -athresh ))
+ {
+ /* The pixel above and below are different,
+ and they change in the same "direction" too.*/
+ int motion = 0;
+ if (mthresh > 0)
+ {
+ /* Make sure there's sufficient motion between frame t-1 to frame t+1. */
+ if (fabs(pv->gamma_lut[prev[0]] - pv->gamma_lut[cur[0]] ) > mthresh &&
+ fabs(pv->gamma_lut[cur[up_1]] - pv->gamma_lut[next[up_1]] ) > mthresh &&
+ fabs(pv->gamma_lut[cur[down_1]] - pv->gamma_lut[next[down_1]]) > mthresh)
+ motion++;
+ if (fabs(pv->gamma_lut[next[0]] - pv->gamma_lut[cur[0]] ) > mthresh &&
+ fabs(pv->gamma_lut[prev[up_1]] - pv->gamma_lut[cur[up_1]] ) > mthresh &&
+ fabs(pv->gamma_lut[prev[down_1]] - pv->gamma_lut[cur[down_1]]) > mthresh)
+ motion++;
+
+ }
+ else
+ {
+ /* User doesn't want to check for motion,
+ so move on to the spatial check. */
+ motion = 1;
+ }
+
+ if (motion || pv->frames == 0)
+ {
+ float combing;
+ /* Tritical's noise-resistant combing scorer.
+ The check is done on a bob+blur convolution. */
+ combing = fabs(pv->gamma_lut[cur[up_2]] +
+ (4 * pv->gamma_lut[cur[0]]) +
+ pv->gamma_lut[cur[down_2]] -
+ (3 * (pv->gamma_lut[cur[up_1]] +
+ pv->gamma_lut[cur[down_1]])));
+ /* If the frame is sufficiently combed,
+ then mark it down on the mask as 1. */
+ if (combing > athresh6)
+ {
+ mask[0] = 1;
+ }
+ }
+ }
+
+ cur++;
+ prev++;
+ next++;
+ mask++;
+ }
+ }
+ }
+}
+
+static void detect_combed_segment( hb_filter_private_t * pv,
+ int segment_start, int segment_stop )
+{
+ /* A mish-mash of various comb detection tricks
+ picked up from neuron2's Decomb plugin for
+ AviSynth and tritical's IsCombedT and
+ IsCombedTIVTC plugins. */
+
+
+ /* Comb scoring algorithm */
+ int spatial_metric = pv->spatial_metric;
+ /* Motion threshold */
+ int mthresh = pv->motion_threshold;
+ /* Spatial threshold */
+ int athresh = pv->spatial_threshold;
+ int athresh_squared = athresh * athresh;
+ int athresh6 = 6 * athresh;
+
+ /* One pas for Y, one pass for U, one pass for V */
+ int pp;
+ for (pp = 0; pp < 1; pp++)
+ {
+ int x, y;
+ int stride = pv->ref[0]->plane[pp].stride;
+ int width = pv->ref[0]->plane[pp].width;
+ int height = pv->ref[0]->plane[pp].height;
+
+ /* Comb detection has to start at y = 2 and end at
+ y = height - 2, because it needs to examine
+ 2 pixels above and 2 below the current pixel. */
+ if (segment_start < 2)
+ segment_start = 2;
+ if (segment_stop > height - 2)
+ segment_stop = height - 2;
+
+ for (y = segment_start; y < segment_stop; y++)
+ {
+ /* These are just to make the buffer locations easier to read. */
+ int up_2 = -2 * stride ;
+ int up_1 = -1 * stride;
+ int down_1 = stride;
+ int down_2 = 2 * stride;
+
+ /* We need to examine a column of 5 pixels
+ in the prev, cur, and next frames. */
+ uint8_t * prev = &pv->ref[0]->plane[pp].data[y * stride];
+ uint8_t * cur = &pv->ref[1]->plane[pp].data[y * stride];
+ uint8_t * next = &pv->ref[2]->plane[pp].data[y * stride];
+ uint8_t * mask = &pv->mask->plane[pp].data[y * stride];
+
+ memset(mask, 0, stride);
+
+ for (x = 0; x < width; x++)
+ {
+ int up_diff = cur[0] - cur[up_1];
+ int down_diff = cur[0] - cur[down_1];
+
+ if (( up_diff > athresh && down_diff > athresh ) ||
+ ( up_diff < -athresh && down_diff < -athresh ))
+ {
+ /* The pixel above and below are different,
+ and they change in the same "direction" too.*/
+ int motion = 0;
+ if (mthresh > 0)
+ {
+ /* Make sure there's sufficient motion between frame t-1 to frame t+1. */
+ if (abs(prev[0] - cur[0] ) > mthresh &&
+ abs(cur[up_1] - next[up_1] ) > mthresh &&
+ abs(cur[down_1] - next[down_1]) > mthresh)
+ motion++;
+ if (abs(next[0] - cur[0] ) > mthresh &&
+ abs(prev[up_1] - cur[up_1] ) > mthresh &&
+ abs(prev[down_1] - cur[down_1]) > mthresh)
+ motion++;
+ }
+ else
+ {
+ /* User doesn't want to check for motion,
+ so move on to the spatial check. */
+ motion = 1;
+ }
+
+ // If motion, or we can't measure motion yet...
+ if (motion || pv->frames == 0)
+ {
+ /* That means it's time for the spatial check.
+ We've got several options here. */
+ if (spatial_metric == 0)
+ {
+ /* Simple 32detect style comb detection */
+ if ((abs(cur[0] - cur[down_2]) < 10) &&
+ (abs(cur[0] - cur[down_1]) > 15))
+ {
+ mask[0] = 1;
+ }
+ }
+ else if (spatial_metric == 1)
+ {
+ /* This, for comparison, is what IsCombed uses.
+ It's better, but still noise senstive. */
+ int combing = ( cur[up_1] - cur[0] ) *
+ ( cur[down_1] - cur[0] );
+
+ if (combing > athresh_squared)
+ {
+ mask[0] = 1;
+ }
+ }
+ else if (spatial_metric == 2)
+ {
+ /* Tritical's noise-resistant combing scorer.
+ The check is done on a bob+blur convolution. */
+ int combing = abs( cur[up_2]
+ + ( 4 * cur[0] )
+ + cur[down_2]
+ - ( 3 * ( cur[up_1]
+ + cur[down_1] ) ) );
+
+ /* If the frame is sufficiently combed,
+ then mark it down on the mask as 1. */
+ if (combing > athresh6)
+ {
+ mask[0] = 1;
+ }
+ }
+ }
+ }
+
+ cur++;
+ prev++;
+ next++;
+ mask++;
+ }
+ }
+ }
+}
+
+static void mask_dilate_thread( void *thread_args_v )
+{
+ hb_filter_private_t * pv;
+ int segment, segment_start, segment_stop;
+ decomb_thread_arg_t *thread_args = thread_args_v;
+
+ pv = thread_args->pv;
+ segment = thread_args->segment;
+
+ hb_log("mask dilate thread started for segment %d", segment);
+
+ while (1)
+ {
+ /*
+ * Wait here until there is work to do.
+ */
+ taskset_thread_wait4start( &pv->mask_dilate_taskset, segment );
+
+ if (taskset_thread_stop(&pv->mask_dilate_taskset, segment))
+ {
+ /*
+ * No more work to do, exit this thread.
+ */
+ break;
+ }
+
+ int xx, yy, pp;
+
+ int count;
+ int dilation_threshold = 4;
+
+ for (pp = 0; pp < 1; pp++)
+ {
+ int width = pv->mask_filtered->plane[pp].width;
+ int height = pv->mask_filtered->plane[pp].height;
+ int stride = pv->mask_filtered->plane[pp].stride;
+
+ int start, stop, p, c, n;
+ segment_start = thread_args->segment_start[pp];
+ segment_stop = segment_start + thread_args->segment_height[pp];
+
+ if (segment_start == 0)
+ {
+ start = 1;
+ p = 0;
+ c = 1;
+ n = 2;
+ }
+ else
+ {
+ start = segment_start;
+ p = segment_start - 1;
+ c = segment_start;
+ n = segment_start + 1;
+ }
+
+ if (segment_stop == height)
+ {
+ stop = height -1;
+ }
+ else
+ {
+ stop = segment_stop;
+ }
+
+ uint8_t *curp = &pv->mask_filtered->plane[pp].data[p * stride + 1];
+ uint8_t *cur = &pv->mask_filtered->plane[pp].data[c * stride + 1];
+ uint8_t *curn = &pv->mask_filtered->plane[pp].data[n * stride + 1];
+ uint8_t *dst = &pv->mask_temp->plane[pp].data[c * stride + 1];
+
+ for (yy = start; yy < stop; yy++)
+ {
+ for (xx = 1; xx < width - 1; xx++)
+ {
+ if (cur[xx])
+ {
+ dst[xx] = 1;
+ continue;
+ }
+
+ count = curp[xx-1] + curp[xx] + curp[xx+1] +
+ cur [xx-1] + cur [xx+1] +
+ curn[xx-1] + curn[xx] + curn[xx+1];
+
+ dst[xx] = count >= dilation_threshold;
+ }
+ curp += stride;
+ cur += stride;
+ curn += stride;
+ dst += stride;
+ }
+ }
+
+ taskset_thread_complete( &pv->mask_dilate_taskset, segment );
+ }
+
+ /*
+ * Finished this segment, let everyone know.
+ */
+ taskset_thread_complete( &pv->mask_dilate_taskset, segment );
+}
+
+static void mask_erode_thread( void *thread_args_v )
+{
+ hb_filter_private_t * pv;
+ int segment, segment_start, segment_stop;
+ decomb_thread_arg_t *thread_args = thread_args_v;
+
+ pv = thread_args->pv;
+ segment = thread_args->segment;
+
+ hb_log("mask erode thread started for segment %d", segment);
+
+ while (1)
+ {
+ /*
+ * Wait here until there is work to do.
+ */
+ taskset_thread_wait4start( &pv->mask_erode_taskset, segment );
+
+ if (taskset_thread_stop( &pv->mask_erode_taskset, segment ))
+ {
+ /*
+ * No more work to do, exit this thread.
+ */
+ break;
+ }
+
+ int xx, yy, pp;
+
+ int count;
+ int erosion_threshold = 2;
+
+ for (pp = 0; pp < 1; pp++)
+ {
+ int width = pv->mask_filtered->plane[pp].width;
+ int height = pv->mask_filtered->plane[pp].height;
+ int stride = pv->mask_filtered->plane[pp].stride;
+
+ int start, stop, p, c, n;
+ segment_start = thread_args->segment_start[pp];
+ segment_stop = segment_start + thread_args->segment_height[pp];
+
+ if (segment_start == 0)
+ {
+ start = 1;
+ p = 0;
+ c = 1;
+ n = 2;
+ }
+ else
+ {
+ start = segment_start;
+ p = segment_start - 1;
+ c = segment_start;
+ n = segment_start + 1;
+ }
+
+ if (segment_stop == height)
+ {
+ stop = height -1;
+ }
+ else
+ {
+ stop = segment_stop;
+ }
+
+ uint8_t *curp = &pv->mask_temp->plane[pp].data[p * stride + 1];
+ uint8_t *cur = &pv->mask_temp->plane[pp].data[c * stride + 1];
+ uint8_t *curn = &pv->mask_temp->plane[pp].data[n * stride + 1];
+ uint8_t *dst = &pv->mask_filtered->plane[pp].data[c * stride + 1];
+
+ for (yy = start; yy < stop; yy++)
+ {
+ for (xx = 1; xx < width - 1; xx++)
+ {
+ if (cur[xx] == 0)
+ {
+ dst[xx] = 0;
+ continue;
+ }
+
+ count = curp[xx-1] + curp[xx] + curp[xx+1] +
+ cur [xx-1] + cur [xx+1] +
+ curn[xx-1] + curn[xx] + curn[xx+1];
+
+ dst[xx] = count >= erosion_threshold;
+ }
+ curp += stride;
+ cur += stride;
+ curn += stride;
+ dst += stride;
+ }
+ }
+
+ taskset_thread_complete( &pv->mask_erode_taskset, segment );
+ }
+
+ /*
+ * Finished this segment, let everyone know.
+ */
+ taskset_thread_complete( &pv->mask_erode_taskset, segment );
+}
+
+static void mask_filter_thread( void *thread_args_v )
+{
+ hb_filter_private_t * pv;
+ int segment, segment_start, segment_stop;
+ decomb_thread_arg_t *thread_args = thread_args_v;
+
+ pv = thread_args->pv;
+ segment = thread_args->segment;
+
+ hb_log("mask filter thread started for segment %d", segment);
+
+ while (1)
+ {
+ /*
+ * Wait here until there is work to do.
+ */
+ taskset_thread_wait4start( &pv->mask_filter_taskset, segment );
+
+ if (taskset_thread_stop( &pv->mask_filter_taskset, segment ))
+ {
+ /*
+ * No more work to do, exit this thread.
+ */
+ break;
+ }
+
+ int xx, yy, pp;
+
+ for (pp = 0; pp < 1; pp++)
+ {
+ int width = pv->mask->plane[pp].width;
+ int height = pv->mask->plane[pp].height;
+ int stride = pv->mask->plane[pp].stride;
+
+ int start, stop, p, c, n;
+ segment_start = thread_args->segment_start[pp];
+ segment_stop = segment_start + thread_args->segment_height[pp];
+
+ if (segment_start == 0)
+ {
+ start = 1;
+ p = 0;
+ c = 1;
+ n = 2;
+ }
+ else
+ {
+ start = segment_start;
+ p = segment_start - 1;
+ c = segment_start;
+ n = segment_start + 1;
+ }
+
+ if (segment_stop == height)
+ {
+ stop = height - 1;
+ }
+ else
+ {
+ stop = segment_stop;
+ }
+
+ uint8_t *curp = &pv->mask->plane[pp].data[p * stride + 1];
+ uint8_t *cur = &pv->mask->plane[pp].data[c * stride + 1];
+ uint8_t *curn = &pv->mask->plane[pp].data[n * stride + 1];
+ uint8_t *dst = (pv->filter_mode == FILTER_CLASSIC ) ?
+ &pv->mask_filtered->plane[pp].data[c * stride + 1] :
+ &pv->mask_temp->plane[pp].data[c * stride + 1] ;
+
+ for (yy = start; yy < stop; yy++)
+ {
+ for (xx = 1; xx < width - 1; xx++)
+ {
+ int h_count, v_count;
+
+ h_count = cur[xx-1] & cur[xx] & cur[xx+1];
+ v_count = curp[xx] & cur[xx] & curn[xx];
+
+ if (pv->filter_mode == FILTER_CLASSIC)
+ {
+ dst[xx] = h_count;
+ }
+ else
+ {
+ dst[xx] = h_count & v_count;
+ }
+ }
+ curp += stride;
+ cur += stride;
+ curn += stride;
+ dst += stride;
+ }
+ }
+
+ taskset_thread_complete( &pv->mask_filter_taskset, segment );
+ }
+
+ /*
+ * Finished this segment, let everyone know.
+ */
+ taskset_thread_complete( &pv->mask_filter_taskset, segment );
+}
+
+static void decomb_check_thread( void *thread_args_v )
+{
+ hb_filter_private_t * pv;
+ int segment, segment_start, segment_stop;
+ decomb_thread_arg_t *thread_args = thread_args_v;
+
+ pv = thread_args->pv;
+ segment = thread_args->segment;
+
+ hb_log("decomb check thread started for segment %d", segment);
+
+ while (1)
+ {
+ /*
+ * Wait here until there is work to do.
+ */
+ taskset_thread_wait4start( &pv->decomb_check_taskset, segment );
+
+ if (taskset_thread_stop( &pv->decomb_check_taskset, segment ))
+ {
+ /*
+ * No more work to do, exit this thread.
+ */
+ break;
+ }
+
+ segment_start = thread_args->segment_start[0];
+ segment_stop = segment_start + thread_args->segment_height[0];
+
+ if (pv->mode & MODE_FILTER)
+ {
+ check_filtered_combing_mask(pv, segment, segment_start, segment_stop);
+ }
+ else
+ {
+ check_combing_mask(pv, segment, segment_start, segment_stop);
+ }
+
+ taskset_thread_complete( &pv->decomb_check_taskset, segment );
+ }
+
+ /*
+ * Finished this segment, let everyone know.
+ */
+ taskset_thread_complete( &pv->decomb_check_taskset, segment );
+}
+
+/*
+ * comb detect this segment of all three planes in a single thread.
+ */
+static void decomb_filter_thread( void *thread_args_v )
+{
+ hb_filter_private_t * pv;
+ int segment, segment_start, segment_stop;
+ decomb_thread_arg_t *thread_args = thread_args_v;
+
+ pv = thread_args->pv;
+ segment = thread_args->segment;
+
+ hb_log("decomb filter thread started for segment %d", segment);
+
+ while (1)
+ {
+ /*
+ * Wait here until there is work to do.
+ */
+ taskset_thread_wait4start( &pv->decomb_filter_taskset, segment );
+
+ if (taskset_thread_stop( &pv->decomb_filter_taskset, segment ))
+ {
+ /*
+ * No more work to do, exit this thread.
+ */
+ break;
+ }
+
+ /*
+ * Process segment (for now just from luma)
+ */
+ int pp;
+ for (pp = 0; pp < 1; pp++)
+ {
+ segment_start = thread_args->segment_start[pp];
+ segment_stop = segment_start + thread_args->segment_height[pp];
+
+ if (pv->mode & MODE_GAMMA)
+ {
+ detect_gamma_combed_segment( pv, segment_start, segment_stop );
+ }
+ else
+ {
+ detect_combed_segment( pv, segment_start, segment_stop );
+ }
+ }
+
+ taskset_thread_complete( &pv->decomb_filter_taskset, segment );
+ }
+
+ /*
+ * Finished this segment, let everyone know.
+ */
+ taskset_thread_complete( &pv->decomb_filter_taskset, segment );
+}
+
+static int comb_segmenter( hb_filter_private_t * pv )
+{
+ /*
+ * Now that all data for decomb detection is ready for
+ * our threads, fire them off and wait for their completion.
+ */
+ taskset_cycle( &pv->decomb_filter_taskset );
+
+ if (pv->mode & MODE_FILTER)
+ {
+ taskset_cycle( &pv->mask_filter_taskset );
+ if (pv->filter_mode == FILTER_ERODE_DILATE)
+ {
+ taskset_cycle( &pv->mask_erode_taskset );
+ taskset_cycle( &pv->mask_dilate_taskset );
+ taskset_cycle( &pv->mask_erode_taskset );
+ }
+ }
+ reset_combing_results(pv);
+ taskset_cycle(&pv->decomb_check_taskset);
+ return check_combing_results(pv);
+}
+
+static int comb_detect_init( hb_filter_object_t * filter,
+ hb_filter_init_t * init )
+{
+ filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) );
+ hb_filter_private_t * pv = filter->private_data;
+
+ hb_buffer_list_clear(&pv->out_list);
+ build_gamma_lut( pv );
+
+ pv->frames = 0;
+ pv->comb_heavy = 0;
+ pv->comb_light = 0;
+ pv->comb_none = 0;
+
+ pv->comb_detect_ready = 0;
+
+ pv->mode = MODE_GAMMA | MODE_FILTER;
+ pv->filter_mode = FILTER_ERODE_DILATE;
+ pv->spatial_metric = 2;
+ pv->motion_threshold = 3;
+ pv->spatial_threshold = 3;
+ pv->block_threshold = 40;
+ pv->block_width = 16;
+ pv->block_height = 16;
+
+ if (filter->settings)
+ {
+ hb_value_t * dict = filter->settings;
+
+ // Get comb detection settings
+ hb_dict_extract_int(&pv->mode, dict, "mode");
+ hb_dict_extract_int(&pv->spatial_metric, dict, "spatial-metric");
+ hb_dict_extract_int(&pv->motion_threshold, dict, "motion-thresh");
+ hb_dict_extract_int(&pv->spatial_threshold, dict, "spatial-thresh");
+ hb_dict_extract_int(&pv->filter_mode, dict, "filter-mode");
+ hb_dict_extract_int(&pv->block_threshold, dict, "block-thresh");
+ hb_dict_extract_int(&pv->block_width, dict, "block-width");
+ hb_dict_extract_int(&pv->block_height, dict, "block-height");
+ }
+
+ pv->cpu_count = hb_get_cpu_count();
+
+ // Make segment sizes an even number of lines
+ int height = hb_image_height(init->pix_fmt, init->geometry.height, 0);
+ // each segment of each plane must begin on an even row.
+ pv->segment_height[0] = (height / pv->cpu_count) & ~3;
+ pv->segment_height[1] = hb_image_height(init->pix_fmt, pv->segment_height[0], 1);
+ pv->segment_height[2] = hb_image_height(init->pix_fmt, pv->segment_height[0], 2);
+
+ /* Allocate buffers to store comb masks. */
+ pv->mask = hb_frame_buffer_init(init->pix_fmt,
+ init->geometry.width, init->geometry.height);
+ pv->mask_filtered = hb_frame_buffer_init(init->pix_fmt,
+ init->geometry.width, init->geometry.height);
+ pv->mask_temp = hb_frame_buffer_init(init->pix_fmt,
+ init->geometry.width, init->geometry.height);
+ memset(pv->mask->data, 0, pv->mask->size);
+ memset(pv->mask_filtered->data, 0, pv->mask_filtered->size);
+ memset(pv->mask_temp->data, 0, pv->mask_temp->size);
+
+ int ii;
+
+ /*
+ * Create comb detection taskset.
+ */
+ if (taskset_init( &pv->decomb_filter_taskset, pv->cpu_count,
+ sizeof( decomb_thread_arg_t ) ) == 0)
+ {
+ hb_error( "decomb could not initialize taskset" );
+ }
+
+ decomb_thread_arg_t *decomb_prev_thread_args = NULL;
+ for (ii = 0; ii < pv->cpu_count; ii++)
+ {
+ decomb_thread_arg_t *thread_args;
+
+ thread_args = taskset_thread_args( &pv->decomb_filter_taskset, ii );
+ thread_args->pv = pv;
+ thread_args->segment = ii;
+
+ int pp;
+ for (pp = 0; pp < 3; pp++)
+ {
+ if (decomb_prev_thread_args != NULL)
+ {
+ thread_args->segment_start[pp] =
+ decomb_prev_thread_args->segment_start[pp] +
+ decomb_prev_thread_args->segment_height[pp];
+ }
+ if (ii == pv->cpu_count - 1)
+ {
+ /*
+ * Final segment
+ */
+ thread_args->segment_height[pp] =
+ hb_image_height(init->pix_fmt, init->geometry.height, pp) -
+ thread_args->segment_start[pp];
+ } else {
+ thread_args->segment_height[pp] = pv->segment_height[pp];
+ }
+ }
+
+ if (taskset_thread_spawn( &pv->decomb_filter_taskset, ii,
+ "decomb_filter_segment",
+ decomb_filter_thread,
+ HB_NORMAL_PRIORITY ) == 0)
+ {
+ hb_error( "decomb could not spawn thread" );
+ }
+
+ decomb_prev_thread_args = thread_args;
+ }
+
+ pv->comb_check_nthreads = init->geometry.height / pv->block_height;
+
+ if (pv->comb_check_nthreads > pv->cpu_count)
+ pv->comb_check_nthreads = pv->cpu_count;
+
+ pv->block_score = calloc(pv->comb_check_nthreads, sizeof(int));
+
+ /*
+ * Create comb check taskset.
+ */
+ if (taskset_init( &pv->decomb_check_taskset, pv->comb_check_nthreads,
+ sizeof( decomb_thread_arg_t ) ) == 0)
+ {
+ hb_error( "decomb check could not initialize taskset" );
+ }
+
+ decomb_prev_thread_args = NULL;
+ for (ii = 0; ii < pv->comb_check_nthreads; ii++)
+ {
+ decomb_thread_arg_t *thread_args;
+
+ thread_args = taskset_thread_args( &pv->decomb_check_taskset, ii);
+ thread_args->pv = pv;
+ thread_args->segment = ii;
+
+ int pp;
+ for (pp = 0; pp < 3; pp++)
+ {
+ if (decomb_prev_thread_args != NULL)
+ {
+ thread_args->segment_start[pp] =
+ decomb_prev_thread_args->segment_start[pp] +
+ decomb_prev_thread_args->segment_height[pp];
+ }
+
+ // Make segment hight a multiple of block_height
+ int h = hb_image_height(init->pix_fmt, init->geometry.height, pp) / pv->comb_check_nthreads;
+ h = h / pv->block_height * pv->block_height;
+ if (h == 0)
+ h = pv->block_height;
+
+ if (ii == pv->comb_check_nthreads - 1)
+ {
+ /*
+ * Final segment
+ */
+ thread_args->segment_height[pp] =
+ hb_image_height(init->pix_fmt, init->geometry.height, pp) -
+ thread_args->segment_start[pp];
+ } else {
+ thread_args->segment_height[pp] = h;
+ }
+ }
+
+ if (taskset_thread_spawn( &pv->decomb_check_taskset, ii,
+ "decomb_check_segment",
+ decomb_check_thread,
+ HB_NORMAL_PRIORITY ) == 0)
+ {
+ hb_error( "decomb check could not spawn thread" );
+ }
+
+ decomb_prev_thread_args = thread_args;
+ }
+
+ if (pv->mode & MODE_FILTER)
+ {
+ if (taskset_init( &pv->mask_filter_taskset, pv->cpu_count,
+ sizeof( decomb_thread_arg_t ) ) == 0)
+ {
+ hb_error( "maske filter could not initialize taskset" );
+ }
+
+ decomb_prev_thread_args = NULL;
+ for (ii = 0; ii < pv->cpu_count; ii++)
+ {
+ decomb_thread_arg_t *thread_args;
+
+ thread_args = taskset_thread_args( &pv->mask_filter_taskset, ii );
+ thread_args->pv = pv;
+ thread_args->segment = ii;
+
+ int pp;
+ for (pp = 0; pp < 3; pp++)
+ {
+ if (decomb_prev_thread_args != NULL)
+ {
+ thread_args->segment_start[pp] =
+ decomb_prev_thread_args->segment_start[pp] +
+ decomb_prev_thread_args->segment_height[pp];
+ }
+
+ if (ii == pv->cpu_count - 1)
+ {
+ /*
+ * Final segment
+ */
+ thread_args->segment_height[pp] =
+ hb_image_height(init->pix_fmt, init->geometry.height, pp) -
+ thread_args->segment_start[pp];
+ } else {
+ thread_args->segment_height[pp] = pv->segment_height[pp];
+ }
+ }
+
+ if (taskset_thread_spawn( &pv->mask_filter_taskset, ii,
+ "mask_filter_segment",
+ mask_filter_thread,
+ HB_NORMAL_PRIORITY ) == 0)
+ {
+ hb_error( "mask filter could not spawn thread" );
+ }
+
+ decomb_prev_thread_args = thread_args;
+ }
+
+ if (pv->filter_mode == FILTER_ERODE_DILATE)
+ {
+ if (taskset_init( &pv->mask_erode_taskset, pv->cpu_count,
+ sizeof( decomb_thread_arg_t ) ) == 0)
+ {
+ hb_error( "mask erode could not initialize taskset" );
+ }
+
+ decomb_prev_thread_args = NULL;
+ for (ii = 0; ii < pv->cpu_count; ii++)
+ {
+ decomb_thread_arg_t *thread_args;
+
+ thread_args = taskset_thread_args( &pv->mask_erode_taskset, ii );
+ thread_args->pv = pv;
+ thread_args->segment = ii;
+
+ int pp;
+ for (pp = 0; pp < 3; pp++)
+ {
+ if (decomb_prev_thread_args != NULL)
+ {
+ thread_args->segment_start[pp] =
+ decomb_prev_thread_args->segment_start[pp] +
+ decomb_prev_thread_args->segment_height[pp];
+ }
+
+ if (ii == pv->cpu_count - 1)
+ {
+ /*
+ * Final segment
+ */
+ thread_args->segment_height[pp] =
+ hb_image_height(init->pix_fmt, init->geometry.height, pp) -
+ thread_args->segment_start[pp];
+ } else {
+ thread_args->segment_height[pp] = pv->segment_height[pp];
+ }
+ }
+
+ if (taskset_thread_spawn( &pv->mask_erode_taskset, ii,
+ "mask_erode_segment",
+ mask_erode_thread,
+ HB_NORMAL_PRIORITY ) == 0)
+ {
+ hb_error( "mask erode could not spawn thread" );
+ }
+
+ decomb_prev_thread_args = thread_args;
+ }
+
+ if (taskset_init( &pv->mask_dilate_taskset, pv->cpu_count,
+ sizeof( decomb_thread_arg_t ) ) == 0)
+ {
+ hb_error( "mask dilate could not initialize taskset" );
+ }
+
+ decomb_prev_thread_args = NULL;
+ for (ii = 0; ii < pv->cpu_count; ii++)
+ {
+ decomb_thread_arg_t *thread_args;
+
+ thread_args = taskset_thread_args( &pv->mask_dilate_taskset, ii );
+ thread_args->pv = pv;
+ thread_args->segment = ii;
+
+ int pp;
+ for (pp = 0; pp < 3; pp++)
+ {
+ if (decomb_prev_thread_args != NULL)
+ {
+ thread_args->segment_start[pp] =
+ decomb_prev_thread_args->segment_start[pp] +
+ decomb_prev_thread_args->segment_height[pp];
+ }
+
+ if (ii == pv->cpu_count - 1)
+ {
+ /*
+ * Final segment
+ */
+ thread_args->segment_height[pp] =
+ hb_image_height(init->pix_fmt, init->geometry.height, pp) -
+ thread_args->segment_start[pp];
+ } else {
+ thread_args->segment_height[pp] = pv->segment_height[pp];
+ }
+ }
+
+ if (taskset_thread_spawn( &pv->mask_dilate_taskset, ii,
+ "mask_dilate_segment",
+ mask_dilate_thread,
+ HB_NORMAL_PRIORITY ) == 0)
+ {
+ hb_error( "mask dilate could not spawn thread" );
+ }
+
+ decomb_prev_thread_args = thread_args;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void comb_detect_close( hb_filter_object_t * filter )
+{
+ hb_filter_private_t * pv = filter->private_data;
+
+ if (pv == NULL)
+ {
+ return;
+ }
+
+ hb_log("comb detect: heavy %i | light %i | uncombed %i | total %i",
+ pv->comb_heavy, pv->comb_light, pv->comb_none, pv->frames);
+
+ taskset_fini( &pv->decomb_filter_taskset );
+ taskset_fini( &pv->decomb_check_taskset );
+
+ if (pv->mode & MODE_FILTER)
+ {
+ taskset_fini( &pv->mask_filter_taskset );
+ if (pv->filter_mode == FILTER_ERODE_DILATE)
+ {
+ taskset_fini( &pv->mask_erode_taskset );
+ taskset_fini( &pv->mask_dilate_taskset );
+ }
+ }
+
+ /* Cleanup reference buffers. */
+ int ii;
+ for (ii = 0; ii < 3; ii++)
+ {
+ if (!pv->ref_used[ii])
+ {
+ hb_buffer_close(&pv->ref[ii]);
+ }
+ }
+
+ /* Cleanup combing masks. */
+ hb_buffer_close(&pv->mask);
+ hb_buffer_close(&pv->mask_filtered);
+ hb_buffer_close(&pv->mask_temp);
+
+ free(pv->block_score);
+ free( pv );
+ filter->private_data = NULL;
+}
+
+static void process_frame( hb_filter_private_t * pv )
+{
+ int combed;
+
+ combed = comb_segmenter(pv);
+ switch (combed)
+ {
+ case HB_COMB_HEAVY:
+ pv->comb_heavy++;
+ break;
+
+ case HB_COMB_LIGHT:
+ pv->comb_light++;
+ break;
+
+ case HB_COMB_NONE:
+ default:
+ pv->comb_none++;
+ break;
+ }
+ pv->frames++;
+ if ((pv->mode & MODE_MASK) && combed)
+ {
+ hb_buffer_t * out;
+ out = hb_buffer_dup(pv->ref[1]);
+ apply_mask(pv, out);
+ out->s.combed = combed;
+ hb_buffer_list_append(&pv->out_list, out);
+ }
+ else
+ {
+ pv->ref_used[1] = 1;
+ pv->ref[1]->s.combed = combed;
+ hb_buffer_list_append(&pv->out_list, pv->ref[1]);
+ }
+}
+
+static int comb_detect_work( hb_filter_object_t * filter,
+ hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out )
+{
+ hb_filter_private_t * pv = filter->private_data;
+ hb_buffer_t * in = *buf_in;
+
+ // Input buffer is always consumed.
+ *buf_in = NULL;
+ if (in->s.flags & HB_BUF_FLAG_EOF)
+ {
+ // Duplicate last frame and process refs
+ store_ref(pv, hb_buffer_dup(pv->ref[2]));
+ process_frame(pv);
+ hb_buffer_list_append(&pv->out_list, in);
+ *buf_out = hb_buffer_list_clear(&pv->out_list);
+ return HB_FILTER_DONE;
+ }
+
+ // comb detect requires 3 buffers, prev, cur, and next. For the first
+ // frame, there can be no prev, so we duplicate the first frame.
+ if (!pv->comb_detect_ready)
+ {
+ // If not ready, store duplicate ref and return HB_FILTER_DELAY
+ store_ref(pv, hb_buffer_dup(in));
+ store_ref(pv, in);
+ pv->comb_detect_ready = 1;
+ // Wait for next
+ return HB_FILTER_DELAY;
+ }
+
+ store_ref(pv, in);
+ process_frame(pv);
+
+ // Output buffers may also be in comb detect's internal ref list.
+ // Since buffers are not reference counted, we must wait until
+ // we are certain they are no longer in the ref list before sending
+ // down the pipeline where they will ultimately get closed.
+ if (hb_buffer_list_count(&pv->out_list) > 3)
+ {
+ *buf_out = hb_buffer_list_rem_head(&pv->out_list);
+ }
+ return HB_FILTER_OK;
+}
diff --git a/libhb/common.c b/libhb/common.c
index ca74aeb16..3cb804ce7 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -3718,6 +3718,27 @@ hb_list_t *hb_filter_list_copy(const hb_list_t *src)
return list;
}
+hb_filter_object_t * hb_filter_find(const hb_list_t *list, int filter_id)
+{
+ hb_filter_object_t *filter = NULL;
+ int ii;
+
+ if (list == NULL)
+ {
+ return NULL;
+ }
+ for (ii = 0; ii < hb_list_count(list); ii++)
+ {
+ filter = hb_list_item(list, ii);
+ if (filter->id == filter_id)
+ {
+ return filter;
+ }
+ }
+
+ return NULL;
+}
+
/**
* Gets a filter object with the given type
* @param filter_id The type of filter to get.
@@ -3732,6 +3753,10 @@ hb_filter_object_t * hb_filter_get( int filter_id )
filter = &hb_filter_detelecine;
break;
+ case HB_FILTER_COMB_DETECT:
+ filter = &hb_filter_comb_detect;
+ break;
+
case HB_FILTER_DECOMB:
filter = &hb_filter_decomb;
break;
diff --git a/libhb/common.h b/libhb/common.h
index d09c6e9a0..5d99e2eb8 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -1230,6 +1230,7 @@ enum
// First, filters that may change the framerate (drop or dup frames)
HB_FILTER_DETELECINE,
+ HB_FILTER_COMB_DETECT,
HB_FILTER_DECOMB,
HB_FILTER_DEINTERLACE,
HB_FILTER_VFR,
@@ -1259,6 +1260,7 @@ hb_filter_object_t * hb_filter_get( int filter_id );
hb_filter_object_t * hb_filter_init( int filter_id );
hb_filter_object_t * hb_filter_copy( hb_filter_object_t * filter );
hb_list_t * hb_filter_list_copy(const hb_list_t *src);
+hb_filter_object_t * hb_filter_find(const hb_list_t *list, int filter_id);
void hb_filter_close( hb_filter_object_t ** );
void hb_filter_info_close( hb_filter_info_t ** );
hb_dict_t * hb_parse_filter_settings(const char * settings);
diff --git a/libhb/decomb.c b/libhb/decomb.c
index cf0db0b6e..a91dffc7f 100644
--- a/libhb/decomb.c
+++ b/libhb/decomb.c
@@ -13,8 +13,13 @@
/*****
Parameters:
- Mode : Spatial metric : Motion thresh : Spatial thresh : Mask Filter Mode :
- Block thresh : Block width : Block height
+ Mode:
+ 1 = yadif
+ 2 = blend
+ 4 = cubic interpolation
+ 8 = EEDI2 interpolation
+ 16 = Deinterlace each field to a separate frame
+ 32 = Selectively deinterlace based on comb detection
Appended for EEDI2:
Magnitude thresh : Variance thresh : Laplacian thresh : Dilation thresh :
@@ -24,24 +29,9 @@ Plus:
Parity
Defaults:
- 391:2:3:3:2:40:16:16:10:20:20:4:2:50:24:1:-1
+ 7:10:20:20:4:2:50:24:1:-1
-Original "Faster" settings:
- 7:2:6:9:1:80:16:16:10:20:20:4:2:50:24:1:-1
*****/
-#define MODE_YADIF 1 // Use yadif
-#define MODE_BLEND 2 // Use blending interpolation
-#define MODE_CUBIC 4 // Use cubic interpolation
-#define MODE_EEDI2 8 // Use EEDI2 interpolation
-#define MODE_MASK 32 // Output combing masks instead of pictures
-#define MODE_BOB 64 // Deinterlace each field to a separate frame
-
-#define MODE_GAMMA 128 // Scale gamma when decombing
-#define MODE_FILTER 256 // Filter combing mask
-#define MODE_COMPOSITE 512 // Overlay combing mask onto picture
-
-#define FILTER_CLASSIC 1
-#define FILTER_ERODE_DILATE 2
/*****
These modes can be layered. For example, Yadif (1) + EEDI2 (8) = 9,
@@ -60,8 +50,6 @@ which will feed EEDI2 interpolations to yadif.
10: Switch between EEDI2 and blend
11: Switch between EEDI2->yadif and blend
...okay I'm getting bored now listing all these different modes
-32: Passes through the combing mask for every combed frame (white for combed pixels, otherwise black)
-33+: Overlay the combing mask for every combed frame on top of the filtered output (white for combed pixels)
12-15: EEDI2 will override cubic interpolation
*****/
@@ -70,6 +58,7 @@ which will feed EEDI2 interpolations to yadif.
#include "hbffmpeg.h"
#include "eedi2.h"
#include "taskset.h"
+#include "decomb.h"
#define PARITY_DEFAULT -1
@@ -92,7 +81,7 @@ struct yadif_arguments_s {
hb_buffer_t *dst;
int parity;
int tff;
- int is_combed;
+ int mode;
};
typedef struct yadif_arguments_s yadif_arguments_t;
@@ -102,13 +91,6 @@ typedef struct eedi2_thread_arg_s {
int plane;
} eedi2_thread_arg_t;
-typedef struct decomb_thread_arg_s {
- hb_filter_private_t *pv;
- int segment;
- int segment_start[3];
- int segment_height[3];
-} decomb_thread_arg_t;
-
typedef struct yadif_thread_arg_s {
hb_filter_private_t *pv;
int segment;
@@ -118,23 +100,8 @@ typedef struct yadif_thread_arg_s {
struct hb_filter_private_s
{
- // Decomb detect parameters
+ // Decomb parameters
int mode;
- int filter_mode;
- int spatial_metric;
- int motion_threshold;
- int spatial_threshold;
- int block_threshold;
- int block_width;
- int block_height;
-
- int * block_score;
- int comb_check_complete;
- int comb_check_nthreads;
- int skip_comb_check;
- int is_combed;
-
- float gamma_lut[256];
/* Make buffers to store a comb masks. */
hb_buffer_t * mask;
@@ -144,49 +111,45 @@ struct hb_filter_private_s
int mask_box_y;
uint8_t mask_box_color;
- // Deinterlace parameters
- int parity;
// EEDI2 parameters
- int magnitude_threshold;
- int variance_threshold;
- int laplacian_threshold;
- int dilation_threshold;
- int erosion_threshold;
- int noise_threshold;
- int maximum_search_distance;
- int post_processing;
-
- int tff;
+ int magnitude_threshold;
+ int variance_threshold;
+ int laplacian_threshold;
+ int dilation_threshold;
+ int erosion_threshold;
+ int noise_threshold;
+ int maximum_search_distance;
+ int post_processing;
- int yadif_ready;
+ // Deinterlace parameters
+ int parity;
+ int tff;
- int deinterlaced_frames;
- int blended_frames;
- int unfiltered_frames;
+ int yadif_ready;
- hb_buffer_t * ref[3];
+ int deinterlaced;
+ int blended;
+ int unfiltered;
+ int frames;
+ hb_buffer_t * ref[3];
- hb_buffer_t * eedi_half[4];
- hb_buffer_t * eedi_full[5];
- int * cx2;
- int * cy2;
- int * cxy;
- int * tmpc;
+ hb_buffer_t * eedi_half[4];
+ hb_buffer_t * eedi_full[5];
+ int * cx2;
+ int * cy2;
+ int * cxy;
+ int * tmpc;
- int cpu_count;
- int segment_height[3];
+ int cpu_count;
+ int segment_height[3];
- taskset_t yadif_taskset; // Threads for Yadif - one per CPU
- yadif_arguments_t *yadif_arguments; // Arguments to thread for work
+ taskset_t yadif_taskset; // Threads for Yadif - one per CPU
+ yadif_arguments_t * yadif_arguments; // Arguments to thread for work
- taskset_t decomb_filter_taskset; // Threads for comb detection
- taskset_t decomb_check_taskset; // Threads for comb check
- taskset_t mask_filter_taskset; // Threads for decomb mask filter
- taskset_t mask_erode_taskset; // Threads for decomb mask erode
- taskset_t mask_dilate_taskset; // Threads for decomb mask dilate
+ taskset_t eedi2_taskset; // Threads for eedi2 - one per plane
- taskset_t eedi2_taskset; // Threads for eedi2 - one per plane
+ hb_buffer_list_t out_list;
};
typedef struct
@@ -205,10 +168,7 @@ static int hb_decomb_work( hb_filter_object_t * filter,
static void hb_decomb_close( hb_filter_object_t * filter );
static const char decomb_template[] =
- "mode=^"HB_INT_REG"$:spatial-metric=^([012])$:"
- "motion-thresh=^"HB_INT_REG"$:spatial-thresh=^"HB_INT_REG"$:"
- "filter-mode=^([012])$:block-thresh=^"HB_INT_REG"$:"
- "block-width=^"HB_INT_REG"$:block-height=^"HB_INT_REG"$:"
+ "mode=^"HB_INT_REG"$:"
"magnitude-thresh=^"HB_INT_REG"$:variance-thresh=^"HB_INT_REG"$:"
"laplacian-thresh=^"HB_INT_REG"$:dilation-thresh=^"HB_INT_REG"$:"
"erosion-thresh=^"HB_INT_REG"$:noise-thresh=^"HB_INT_REG"$:"
@@ -321,102 +281,6 @@ static void cubic_interpolate_line(
}
}
-static void draw_mask_box( hb_filter_private_t * pv )
-{
- int x = pv->mask_box_x;
- int y = pv->mask_box_y;
- int box_width = pv->block_width;
- int box_height = pv->block_height;
- int stride;
- uint8_t * mskp;
-
- if (pv->mode & MODE_FILTER)
- {
- mskp = pv->mask_filtered->plane[0].data;
- stride = pv->mask_filtered->plane[0].stride;
- }
- else
- {
- mskp = pv->mask->plane[0].data;
- stride = pv->mask->plane[0].stride;
- }
-
-
- int block_x, block_y;
- for( block_x = 0; block_x < box_width; block_x++)
- {
- mskp[y*stride+x+block_x] = 128;
- mskp[(y+box_height)*stride+x+block_x] = 128;
- }
-
- for( block_y = 0; block_y < box_height; block_y++)
- {
- mskp[stride*(y+block_y)+x] = 128;
- mskp[stride*(y+block_y) + x + box_width] = 128;
- }
-}
-
-static void apply_mask_line( uint8_t * srcp,
- uint8_t * mskp,
- int width )
-{
- int x;
-
- for( x = 0; x < width; x++ )
- {
- if( mskp[x] == 1 )
- {
- srcp[x] = 255;
- }
- if( mskp[x] == 128 )
- {
- srcp[x] = 128;
- }
- }
-}
-
-static void apply_mask(hb_filter_private_t * pv, hb_buffer_t * b)
-{
- /* draw_boxes */
- draw_mask_box( pv );
-
- int pp, yy;
- hb_buffer_t * m;
-
- if (pv->mode & MODE_FILTER)
- {
- m = pv->mask_filtered;
- }
- else
- {
- m = pv->mask;
- }
- for (pp = 0; pp < 3; pp++)
- {
- uint8_t * dstp = b->plane[pp].data;
- uint8_t * mskp = m->plane[pp].data;
-
- for( yy = 0; yy < m->plane[pp].height; yy++ )
- {
- if (!(pv->mode & MODE_COMPOSITE) && pp == 0)
- {
- memcpy(dstp, mskp, m->plane[pp].width);
- }
- else if (!(pv->mode & MODE_COMPOSITE))
- {
- memset(dstp, 128, m->plane[pp].width);
- }
- if (pp == 0)
- {
- apply_mask_line(dstp, mskp, m->plane[pp].width);
- }
-
- dstp += b->plane[pp].stride;
- mskp += m->plane[pp].stride;
- }
- }
-}
-
static void store_ref(hb_filter_private_t * pv, hb_buffer_t * b)
{
hb_buffer_close(&pv->ref[0]);
@@ -503,452 +367,6 @@ static void blend_filter_line(filter_param_t *filter,
}
}
-static void reset_combing_results( hb_filter_private_t * pv )
-{
- pv->comb_check_complete = 0;
- int ii;
- for (ii = 0; ii < pv->comb_check_nthreads; ii++)
- {
- pv->block_score[ii] = 0;
- }
-}
-
-static int check_combing_results( hb_filter_private_t * pv )
-{
- int threshold = pv->block_threshold;
- int send_to_blend = 0;
-
- int ii;
- for (ii = 0; ii < pv->comb_check_nthreads; ii++)
- {
- if( pv->block_score[ii] >= ( threshold / 2 ) )
- {
- if (pv->block_score[ii] <= threshold)
- {
- /* Blend video content that scores between
- ( threshold / 2 ) and threshold. */
- send_to_blend = 1;
- pv->mask_box_color = 2;
- }
- else if( pv->block_score[ii] > threshold )
- {
- /* Yadif deinterlace video content above the threshold. */
- pv->mask_box_color = 1;
- return 1;
- }
- }
- }
-
- if( send_to_blend )
- {
- return 2;
- }
- else
- {
- /* Consider this frame to be uncombed. */
- return 0;
- }
-}
-
-static void check_filtered_combing_mask( hb_filter_private_t * pv, int segment, int start, int stop )
-{
- /* Go through the mask in X*Y blocks. If any of these windows
- have threshold or more combed pixels, consider the whole
- frame to be combed and send it on to be deinterlaced. */
-
- /* Block mask threshold -- The number of pixels
- in a block_width * block_height window of
- he mask that need to show combing for the
- whole frame to be seen as such. */
- int threshold = pv->block_threshold;
- int block_width = pv->block_width;
- int block_height = pv->block_height;
- int block_x, block_y;
- int block_score = 0;
- uint8_t * mask_p;
- int x, y, pp;
-
- for( pp = 0; pp < 1; pp++ )
- {
- int stride = pv->mask_filtered->plane[pp].stride;
- int width = pv->mask_filtered->plane[pp].width;
-
- pv->mask_box_x = -1;
- pv->mask_box_y = -1;
- pv->mask_box_color = 0;
-
- for( y = start; y < ( stop - block_height + 1 ); y = y + block_height )
- {
- for( x = 0; x < ( width - block_width ); x = x + block_width )
- {
- block_score = 0;
-
- for( block_y = 0; block_y < block_height; block_y++ )
- {
- int my = y + block_y;
- mask_p = &pv->mask_filtered->plane[pp].data[my*stride + x];
-
- for( block_x = 0; block_x < block_width; block_x++ )
- {
- block_score += mask_p[0];
- mask_p++;
- }
- }
-
- if (pv->comb_check_complete)
- {
- // Some other thread found coming before this one
- return;
- }
-
- if( block_score >= ( threshold / 2 ) )
- {
- pv->mask_box_x = x;
- pv->mask_box_y = y;
-
- pv->block_score[segment] = block_score;
- if( block_score > threshold )
- {
- pv->comb_check_complete = 1;
- return;
- }
- }
- }
- }
- }
-}
-
-static void check_combing_mask( hb_filter_private_t * pv, int segment, int start, int stop )
-{
- /* Go through the mask in X*Y blocks. If any of these windows
- have threshold or more combed pixels, consider the whole
- frame to be combed and send it on to be deinterlaced. */
-
- /* Block mask threshold -- The number of pixels
- in a block_width * block_height window of
- he mask that need to show combing for the
- whole frame to be seen as such. */
- int threshold = pv->block_threshold;
- int block_width = pv->block_width;
- int block_height = pv->block_height;
- int block_x, block_y;
- int block_score = 0;
- uint8_t * mask_p;
- int x, y, pp;
-
- for( pp = 0; pp < 1; pp++ )
- {
- int stride = pv->mask->plane[pp].stride;
- int width = pv->mask->plane[pp].width;
-
- for (y = start; y < (stop - block_height + 1); y = y + block_height)
- {
- for (x = 0; x < (width - block_width); x = x + block_width)
- {
- block_score = 0;
-
- for( block_y = 0; block_y < block_height; block_y++ )
- {
- int mask_y = y + block_y;
- mask_p = &pv->mask->plane[pp].data[mask_y * stride + x];
-
- for( block_x = 0; block_x < block_width; block_x++ )
- {
- /* We only want to mark a pixel in a block as combed
- if the adjacent pixels are as well. Got to
- handle the sides separately. */
- if( (x + block_x) == 0 )
- {
- block_score += mask_p[0] & mask_p[1];
- }
- else if( (x + block_x) == (width -1) )
- {
- block_score += mask_p[-1] & mask_p[0];
- }
- else
- {
- block_score += mask_p[-1] & mask_p[0] & mask_p[1];
- }
-
- mask_p++;
- }
- }
-
- if (pv->comb_check_complete)
- {
- // Some other thread found coming before this one
- return;
- }
-
- if( block_score >= ( threshold / 2 ) )
- {
- pv->mask_box_x = x;
- pv->mask_box_y = y;
-
- pv->block_score[segment] = block_score;
- if( block_score > threshold )
- {
- pv->comb_check_complete = 1;
- return;
- }
- }
- }
- }
- }
-}
-
-static void build_gamma_lut( hb_filter_private_t * pv )
-{
- int i;
- for( i = 0; i < 256; i++ )
- {
- pv->gamma_lut[i] = pow( ( (float)i / (float)255 ), 2.2f );
- }
-}
-
-static void detect_gamma_combed_segment( hb_filter_private_t * pv, int segment_start, int segment_stop )
-{
- /* A mish-mash of various comb detection tricks
- picked up from neuron2's Decomb plugin for
- AviSynth and tritical's IsCombedT and
- IsCombedTIVTC plugins. */
-
- /* Comb scoring algorithm */
- /* Motion threshold */
- float mthresh = (float)pv->motion_threshold / (float)255;
- /* Spatial threshold */
- float athresh = (float)pv->spatial_threshold / (float)255;
- float athresh6 = 6 *athresh;
-
- /* One pas for Y, one pass for U, one pass for V */
- int pp;
- for( pp = 0; pp < 1; pp++ )
- {
- int x, y;
- int stride = pv->ref[0]->plane[pp].stride;
- int width = pv->ref[0]->plane[pp].width;
- int height = pv->ref[0]->plane[pp].height;
-
- /* Comb detection has to start at y = 2 and end at
- y = height - 2, because it needs to examine
- 2 pixels above and 2 below the current pixel. */
- if( segment_start < 2 )
- segment_start = 2;
- if( segment_stop > height - 2 )
- segment_stop = height - 2;
-
- for( y = segment_start; y < segment_stop; y++ )
- {
- /* These are just to make the buffer locations easier to read. */
- int up_2 = -2 * stride ;
- int up_1 = -1 * stride;
- int down_1 = stride;
- int down_2 = 2 * stride;
-
- /* We need to examine a column of 5 pixels
- in the prev, cur, and next frames. */
- uint8_t * prev = &pv->ref[0]->plane[pp].data[y * stride];
- uint8_t * cur = &pv->ref[1]->plane[pp].data[y * stride];
- uint8_t * next = &pv->ref[2]->plane[pp].data[y * stride];
- uint8_t * mask = &pv->mask->plane[pp].data[y * stride];
-
- memset(mask, 0, stride);
-
- for( x = 0; x < width; x++ )
- {
- float up_diff, down_diff;
- up_diff = pv->gamma_lut[cur[0]] - pv->gamma_lut[cur[up_1]];
- down_diff = pv->gamma_lut[cur[0]] - pv->gamma_lut[cur[down_1]];
-
- if( ( up_diff > athresh && down_diff > athresh ) ||
- ( up_diff < -athresh && down_diff < -athresh ) )
- {
- /* The pixel above and below are different,
- and they change in the same "direction" too.*/
- int motion = 0;
- if( mthresh > 0 )
- {
- /* Make sure there's sufficient motion between frame t-1 to frame t+1. */
- if( fabs( pv->gamma_lut[prev[0]] - pv->gamma_lut[cur[0]] ) > mthresh &&
- fabs( pv->gamma_lut[cur[up_1]] - pv->gamma_lut[next[up_1]] ) > mthresh &&
- fabs( pv->gamma_lut[cur[down_1]] - pv->gamma_lut[next[down_1]] ) > mthresh )
- motion++;
- if( fabs( pv->gamma_lut[next[0]] - pv->gamma_lut[cur[0]] ) > mthresh &&
- fabs( pv->gamma_lut[prev[up_1]] - pv->gamma_lut[cur[up_1]] ) > mthresh &&
- fabs( pv->gamma_lut[prev[down_1]] - pv->gamma_lut[cur[down_1]] ) > mthresh )
- motion++;
-
- }
- else
- {
- /* User doesn't want to check for motion,
- so move on to the spatial check. */
- motion = 1;
- }
-
- if( motion || ( pv->deinterlaced_frames==0 && pv->blended_frames==0 && pv->unfiltered_frames==0) )
- {
-
- /* Tritical's noise-resistant combing scorer.
- The check is done on a bob+blur convolution. */
- float combing = fabs( pv->gamma_lut[cur[up_2]]
- + ( 4 * pv->gamma_lut[cur[0]] )
- + pv->gamma_lut[cur[down_2]]
- - ( 3 * ( pv->gamma_lut[cur[up_1]]
- + pv->gamma_lut[cur[down_1]] ) ) );
- /* If the frame is sufficiently combed,
- then mark it down on the mask as 1. */
- if( combing > athresh6 )
- {
- mask[0] = 1;
- }
- }
- }
-
- cur++;
- prev++;
- next++;
- mask++;
- }
- }
- }
-}
-
-
-static void detect_combed_segment( hb_filter_private_t * pv, int segment_start, int segment_stop )
-{
- /* A mish-mash of various comb detection tricks
- picked up from neuron2's Decomb plugin for
- AviSynth and tritical's IsCombedT and
- IsCombedTIVTC plugins. */
-
- /* Comb scoring algorithm */
- int spatial_metric = pv->spatial_metric;
- /* Motion threshold */
- int mthresh = pv->motion_threshold;
- /* Spatial threshold */
- int athresh = pv->spatial_threshold;
- int athresh_squared = athresh * athresh;
- int athresh6 = 6 * athresh;
-
- /* One pas for Y, one pass for U, one pass for V */
- int pp;
- for( pp = 0; pp < 1; pp++ )
- {
- int x, y;
- int stride = pv->ref[0]->plane[pp].stride;
- int width = pv->ref[0]->plane[pp].width;
- int height = pv->ref[0]->plane[pp].height;
-
- /* Comb detection has to start at y = 2 and end at
- y = height - 2, because it needs to examine
- 2 pixels above and 2 below the current pixel. */
- if( segment_start < 2 )
- segment_start = 2;
- if( segment_stop > height - 2 )
- segment_stop = height - 2;
-
- for( y = segment_start; y < segment_stop; y++ )
- {
- /* These are just to make the buffer locations easier to read. */
- int up_2 = -2 * stride ;
- int up_1 = -1 * stride;
- int down_1 = stride;
- int down_2 = 2 * stride;
-
- /* We need to examine a column of 5 pixels
- in the prev, cur, and next frames. */
- uint8_t * prev = &pv->ref[0]->plane[pp].data[y * stride];
- uint8_t * cur = &pv->ref[1]->plane[pp].data[y * stride];
- uint8_t * next = &pv->ref[2]->plane[pp].data[y * stride];
- uint8_t * mask = &pv->mask->plane[pp].data[y * stride];
-
- memset(mask, 0, stride);
-
- for( x = 0; x < width; x++ )
- {
- int up_diff = cur[0] - cur[up_1];
- int down_diff = cur[0] - cur[down_1];
-
- if( ( up_diff > athresh && down_diff > athresh ) ||
- ( up_diff < -athresh && down_diff < -athresh ) )
- {
- /* The pixel above and below are different,
- and they change in the same "direction" too.*/
- int motion = 0;
- if( mthresh > 0 )
- {
- /* Make sure there's sufficient motion between frame t-1 to frame t+1. */
- if( abs( prev[0] - cur[0] ) > mthresh &&
- abs( cur[up_1] - next[up_1] ) > mthresh &&
- abs( cur[down_1] - next[down_1] ) > mthresh )
- motion++;
- if( abs( next[0] - cur[0] ) > mthresh &&
- abs( prev[up_1] - cur[up_1] ) > mthresh &&
- abs( prev[down_1] - cur[down_1] ) > mthresh )
- motion++;
- }
- else
- {
- /* User doesn't want to check for motion,
- so move on to the spatial check. */
- motion = 1;
- }
-
- if( motion || ( pv->deinterlaced_frames==0 && pv->blended_frames==0 && pv->unfiltered_frames==0) )
- {
- /* That means it's time for the spatial check.
- We've got several options here. */
- if( spatial_metric == 0 )
- {
- /* Simple 32detect style comb detection */
- if( ( abs( cur[0] - cur[down_2] ) < 10 ) &&
- ( abs( cur[0] - cur[down_1] ) > 15 ) )
- {
- mask[0] = 1;
- }
- }
- else if( spatial_metric == 1 )
- {
- /* This, for comparison, is what IsCombed uses.
- It's better, but still noise senstive. */
- int combing = ( cur[up_1] - cur[0] ) *
- ( cur[down_1] - cur[0] );
-
- if( combing > athresh_squared )
- {
- mask[0] = 1;
- }
- }
- else if( spatial_metric == 2 )
- {
- /* Tritical's noise-resistant combing scorer.
- The check is done on a bob+blur convolution. */
- int combing = abs( cur[up_2]
- + ( 4 * cur[0] )
- + cur[down_2]
- - ( 3 * ( cur[up_1]
- + cur[down_1] ) ) );
-
- /* If the frame is sufficiently combed,
- then mark it down on the mask as 1. */
- if( combing > athresh6 )
- {
- mask[0] = 1;
- }
- }
- }
- }
-
- cur++;
- prev++;
- next++;
- mask++;
- }
- }
- }
-}
-
// This function calls all the eedi2 filters in sequence for a given plane.
// It outputs the final interpolated image to pv->eedi_full[DST2PF].
static void eedi2_interpolate_plane( hb_filter_private_t * pv, int plane )
@@ -1095,446 +513,6 @@ static void eedi2_planer( hb_filter_private_t * pv )
taskset_cycle( &pv->eedi2_taskset );
}
-
-static void mask_dilate_thread( void *thread_args_v )
-{
- hb_filter_private_t * pv;
- int segment, segment_start, segment_stop;
- decomb_thread_arg_t *thread_args = thread_args_v;
-
- pv = thread_args->pv;
- segment = thread_args->segment;
-
- hb_log("mask dilate thread started for segment %d", segment);
-
- while (1)
- {
- /*
- * Wait here until there is work to do.
- */
- taskset_thread_wait4start( &pv->mask_dilate_taskset, segment );
-
- if (taskset_thread_stop(&pv->mask_dilate_taskset, segment))
- {
- /*
- * No more work to do, exit this thread.
- */
- break;
- }
-
- int xx, yy, pp;
-
- int count;
- int dilation_threshold = 4;
-
- for( pp = 0; pp < 1; pp++ )
- {
- int width = pv->mask_filtered->plane[pp].width;
- int height = pv->mask_filtered->plane[pp].height;
- int stride = pv->mask_filtered->plane[pp].stride;
-
- int start, stop, p, c, n;
- segment_start = thread_args->segment_start[pp];
- segment_stop = segment_start + thread_args->segment_height[pp];
-
- if (segment_start == 0)
- {
- start = 1;
- p = 0;
- c = 1;
- n = 2;
- }
- else
- {
- start = segment_start;
- p = segment_start - 1;
- c = segment_start;
- n = segment_start + 1;
- }
-
- if (segment_stop == height)
- {
- stop = height -1;
- }
- else
- {
- stop = segment_stop;
- }
-
- uint8_t *curp = &pv->mask_filtered->plane[pp].data[p * stride + 1];
- uint8_t *cur = &pv->mask_filtered->plane[pp].data[c * stride + 1];
- uint8_t *curn = &pv->mask_filtered->plane[pp].data[n * stride + 1];
- uint8_t *dst = &pv->mask_temp->plane[pp].data[c * stride + 1];
-
- for( yy = start; yy < stop; yy++ )
- {
- for( xx = 1; xx < width - 1; xx++ )
- {
- if (cur[xx])
- {
- dst[xx] = 1;
- continue;
- }
-
- count = curp[xx-1] + curp[xx] + curp[xx+1] +
- cur [xx-1] + cur [xx+1] +
- curn[xx-1] + curn[xx] + curn[xx+1];
-
- dst[xx] = count >= dilation_threshold;
- }
- curp += stride;
- cur += stride;
- curn += stride;
- dst += stride;
- }
- }
-
- taskset_thread_complete( &pv->mask_dilate_taskset, segment );
- }
-
- /*
- * Finished this segment, let everyone know.
- */
- taskset_thread_complete( &pv->mask_dilate_taskset, segment );
-}
-
-static void mask_erode_thread( void *thread_args_v )
-{
- hb_filter_private_t * pv;
- int segment, segment_start, segment_stop;
- decomb_thread_arg_t *thread_args = thread_args_v;
-
- pv = thread_args->pv;
- segment = thread_args->segment;
-
- hb_log("mask erode thread started for segment %d", segment);
-
- while (1)
- {
- /*
- * Wait here until there is work to do.
- */
- taskset_thread_wait4start( &pv->mask_erode_taskset, segment );
-
- if( taskset_thread_stop( &pv->mask_erode_taskset, segment ) )
- {
- /*
- * No more work to do, exit this thread.
- */
- break;
- }
-
- int xx, yy, pp;
-
- int count;
- int erosion_threshold = 2;
-
- for( pp = 0; pp < 1; pp++ )
- {
- int width = pv->mask_filtered->plane[pp].width;
- int height = pv->mask_filtered->plane[pp].height;
- int stride = pv->mask_filtered->plane[pp].stride;
-
- int start, stop, p, c, n;
- segment_start = thread_args->segment_start[pp];
- segment_stop = segment_start + thread_args->segment_height[pp];
-
- if (segment_start == 0)
- {
- start = 1;
- p = 0;
- c = 1;
- n = 2;
- }
- else
- {
- start = segment_start;
- p = segment_start - 1;
- c = segment_start;
- n = segment_start + 1;
- }
-
- if (segment_stop == height)
- {
- stop = height -1;
- }
- else
- {
- stop = segment_stop;
- }
-
- uint8_t *curp = &pv->mask_temp->plane[pp].data[p * stride + 1];
- uint8_t *cur = &pv->mask_temp->plane[pp].data[c * stride + 1];
- uint8_t *curn = &pv->mask_temp->plane[pp].data[n * stride + 1];
- uint8_t *dst = &pv->mask_filtered->plane[pp].data[c * stride + 1];
-
- for( yy = start; yy < stop; yy++ )
- {
- for( xx = 1; xx < width - 1; xx++ )
- {
- if( cur[xx] == 0 )
- {
- dst[xx] = 0;
- continue;
- }
-
- count = curp[xx-1] + curp[xx] + curp[xx+1] +
- cur [xx-1] + cur [xx+1] +
- curn[xx-1] + curn[xx] + curn[xx+1];
-
- dst[xx] = count >= erosion_threshold;
- }
- curp += stride;
- cur += stride;
- curn += stride;
- dst += stride;
- }
- }
-
- taskset_thread_complete( &pv->mask_erode_taskset, segment );
- }
-
- /*
- * Finished this segment, let everyone know.
- */
- taskset_thread_complete( &pv->mask_erode_taskset, segment );
-}
-
-static void mask_filter_thread( void *thread_args_v )
-{
- hb_filter_private_t * pv;
- int segment, segment_start, segment_stop;
- decomb_thread_arg_t *thread_args = thread_args_v;
-
- pv = thread_args->pv;
- segment = thread_args->segment;
-
- hb_log("mask filter thread started for segment %d", segment);
-
- while (1)
- {
- /*
- * Wait here until there is work to do.
- */
- taskset_thread_wait4start( &pv->mask_filter_taskset, segment );
-
- if( taskset_thread_stop( &pv->mask_filter_taskset, segment ) )
- {
- /*
- * No more work to do, exit this thread.
- */
- break;
- }
-
- int xx, yy, pp;
-
- for( pp = 0; pp < 1; pp++ )
- {
- int width = pv->mask->plane[pp].width;
- int height = pv->mask->plane[pp].height;
- int stride = pv->mask->plane[pp].stride;
-
- int start, stop, p, c, n;
- segment_start = thread_args->segment_start[pp];
- segment_stop = segment_start + thread_args->segment_height[pp];
-
- if (segment_start == 0)
- {
- start = 1;
- p = 0;
- c = 1;
- n = 2;
- }
- else
- {
- start = segment_start;
- p = segment_start - 1;
- c = segment_start;
- n = segment_start + 1;
- }
-
- if (segment_stop == height)
- {
- stop = height - 1;
- }
- else
- {
- stop = segment_stop;
- }
-
- uint8_t *curp = &pv->mask->plane[pp].data[p * stride + 1];
- uint8_t *cur = &pv->mask->plane[pp].data[c * stride + 1];
- uint8_t *curn = &pv->mask->plane[pp].data[n * stride + 1];
- uint8_t *dst = (pv->filter_mode == FILTER_CLASSIC ) ?
- &pv->mask_filtered->plane[pp].data[c * stride + 1] :
- &pv->mask_temp->plane[pp].data[c * stride + 1] ;
-
- for( yy = start; yy < stop; yy++ )
- {
- for( xx = 1; xx < width - 1; xx++ )
- {
- int h_count, v_count;
-
- h_count = cur[xx-1] & cur[xx] & cur[xx+1];
- v_count = curp[xx] & cur[xx] & curn[xx];
-
- if (pv->filter_mode == FILTER_CLASSIC)
- {
- dst[xx] = h_count;
- }
- else
- {
- dst[xx] = h_count & v_count;
- }
- }
- curp += stride;
- cur += stride;
- curn += stride;
- dst += stride;
- }
- }
-
- taskset_thread_complete( &pv->mask_filter_taskset, segment );
- }
-
- /*
- * Finished this segment, let everyone know.
- */
- taskset_thread_complete( &pv->mask_filter_taskset, segment );
-}
-
-static void decomb_check_thread( void *thread_args_v )
-{
- hb_filter_private_t * pv;
- int segment, segment_start, segment_stop;
- decomb_thread_arg_t *thread_args = thread_args_v;
-
- pv = thread_args->pv;
- segment = thread_args->segment;
-
- hb_log("decomb check thread started for segment %d", segment);
-
- while (1)
- {
- /*
- * Wait here until there is work to do.
- */
- taskset_thread_wait4start( &pv->decomb_check_taskset, segment );
-
- if( taskset_thread_stop( &pv->decomb_check_taskset, segment ) )
- {
- /*
- * No more work to do, exit this thread.
- */
- break;
- }
-
- segment_start = thread_args->segment_start[0];
- segment_stop = segment_start + thread_args->segment_height[0];
-
- if( pv->mode & MODE_FILTER )
- {
- check_filtered_combing_mask(pv, segment, segment_start, segment_stop);
- }
- else
- {
- check_combing_mask(pv, segment, segment_start, segment_stop);
- }
-
- taskset_thread_complete( &pv->decomb_check_taskset, segment );
- }
-
- /*
- * Finished this segment, let everyone know.
- */
- taskset_thread_complete( &pv->decomb_check_taskset, segment );
-}
-
-/*
- * comb detect this segment of all three planes in a single thread.
- */
-static void decomb_filter_thread( void *thread_args_v )
-{
- hb_filter_private_t * pv;
- int segment, segment_start, segment_stop;
- decomb_thread_arg_t *thread_args = thread_args_v;
-
- pv = thread_args->pv;
- segment = thread_args->segment;
-
- hb_log("decomb filter thread started for segment %d", segment);
-
- while (1)
- {
- /*
- * Wait here until there is work to do.
- */
- taskset_thread_wait4start( &pv->decomb_filter_taskset, segment );
-
- if( taskset_thread_stop( &pv->decomb_filter_taskset, segment ) )
- {
- /*
- * No more work to do, exit this thread.
- */
- break;
- }
-
- /*
- * Process segment (for now just from luma)
- */
- int pp;
- for( pp = 0; pp < 1; pp++)
- {
- segment_start = thread_args->segment_start[pp];
- segment_stop = segment_start + thread_args->segment_height[pp];
-
- if( pv->mode & MODE_GAMMA )
- {
- detect_gamma_combed_segment( pv, segment_start, segment_stop );
- }
- else
- {
- detect_combed_segment( pv, segment_start, segment_stop );
- }
- }
-
- taskset_thread_complete( &pv->decomb_filter_taskset, segment );
- }
-
- /*
- * Finished this segment, let everyone know.
- */
- taskset_thread_complete( &pv->decomb_filter_taskset, segment );
-}
-
-static int comb_segmenter( hb_filter_private_t * pv )
-{
- /*
- * Now that all data for decomb detection is ready for
- * our threads, fire them off and wait for their completion.
- */
- taskset_cycle( &pv->decomb_filter_taskset );
-
- if( pv->mode & MODE_FILTER )
- {
- taskset_cycle( &pv->mask_filter_taskset );
- if( pv->filter_mode == FILTER_ERODE_DILATE )
- {
- taskset_cycle( &pv->mask_erode_taskset );
- taskset_cycle( &pv->mask_dilate_taskset );
- taskset_cycle( &pv->mask_erode_taskset );
- }
- //return check_filtered_combing_mask( pv );
- }
- else
- {
- //return check_combing_mask( pv );
- }
- reset_combing_results(pv);
- taskset_cycle( &pv->decomb_check_taskset );
- return check_combing_results(pv);
-}
-
/* EDDI: Edge Directed Deinterlacing Interpolation
Checks 4 different slopes to see if there is more similarity along a diagonal
than there was vertically. If a diagonal is more similar, then it indicates
@@ -1546,7 +524,7 @@ static int comb_segmenter( hb_filter_private_t * pv )
+ ABS(cur[-stride+1+j] - cur[+stride+1-j]);\
if( score < spatial_score ){\
spatial_score = score;\
- if( ( pv->mode & MODE_CUBIC ) && !vertical_edge )\
+ if( ( pv->mode & MODE_DECOMB_CUBIC ) && !vertical_edge )\
{\
switch(j)\
{\
@@ -1590,7 +568,7 @@ static void yadif_filter_line(
uint8_t *next2 = parity ? cur : next;
int x;
- int eedi2_mode = ( pv->mode & MODE_EEDI2 );
+ int eedi2_mode = ( pv->mode & MODE_DECOMB_EEDI2 );
/* We can replace spatial_pred with this interpolation*/
uint8_t * eedi2_guess = NULL;
@@ -1639,7 +617,7 @@ static void yadif_filter_line(
ABS(cur[-stride+1] - cur[+stride+1]) - 1;
/* Spatial pred is either a bilinear or cubic vertical interpolation. */
- if( ( pv->mode & MODE_CUBIC ) && !vertical_edge)
+ if( ( pv->mode & MODE_DECOMB_CUBIC ) && !vertical_edge)
{
spatial_pred = cubic_interpolate_pixel( cur[-3*stride], cur[-stride], cur[+stride], cur[3*stride] );
}
@@ -1649,10 +627,10 @@ static void yadif_filter_line(
}
// YADIF_CHECK requires a margin to avoid invalid memory access.
- // In MODE_CUBIC, margin needed is 2 + ABS(param).
+ // In MODE_DECOMB_CUBIC, margin needed is 2 + ABS(param).
// Else, the margin needed is 1 + ABS(param).
int margin = 2;
- if (pv->mode & MODE_CUBIC)
+ if (pv->mode & MODE_DECOMB_CUBIC)
margin = 3;
if (x >= margin && x <= width - (margin + 1))
@@ -1743,9 +721,9 @@ static void yadif_decomb_filter_thread( void *thread_args_v )
* Process all three planes, but only this segment of it.
*/
hb_buffer_t *dst;
- int parity, tff, is_combed;
+ int parity, tff, mode;
- is_combed = pv->yadif_arguments[segment].is_combed;
+ mode = pv->yadif_arguments[segment].mode;
dst = yadif_work->dst;
tff = yadif_work->tff;
parity = yadif_work->parity;
@@ -1769,7 +747,7 @@ static void yadif_decomb_filter_thread( void *thread_args_v )
uint8_t *cur = &pv->ref[1]->plane[pp].data[start * stride];
uint8_t *next = &pv->ref[2]->plane[pp].data[start * stride];
- if( is_combed == 2 )
+ if (mode == MODE_DECOMB_BLEND)
{
/* These will be useful if we ever do temporal blending. */
for( yy = start; yy < segment_stop; yy += 2 )
@@ -1780,7 +758,7 @@ static void yadif_decomb_filter_thread( void *thread_args_v )
cur += stride * 2;
}
}
- else if (pv->mode == MODE_CUBIC && is_combed)
+ else if (mode == MODE_DECOMB_CUBIC)
{
for( yy = start; yy < segment_stop; yy += 2 )
{
@@ -1790,7 +768,7 @@ static void yadif_decomb_filter_thread( void *thread_args_v )
cur += stride * 2;
}
}
- else if ((pv->mode & MODE_YADIF) && is_combed == 1)
+ else if (mode & MODE_DECOMB_YADIF)
{
for( yy = start; yy < segment_stop; yy += 2 )
{
@@ -1856,70 +834,47 @@ static void yadif_filter( hb_filter_private_t * pv,
int tff)
{
/* If we're running comb detection, do it now, otherwise default to true. */
- int is_combed;
+ int is_combed = HB_COMB_HEAVY;
+ int mode = 0;
- if (!pv->skip_comb_check)
+ if (pv->mode & MODE_DECOMB_SELECTIVE)
{
- is_combed = pv->spatial_metric >= 0 ? comb_segmenter( pv ) : 1;
- }
- else
- {
- is_combed = pv->is_combed;
+ is_combed = pv->ref[1]->s.combed;
}
- /* The comb detector suggests three different values:
- 0: Don't comb this frame.
- 1: Deinterlace this frame.
- 2: Blend this frame.
- Since that might conflict with the filter's mode,
- it may be necesary to adjust this value. */
- if( is_combed == 1 && (pv->mode == MODE_BLEND) )
- {
- /* All combed frames are getting blended */
- is_combed = 2;
- }
- else if( is_combed == 2 && !( pv->mode & MODE_BLEND ) )
+ // Pick a mode based on the comb detect state and selected decomb modes
+ if ((pv->mode & MODE_DECOMB_BLEND) && is_combed == HB_COMB_LIGHT )
{
- /* Blending is disabled, so force interpolation of these frames. */
- is_combed = 1;
+ mode = MODE_DECOMB_BLEND;
}
- if( is_combed == 1 &&
- ( pv->mode & MODE_BLEND ) &&
- !( pv->mode & ( MODE_YADIF | MODE_EEDI2 | MODE_CUBIC ) ) )
+ else if (is_combed != HB_COMB_NONE)
{
- /* Deinterlacers are disabled, blending isn't, so blend these frames. */
- is_combed = 2;
- }
- else if( is_combed &&
- !( pv->mode & ( MODE_BLEND | MODE_YADIF | MODE_EEDI2 | MODE_CUBIC | MODE_MASK ) ) )
- {
- /* No deinterlacer or mask chosen, pass the frame through. */
- is_combed = 0;
+ mode = pv->mode & ~MODE_DECOMB_SELECTIVE;
}
- if( is_combed == 1 )
+ if (mode == MODE_DECOMB_BLEND)
{
- pv->deinterlaced_frames++;
+ pv->blended++;
}
- else if( is_combed == 2 )
+ else if (mode != 0)
{
- pv->blended_frames++;
+ pv->deinterlaced++;
}
else
{
- pv->unfiltered_frames++;
+ pv->unfiltered++;
}
+ pv->frames++;
- if( is_combed == 1 && ( pv->mode & MODE_EEDI2 ) )
+ if (mode & MODE_DECOMB_EEDI2)
{
/* Generate an EEDI2 interpolation */
eedi2_planer( pv );
}
- pv->is_combed = is_combed;
- if( is_combed )
+ if (mode != 0)
{
- if( ( pv->mode & MODE_EEDI2 ) && !( pv->mode & MODE_YADIF ) && is_combed == 1 )
+ if ((mode & MODE_DECOMB_EEDI2 ) && !(mode & MODE_DECOMB_YADIF))
{
// Just pass through the EEDI2 interpolation
int pp;
@@ -1954,7 +909,7 @@ static void yadif_filter( hb_filter_private_t * pv,
pv->yadif_arguments[segment].parity = parity;
pv->yadif_arguments[segment].tff = tff;
pv->yadif_arguments[segment].dst = dst;
- pv->yadif_arguments[segment].is_combed = is_combed;
+ pv->yadif_arguments[segment].mode = mode;
}
/*
@@ -1970,7 +925,7 @@ static void yadif_filter( hb_filter_private_t * pv,
else
{
/* Just passing through... */
- pv->yadif_arguments[0].is_combed = is_combed; // 0
+ pv->yadif_arguments[0].mode = mode; // 0
hb_buffer_copy(dst, pv->ref[1]);
}
}
@@ -1980,35 +935,25 @@ static int hb_decomb_init( hb_filter_object_t * filter,
{
filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) );
hb_filter_private_t * pv = filter->private_data;
-
- build_gamma_lut( pv );
-
- pv->deinterlaced_frames = 0;
- pv->blended_frames = 0;
- pv->unfiltered_frames = 0;
-
- pv->yadif_ready = 0;
-
- pv->mode = MODE_YADIF | MODE_BLEND | MODE_CUBIC |
- MODE_GAMMA | MODE_FILTER;
- pv->filter_mode = FILTER_ERODE_DILATE;
- pv->spatial_metric = 2;
- pv->motion_threshold = 3;
- pv->spatial_threshold = 3;
- pv->block_threshold = 40;
- pv->block_width = 16;
- pv->block_height = 16;
-
- pv->magnitude_threshold = 10;
- pv->variance_threshold = 20;
- pv->laplacian_threshold = 20;
- pv->dilation_threshold = 4;
- pv->erosion_threshold = 2;
- pv->noise_threshold = 50;
+ hb_buffer_list_clear(&pv->out_list);
+
+ pv->deinterlaced = 0;
+ pv->blended = 0;
+ pv->unfiltered = 0;
+ pv->frames = 0;
+ pv->yadif_ready = 0;
+
+ pv->mode = MODE_DECOMB_YADIF | MODE_DECOMB_BLEND |
+ MODE_DECOMB_CUBIC;
+ pv->magnitude_threshold = 10;
+ pv->variance_threshold = 20;
+ pv->laplacian_threshold = 20;
+ pv->dilation_threshold = 4;
+ pv->erosion_threshold = 2;
+ pv->noise_threshold = 50;
pv->maximum_search_distance = 24;
- pv->post_processing = 1;
-
- pv->parity = PARITY_DEFAULT;
+ pv->post_processing = 1;
+ pv->parity = PARITY_DEFAULT;
if (filter->settings)
{
@@ -2016,20 +961,10 @@ static int hb_decomb_init( hb_filter_object_t * filter,
// Get comb detection settings
hb_dict_extract_int(&pv->mode, dict, "mode");
- hb_dict_extract_int(&pv->spatial_metric, dict, "spatial-metric");
- hb_dict_extract_int(&pv->motion_threshold, dict,
- "motion-thresh");
- hb_dict_extract_int(&pv->spatial_threshold, dict,
- "spatial-thresh");
- hb_dict_extract_int(&pv->filter_mode, dict, "filter-mode");
- hb_dict_extract_int(&pv->block_threshold, dict,
- "block-thresh");
- hb_dict_extract_int(&pv->block_width, dict, "block-width");
- hb_dict_extract_int(&pv->block_height, dict, "block-height");
// Get deinterlace settings
hb_dict_extract_int(&pv->parity, dict, "parity");
- if (pv->mode & MODE_EEDI2)
+ if (pv->mode & MODE_DECOMB_EEDI2)
{
hb_dict_extract_int(&pv->magnitude_threshold, dict,
"magnitude-thresh");
@@ -2060,19 +995,8 @@ static int hb_decomb_init( hb_filter_object_t * filter,
pv->segment_height[1] = hb_image_height(init->pix_fmt, pv->segment_height[0], 1);
pv->segment_height[2] = hb_image_height(init->pix_fmt, pv->segment_height[0], 2);
- /* Allocate buffers to store comb masks. */
- pv->mask = hb_frame_buffer_init(init->pix_fmt,
- init->geometry.width, init->geometry.height);
- pv->mask_filtered = hb_frame_buffer_init(init->pix_fmt,
- init->geometry.width, init->geometry.height);
- pv->mask_temp = hb_frame_buffer_init(init->pix_fmt,
- init->geometry.width, init->geometry.height);
- memset(pv->mask->data, 0, pv->mask->size);
- memset(pv->mask_filtered->data, 0, pv->mask_filtered->size);
- memset(pv->mask_temp->data, 0, pv->mask_temp->size);
-
int ii;
- if( pv->mode & MODE_EEDI2 )
+ if( pv->mode & MODE_DECOMB_EEDI2 )
{
/* Allocate half-height eedi2 buffers */
for( ii = 0; ii < 4; ii++ )
@@ -2141,276 +1065,7 @@ static int hb_decomb_init( hb_filter_object_t * filter,
yadif_prev_thread_args = thread_args;
}
- /*
- * Create comb detection taskset.
- */
- if( taskset_init( &pv->decomb_filter_taskset, pv->cpu_count,
- sizeof( decomb_thread_arg_t ) ) == 0 )
- {
- hb_error( "decomb could not initialize taskset" );
- }
-
- decomb_thread_arg_t *decomb_prev_thread_args = NULL;
- for( ii = 0; ii < pv->cpu_count; ii++ )
- {
- decomb_thread_arg_t *thread_args;
-
- thread_args = taskset_thread_args( &pv->decomb_filter_taskset, ii );
- thread_args->pv = pv;
- thread_args->segment = ii;
-
- int pp;
- for (pp = 0; pp < 3; pp++)
- {
- if (decomb_prev_thread_args != NULL)
- {
- thread_args->segment_start[pp] =
- decomb_prev_thread_args->segment_start[pp] +
- decomb_prev_thread_args->segment_height[pp];
- }
- if( ii == pv->cpu_count - 1 )
- {
- /*
- * Final segment
- */
- thread_args->segment_height[pp] =
- hb_image_height(init->pix_fmt, init->geometry.height, pp) -
- thread_args->segment_start[pp];
- } else {
- thread_args->segment_height[pp] = pv->segment_height[pp];
- }
- }
-
- if( taskset_thread_spawn( &pv->decomb_filter_taskset, ii,
- "decomb_filter_segment",
- decomb_filter_thread,
- HB_NORMAL_PRIORITY ) == 0 )
- {
- hb_error( "decomb could not spawn thread" );
- }
-
- decomb_prev_thread_args = thread_args;
- }
-
- pv->comb_check_nthreads = init->geometry.height / pv->block_height;
-
- if (pv->comb_check_nthreads > pv->cpu_count)
- pv->comb_check_nthreads = pv->cpu_count;
-
- pv->block_score = calloc(pv->comb_check_nthreads, sizeof(int));
-
- /*
- * Create comb check taskset.
- */
- if( taskset_init( &pv->decomb_check_taskset, pv->comb_check_nthreads,
- sizeof( decomb_thread_arg_t ) ) == 0 )
- {
- hb_error( "decomb check could not initialize taskset" );
- }
-
- decomb_prev_thread_args = NULL;
- for( ii = 0; ii < pv->comb_check_nthreads; ii++ )
- {
- decomb_thread_arg_t *thread_args;
-
- thread_args = taskset_thread_args( &pv->decomb_check_taskset, ii );
- thread_args->pv = pv;
- thread_args->segment = ii;
-
- int pp;
- for (pp = 0; pp < 3; pp++)
- {
- if (decomb_prev_thread_args != NULL)
- {
- thread_args->segment_start[pp] =
- decomb_prev_thread_args->segment_start[pp] +
- decomb_prev_thread_args->segment_height[pp];
- }
-
- // Make segment hight a multiple of block_height
- int h = hb_image_height(init->pix_fmt, init->geometry.height, pp) / pv->comb_check_nthreads;
- h = h / pv->block_height * pv->block_height;
- if (h == 0)
- h = pv->block_height;
-
- if (ii == pv->comb_check_nthreads - 1)
- {
- /*
- * Final segment
- */
- thread_args->segment_height[pp] =
- hb_image_height(init->pix_fmt, init->geometry.height, pp) -
- thread_args->segment_start[pp];
- } else {
- thread_args->segment_height[pp] = h;
- }
- }
-
- if( taskset_thread_spawn( &pv->decomb_check_taskset, ii,
- "decomb_check_segment",
- decomb_check_thread,
- HB_NORMAL_PRIORITY ) == 0 )
- {
- hb_error( "decomb check could not spawn thread" );
- }
-
- decomb_prev_thread_args = thread_args;
- }
-
- if( pv->mode & MODE_FILTER )
- {
- if( taskset_init( &pv->mask_filter_taskset, pv->cpu_count,
- sizeof( decomb_thread_arg_t ) ) == 0 )
- {
- hb_error( "maske filter could not initialize taskset" );
- }
-
- decomb_prev_thread_args = NULL;
- for( ii = 0; ii < pv->cpu_count; ii++ )
- {
- decomb_thread_arg_t *thread_args;
-
- thread_args = taskset_thread_args( &pv->mask_filter_taskset, ii );
- thread_args->pv = pv;
- thread_args->segment = ii;
-
- int pp;
- for (pp = 0; pp < 3; pp++)
- {
- if (decomb_prev_thread_args != NULL)
- {
- thread_args->segment_start[pp] =
- decomb_prev_thread_args->segment_start[pp] +
- decomb_prev_thread_args->segment_height[pp];
- }
-
- if( ii == pv->cpu_count - 1 )
- {
- /*
- * Final segment
- */
- thread_args->segment_height[pp] =
- hb_image_height(init->pix_fmt, init->geometry.height, pp) -
- thread_args->segment_start[pp];
- } else {
- thread_args->segment_height[pp] = pv->segment_height[pp];
- }
- }
-
- if( taskset_thread_spawn( &pv->mask_filter_taskset, ii,
- "mask_filter_segment",
- mask_filter_thread,
- HB_NORMAL_PRIORITY ) == 0 )
- {
- hb_error( "mask filter could not spawn thread" );
- }
-
- decomb_prev_thread_args = thread_args;
- }
-
- if( pv->filter_mode == FILTER_ERODE_DILATE )
- {
- if( taskset_init( &pv->mask_erode_taskset, pv->cpu_count,
- sizeof( decomb_thread_arg_t ) ) == 0 )
- {
- hb_error( "mask erode could not initialize taskset" );
- }
-
- decomb_prev_thread_args = NULL;
- for( ii = 0; ii < pv->cpu_count; ii++ )
- {
- decomb_thread_arg_t *thread_args;
-
- thread_args = taskset_thread_args( &pv->mask_erode_taskset, ii );
- thread_args->pv = pv;
- thread_args->segment = ii;
-
- int pp;
- for (pp = 0; pp < 3; pp++)
- {
- if (decomb_prev_thread_args != NULL)
- {
- thread_args->segment_start[pp] =
- decomb_prev_thread_args->segment_start[pp] +
- decomb_prev_thread_args->segment_height[pp];
- }
-
- if( ii == pv->cpu_count - 1 )
- {
- /*
- * Final segment
- */
- thread_args->segment_height[pp] =
- hb_image_height(init->pix_fmt, init->geometry.height, pp) -
- thread_args->segment_start[pp];
- } else {
- thread_args->segment_height[pp] = pv->segment_height[pp];
- }
- }
-
- if( taskset_thread_spawn( &pv->mask_erode_taskset, ii,
- "mask_erode_segment",
- mask_erode_thread,
- HB_NORMAL_PRIORITY ) == 0 )
- {
- hb_error( "mask erode could not spawn thread" );
- }
-
- decomb_prev_thread_args = thread_args;
- }
-
- if( taskset_init( &pv->mask_dilate_taskset, pv->cpu_count,
- sizeof( decomb_thread_arg_t ) ) == 0 )
- {
- hb_error( "mask dilate could not initialize taskset" );
- }
-
- decomb_prev_thread_args = NULL;
- for( ii = 0; ii < pv->cpu_count; ii++ )
- {
- decomb_thread_arg_t *thread_args;
-
- thread_args = taskset_thread_args( &pv->mask_dilate_taskset, ii );
- thread_args->pv = pv;
- thread_args->segment = ii;
-
- int pp;
- for (pp = 0; pp < 3; pp++)
- {
- if (decomb_prev_thread_args != NULL)
- {
- thread_args->segment_start[pp] =
- decomb_prev_thread_args->segment_start[pp] +
- decomb_prev_thread_args->segment_height[pp];
- }
-
- if( ii == pv->cpu_count - 1 )
- {
- /*
- * Final segment
- */
- thread_args->segment_height[pp] =
- hb_image_height(init->pix_fmt, init->geometry.height, pp) -
- thread_args->segment_start[pp];
- } else {
- thread_args->segment_height[pp] = pv->segment_height[pp];
- }
- }
-
- if( taskset_thread_spawn( &pv->mask_dilate_taskset, ii,
- "mask_dilate_segment",
- mask_dilate_thread,
- HB_NORMAL_PRIORITY ) == 0 )
- {
- hb_error( "mask dilate could not spawn thread" );
- }
-
- decomb_prev_thread_args = thread_args;
- }
- }
- }
-
- if( pv->mode & MODE_EEDI2 )
+ if( pv->mode & MODE_DECOMB_EEDI2 )
{
/*
* Create eedi2 taskset.
@@ -2477,28 +1132,16 @@ static void hb_decomb_close( hb_filter_object_t * filter )
return;
}
- hb_log("decomb: deinterlaced %i | blended %i | unfiltered %i | total %i", pv->deinterlaced_frames, pv->blended_frames, pv->unfiltered_frames, pv->deinterlaced_frames + pv->blended_frames + pv->unfiltered_frames);
+ hb_log("decomb: deinterlaced %i | blended %i | unfiltered %i | total %i",
+ pv->deinterlaced, pv->blended, pv->unfiltered, pv->frames);
taskset_fini( &pv->yadif_taskset );
- taskset_fini( &pv->decomb_filter_taskset );
- taskset_fini( &pv->decomb_check_taskset );
- if( pv->mode & MODE_FILTER )
- {
- taskset_fini( &pv->mask_filter_taskset );
- if( pv->filter_mode == FILTER_ERODE_DILATE )
- {
- taskset_fini( &pv->mask_erode_taskset );
- taskset_fini( &pv->mask_dilate_taskset );
- }
- }
-
- if( pv->mode & MODE_EEDI2 )
+ if( pv->mode & MODE_DECOMB_EEDI2 )
{
taskset_fini( &pv->eedi2_taskset );
}
-
/* Cleanup reference buffers. */
int ii;
for (ii = 0; ii < 3; ii++)
@@ -2506,12 +1149,7 @@ static void hb_decomb_close( hb_filter_object_t * filter )
hb_buffer_close(&pv->ref[ii]);
}
- /* Cleanup combing masks. */
- hb_buffer_close(&pv->mask);
- hb_buffer_close(&pv->mask_filtered);
- hb_buffer_close(&pv->mask_temp);
-
- if( pv->mode & MODE_EEDI2 )
+ if( pv->mode & MODE_DECOMB_EEDI2 )
{
/* Cleanup eedi-half buffers */
int ii;
@@ -2527,7 +1165,7 @@ static void hb_decomb_close( hb_filter_object_t * filter )
}
}
- if( pv->post_processing > 1 && ( pv->mode & MODE_EEDI2 ) )
+ if( pv->post_processing > 1 && ( pv->mode & MODE_DECOMB_EEDI2 ) )
{
if (pv->cx2) eedi2_aligned_free(pv->cx2);
if (pv->cy2) eedi2_aligned_free(pv->cy2);
@@ -2535,8 +1173,6 @@ static void hb_decomb_close( hb_filter_object_t * filter )
if (pv->tmpc) eedi2_aligned_free(pv->tmpc);
}
- free(pv->block_score);
-
/*
* free memory for yadif structs
*/
@@ -2568,26 +1204,92 @@ static void fill_stride(hb_buffer_t * buf)
}
}
+static void process_frame( hb_filter_private_t * pv )
+{
+ if ((pv->mode & MODE_DECOMB_SELECTIVE) &&
+ pv->ref[1]->s.combed == HB_COMB_NONE)
+ {
+ // Input buffer is not combed. Just make a dup of it.
+ hb_buffer_t * buf = hb_buffer_dup(pv->ref[1]);
+ hb_buffer_list_append(&pv->out_list, buf);
+ pv->frames++;
+ pv->unfiltered++;
+ }
+ else
+ {
+ /* Determine if top-field first layout */
+ int tff;
+ if (pv->parity < 0)
+ {
+ tff = !!(pv->ref[1]->s.flags & PIC_FLAG_TOP_FIELD_FIRST);
+ }
+ else
+ {
+ tff = (pv->parity & 1) ^ 1;
+ }
+
+ /* deinterlace both fields if bob */
+ int frame, num_frames = 1;
+ if (pv->mode & MODE_DECOMB_BOB)
+ {
+ num_frames = 2;
+ }
+
+ // Will need up to 2 buffers simultaneously
+
+ /* Perform yadif filtering */
+ for (frame = 0; frame < num_frames; frame++)
+ {
+ hb_buffer_t * buf;
+ int parity = frame ^ tff ^ 1;
+
+ // tff for eedi2
+ pv->tff = !parity;
+
+ buf = hb_frame_buffer_init(pv->ref[1]->f.fmt,
+ pv->ref[1]->f.width,
+ pv->ref[1]->f.height);
+ yadif_filter(pv, buf, parity, tff);
+
+ /* Copy buffered settings to output buffer settings */
+ buf->s = pv->ref[1]->s;
+
+ hb_buffer_list_append(&pv->out_list, buf);
+ }
+
+ /* if this frame was deinterlaced and bob mode is engaged, halve
+ the duration of the saved timestamps. */
+ if (pv->mode & MODE_DECOMB_BOB)
+ {
+ hb_buffer_t *first = hb_buffer_list_head(&pv->out_list);
+ hb_buffer_t *second = hb_buffer_list_tail(&pv->out_list);
+ first->s.stop -= (first->s.stop - first->s.start) / 2LL;
+ second->s.start = first->s.stop;
+ second->s.new_chap = 0;
+ }
+ }
+}
+
static int hb_decomb_work( hb_filter_object_t * filter,
hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out )
{
hb_filter_private_t * pv = filter->private_data;
hb_buffer_t * in = *buf_in;
- hb_buffer_list_t list;
- hb_buffer_list_clear(&list);
+ // Input buffer is always consumed.
+ *buf_in = NULL;
if (in->s.flags & HB_BUF_FLAG_EOF)
{
- *buf_out = in;
- *buf_in = NULL;
+ // Duplicate last frame and process refs
+ store_ref(pv, hb_buffer_dup(pv->ref[2]));
+ process_frame(pv);
+ hb_buffer_list_append(&pv->out_list, in);
+ *buf_out = hb_buffer_list_clear(&pv->out_list);
return HB_FILTER_DONE;
}
- /* Store current frame in yadif cache */
- *buf_in = NULL;
fill_stride(in);
- store_ref(pv, in);
// yadif requires 3 buffers, prev, cur, and next. For the first
// frame, there can be no prev, so we duplicate the first frame.
@@ -2595,106 +1297,16 @@ static int hb_decomb_work( hb_filter_object_t * filter,
{
// If yadif is not ready, store another ref and return HB_FILTER_DELAY
store_ref(pv, hb_buffer_dup(in));
+ store_ref(pv, in);
pv->yadif_ready = 1;
// Wait for next
return HB_FILTER_DELAY;
}
- /* Determine if top-field first layout */
- int tff;
- if( pv->parity < 0 )
- {
- tff = !!(in->s.flags & PIC_FLAG_TOP_FIELD_FIRST);
- }
- else
- {
- tff = (pv->parity & 1) ^ 1;
- }
-
- /* deinterlace both fields if bob */
- int frame, num_frames = 1;
- if (pv->mode & MODE_BOB)
- {
- num_frames = 2;
- }
-
- // Will need up to 2 buffers simultaneously
- int idx = 0;
- hb_buffer_t * o_buf[2] = {NULL,};
-
- /* Perform yadif filtering */
- for( frame = 0; frame < num_frames; frame++ )
- {
- int parity = frame ^ tff ^ 1;
-
- /* Skip the second run if the frame is uncombed */
- if (frame && pv->is_combed == 0)
- {
- break;
- }
-
- // tff for eedi2
- pv->tff = !parity;
-
- if (o_buf[idx] == NULL)
- {
- o_buf[idx] = hb_video_buffer_init(in->f.width, in->f.height);
- }
-
- if (frame)
- pv->skip_comb_check = 1;
- else
- pv->skip_comb_check = 0;
-
- yadif_filter(pv, o_buf[idx], parity, tff);
-
- // If bob, add all frames to output
- // else, if not combed, add frame to output
- // else if final iteration, add frame to output
- if ((pv->mode & MODE_BOB) ||
- pv->is_combed == 0 ||
- frame == num_frames - 1)
- {
- /* Copy buffered settings to output buffer settings */
- o_buf[idx]->s = pv->ref[1]->s;
-
- o_buf[idx]->next = NULL;
- hb_buffer_list_append(&list, o_buf[idx]);
-
- // Indicate that buffer was consumed
- o_buf[idx] = NULL;
-
- idx ^= 1;
-
- if ((pv->mode & MODE_MASK) && pv->spatial_metric >= 0 )
- {
- if (pv->mode == MODE_MASK ||
- ((pv->mode & MODE_MASK) &&
- (pv->mode & MODE_FILTER)) ||
- ((pv->mode & MODE_MASK) &&
- (pv->mode & MODE_GAMMA)) ||
- pv->is_combed)
- {
- apply_mask(pv, hb_buffer_list_tail(&list));
- }
- }
- }
- }
- hb_buffer_close(&o_buf[0]);
- hb_buffer_close(&o_buf[1]);
-
- /* if this frame was deinterlaced and bob mode is engaged, halve
- the duration of the saved timestamps. */
- if ((pv->mode & MODE_BOB) && pv->is_combed)
- {
- hb_buffer_t *first = hb_buffer_list_head(&list);
- hb_buffer_t *second = hb_buffer_list_tail(&list);
- first->s.stop -= (first->s.stop - first->s.start) / 2LL;
- second->s.start = first->s.stop;
- second->s.new_chap = 0;
- }
+ store_ref(pv, in);
+ process_frame(pv);
- *buf_out = hb_buffer_list_clear(&list);
+ *buf_out = hb_buffer_list_clear(&pv->out_list);
return HB_FILTER_OK;
}
diff --git a/libhb/decomb.h b/libhb/decomb.h
new file mode 100644
index 000000000..473dd3f7f
--- /dev/null
+++ b/libhb/decomb.h
@@ -0,0 +1,25 @@
+/* decomb.h
+
+ Copyright (c) 2003-2016 HandBrake Team
+ This file is part of the HandBrake source code
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License v2.
+ For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
+ */
+
+#ifndef HB_DECOMB_H
+#define HB_DECOMB_H
+
+#define MODE_DECOMB_YADIF 1 // Use yadif
+#define MODE_DECOMB_BLEND 2 // Use blending interpolation
+#define MODE_DECOMB_CUBIC 4 // Use cubic interpolation
+#define MODE_DECOMB_EEDI2 8 // Use EEDI2 interpolation
+#define MODE_DECOMB_BOB 16 // Deinterlace each field to a separate frame
+#define MODE_DECOMB_SELECTIVE 32 // Selectively deinterlace based on comb detection
+
+#define MODE_YADIF_ENABLE 1
+#define MODE_YADIF_SPATIAL 2
+#define MODE_YADIF_BOB 4
+#define MODE_DEINTERLACE_QSV 8
+
+#endif // HB_DECOMB_H
diff --git a/libhb/hb.c b/libhb/hb.c
index 5937ae1ba..393ced341 100644
--- a/libhb/hb.c
+++ b/libhb/hb.c
@@ -1807,6 +1807,7 @@ int hb_global_init()
hb_error("hb_qsv_info_init failed!");
return -1;
}
+ hb_param_configure_qsv();
#endif
/* libavcodec */
diff --git a/libhb/internal.h b/libhb/internal.h
index 9ad22507f..7c70a2bb7 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -465,6 +465,7 @@ enum
};
extern hb_filter_object_t hb_filter_detelecine;
+extern hb_filter_object_t hb_filter_comb_detect;
extern hb_filter_object_t hb_filter_decomb;
extern hb_filter_object_t hb_filter_deinterlace;
extern hb_filter_object_t hb_filter_vfr;
diff --git a/libhb/libhb_presets.list b/libhb/libhb_presets.list
index cb46ba084..0009d8674 100644
--- a/libhb/libhb_presets.list
+++ b/libhb/libhb_presets.list
@@ -1,6 +1,6 @@
<resources>
<section name="PresetTemplate">
- <integer name="VersionMajor" value="12" />
+ <integer name="VersionMajor" value="13" />
<integer name="VersionMinor" value="0" />
<integer name="VersionMicro" value="0" />
<json name="Preset" file="preset_template.json" />
diff --git a/libhb/param.c b/libhb/param.c
index 350f81773..6b3e0b16f 100644
--- a/libhb/param.c
+++ b/libhb/param.c
@@ -12,6 +12,9 @@
#include "param.h"
#include "common.h"
#include "colormap.h"
+#ifdef USE_QSV
+#include "qsv_common.h"
+#endif
#include <regex.h>
static hb_filter_param_t nlmeans_presets[] =
@@ -75,18 +78,32 @@ static hb_filter_param_t detelecine_presets[] =
{ 0, NULL, NULL, NULL }
};
+static hb_filter_param_t comb_detect_presets[] =
+{
+ { 0, "Off", "off", "disable=1" },
+ { 1, "Custom", "custom", NULL },
+ { 2, "Default", "default",
+ "mode=3:spatial-metric=2:motion-thresh=1:spatial-thresh=1:"
+ "filter-mode=2:block-thresh=40:block-width=16:block-height=16"
+ },
+ { 3, "Less Sensitive", "permissive",
+ "mode=3:spatial-metric=2:motion-thresh=3:spatial-thresh=3:"
+ "filter-mode=2:block-thresh=40:block-width=16:block-height=16"
+ },
+ { 4, "Fast", "fast",
+ "mode=0:spatial-metric=2:motion-thresh=2:spatial-thresh=3:"
+ "filter-mode=1:block-thresh=80:block-width=16:block-height=16"
+ },
+ { 0, NULL, NULL, NULL }
+};
+
static hb_filter_param_t decomb_presets[] =
{
{ 1, "Custom", "custom", NULL },
- { 2, "Default", "default",
- "mode=391:spatial-metric=2:motion-thresh=3:spatial-thresh=3:"
- "filter-mode=2:block-thresh=40"
- },
- { 3, "Fast", "fast",
- "mode=7:motion-thresh=6:spatial-thresh=9:"
- "filter-mode=1:block-thresh=80"
- },
- { 4, "Bob", "bob", "mode=455" },
+ { 2, "Default", "default", "mode=7" },
+ { 4, "Bob", "bob", "mode=23" },
+ { 3, "EEDI2", "eedi2", "mode=15" },
+ { 4, "EEDI2 Bob", "eedi2bob", "mode=31" },
{ 0, NULL, NULL, NULL }
};
@@ -96,10 +113,14 @@ static hb_filter_param_t deinterlace_presets[] =
{ 3, "Default", "default", "mode=3" },
{ 2, "Skip Spatial Check", "skip-spatial", "mode=1" },
{ 5, "Bob", "bob", "mode=7" },
+#ifdef USE_QSV
+ { 6, "QSV", "qsv", "mode=11" },
+#endif
{ 0, NULL, NULL, NULL },
{ 2, "Fast", "fast", "mode=1" },
{ 3, "Slow", "slow", "mode=1" },
- { 4, "Slower", "slower", "mode=3" }
+ { 4, "Slower", "slower", "mode=3" },
+ { 7, "QSV", "qsv", "mode=3" }
};
typedef struct
@@ -121,6 +142,9 @@ static filter_param_map_t param_map[] =
{ HB_FILTER_DETELECINE, detelecine_presets, NULL,
sizeof(detelecine_presets) / sizeof(hb_filter_param_t) },
+ { HB_FILTER_COMB_DETECT, comb_detect_presets, NULL,
+ sizeof(decomb_presets) / sizeof(hb_filter_param_t) },
+
{ HB_FILTER_DECOMB, decomb_presets, NULL,
sizeof(decomb_presets) / sizeof(hb_filter_param_t) },
@@ -130,6 +154,16 @@ static filter_param_map_t param_map[] =
{ HB_FILTER_INVALID, NULL, NULL, 0 }
};
+void hb_param_configure_qsv(void)
+{
+#ifdef USE_QSV
+ if (!hb_qsv_available())
+ {
+ memset(&deinterlace_presets[4], 0, sizeof(hb_filter_param_t));
+ }
+#endif
+}
+
/* NL-means presets and tunes
*
* Presets adjust strength:
@@ -552,6 +586,7 @@ hb_generate_filter_settings(int filter_id, const char *preset, const char *tune,
case HB_FILTER_NLMEANS:
settings = generate_nlmeans_settings(preset, tune, custom);
break;
+ case HB_FILTER_COMB_DETECT:
case HB_FILTER_DECOMB:
case HB_FILTER_DETELECINE:
case HB_FILTER_HQDN3D:
diff --git a/libhb/param.h b/libhb/param.h
index d61fd389c..0222de39b 100644
--- a/libhb/param.h
+++ b/libhb/param.h
@@ -19,6 +19,8 @@ struct hb_filter_param_s
const char *settings;
};
+void hb_param_configure_qsv(void);
+
hb_dict_t * hb_generate_filter_settings(int filter_id, const char *preset,
const char *tune, const char *custom);
char * hb_generate_filter_settings_json(int filter_id, const char *preset,
diff --git a/libhb/preset.c b/libhb/preset.c
index 9b5f0616a..d32e4d1e8 100644
--- a/libhb/preset.c
+++ b/libhb/preset.c
@@ -1114,6 +1114,34 @@ int hb_preset_apply_filters(const hb_dict_t *preset, hb_dict_t *job_dict)
}
}
+ const char *comb_preset;
+ comb_preset = hb_value_get_string(hb_dict_get(preset,
+ "PictureCombDetectPreset"));
+ if (comb_preset != NULL)
+ {
+ const char *comb_custom;
+ comb_custom = hb_value_get_string(hb_dict_get(preset,
+ "PictureCombDetectCustom"));
+ filter_settings = hb_generate_filter_settings(HB_FILTER_COMB_DETECT,
+ comb_preset, NULL, comb_custom);
+ if (filter_settings == NULL)
+ {
+ hb_error("Invalid comb detect filter preset (%s)", comb_preset);
+ return -1;
+ }
+ else if (!hb_dict_get_bool(filter_settings, "disable"))
+ {
+ filter_dict = hb_dict_init();
+ hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_COMB_DETECT));
+ hb_dict_set(filter_dict, "Settings", filter_settings);
+ hb_add_filter2(filter_list, filter_dict);
+ }
+ else
+ {
+ hb_value_free(&filter_settings);
+ }
+ }
+
// Decomb or deinterlace filters
const char *deint_filter, *deint_preset, *deint_custom;
deint_filter = hb_value_get_string(hb_dict_get(preset,
@@ -1146,7 +1174,7 @@ int hb_preset_apply_filters(const hb_dict_t *preset, hb_dict_t *job_dict)
hb_error("Invalid deinterlace filter preset (%s)", deint_preset);
return -1;
}
- else if (!hb_dict_get_bool(filter_settings, "disable"))
+ if (!hb_dict_get_bool(filter_settings, "disable"))
{
filter_dict = hb_dict_init();
hb_dict_set(filter_dict, "ID", hb_value_int(filter_id));
@@ -2088,6 +2116,148 @@ static void import_filters_11_1_0(hb_value_t *preset)
"PictureRotate");
}
+// Split the old decomb filter into separate comb detection
+// and decomb filters.
+static void import_deint_12_0_0(hb_value_t *preset)
+{
+ hb_value_t *val = hb_dict_get(preset, "PictureDeinterlaceFilter");
+ if (val == NULL)
+ {
+ return;
+ }
+ const char * deint = hb_value_get_string(val);
+ if (deint == NULL)
+ {
+ // This really shouldn't happen for a valid preset
+ return;
+ }
+ if (strcasecmp(deint, "decomb"))
+ {
+ return;
+ }
+ val = hb_dict_get(preset, "PictureDeinterlacePreset");
+ if (val == NULL)
+ {
+ hb_dict_set(preset, "PictureDeinterlacePreset",
+ hb_value_string("default"));
+ return;
+ }
+ deint = hb_value_get_string(val);
+ if (deint == NULL)
+ {
+ // This really shouldn't happen for a valid preset
+ return;
+ }
+ if (!strcasecmp(deint, "fast"))
+ {
+ // fast -> PictureCombDetectPreset fast
+ // PictureDeinterlacePreset default
+ hb_dict_set(preset, "PictureCombDetectPreset",
+ hb_value_string("fast"));
+ hb_dict_set(preset, "PictureDeinterlacePreset",
+ hb_value_string("default"));
+ return;
+ }
+ else if (!strcasecmp(deint, "bob") || !strcasecmp(deint, "default"))
+ {
+ hb_dict_set(preset, "PictureCombDetectPreset",
+ hb_value_string("default"));
+ return;
+ }
+ else if (strcasecmp(deint, "custom"))
+ {
+ // not custom -> default
+ hb_dict_set(preset, "PictureCombDetectPreset",
+ hb_value_string("default"));
+ hb_dict_set(preset, "PictureDeinterlacePreset",
+ hb_value_string("default"));
+ return;
+ }
+ val = hb_dict_get(preset, "PictureDeinterlaceCustom");
+ if (val == NULL)
+ {
+ hb_dict_set(preset, "PictureDeinterlacePreset",
+ hb_value_string("default"));
+ return;
+ }
+ // Translate custom values
+ deint = hb_value_get_string(val);
+ if (deint == NULL)
+ {
+ // This really shouldn't happen for a valid preset
+ return;
+ }
+
+ hb_dict_t * dict;
+ dict = hb_parse_filter_settings(deint);
+
+ int yadif, blend, cubic, eedi2, mask, bob, gamma, filter, composite;
+ int detect_mode, decomb_mode;
+
+ int mode = 7, spatial_metric = 2, motion_threshold = 3;
+ int spatial_threshold = 3, filter_mode = 2;
+ int block_threshold = 40, block_width = 16, block_height = 16;
+ int magnitude_threshold = 10, variance_threshold = 20;
+ int laplacian_threshold = 20;
+ int dilation_threshold = 4, erosion_threshold = 2, noise_threshold = 50;
+ int maximum_search_distance = 24, post_processing = 1, parity = -1;
+
+ hb_dict_extract_int(&mode, dict, "mode");
+ hb_dict_extract_int(&spatial_metric, dict, "spatial-metric");
+ hb_dict_extract_int(&motion_threshold, dict, "motion-thresh");
+ hb_dict_extract_int(&spatial_threshold, dict, "spatial-thresh");
+ hb_dict_extract_int(&filter_mode, dict, "filter-mode");
+ hb_dict_extract_int(&block_threshold, dict, "block-thresh");
+ hb_dict_extract_int(&block_width, dict, "block-width");
+ hb_dict_extract_int(&block_height, dict, "block-height");
+ hb_dict_extract_int(&magnitude_threshold, dict, "magnitude-thresh");
+ hb_dict_extract_int(&variance_threshold, dict, "variance-thresh");
+ hb_dict_extract_int(&laplacian_threshold, dict, "laplacian-thresh");
+ hb_dict_extract_int(&dilation_threshold, dict, "dilation-thresh");
+ hb_dict_extract_int(&erosion_threshold, dict, "erosion-thresh");
+ hb_dict_extract_int(&noise_threshold, dict, "noise-thresh");
+ hb_dict_extract_int(&maximum_search_distance, dict, "search-distance");
+ hb_dict_extract_int(&post_processing, dict, "postproc");
+ hb_dict_extract_int(&parity, dict, "parity");
+ hb_value_free(&dict);
+
+ yadif = !!(mode & 1);
+ blend = !!(mode & 2);
+ cubic = !!(mode & 4);
+ eedi2 = !!(mode & 8);
+ mask = !!(mode & 32);
+ bob = !!(mode & 64);
+ gamma = !!(mode & 128);
+ filter = !!(mode & 256);
+ composite = !!(mode & 512);
+
+ detect_mode = gamma + filter * 2 + mask * 4 + composite * 8;
+ decomb_mode = yadif + blend * 2 + cubic * 4 + eedi2 * 8 + bob * 16;
+
+ char * custom = hb_strdup_printf("mode=%d:spatial-metric=%d:"
+ "motion-thresh=%d:spatial-thresh=%d:"
+ "filter-mode=%d:block-thresh=%d:"
+ "block-width=%d:block-height=%d",
+ detect_mode, spatial_metric,
+ motion_threshold, spatial_threshold,
+ filter_mode, block_threshold,
+ block_width, block_height);
+ hb_dict_set(preset, "PictureCombDetectCustom", hb_value_string(custom));
+ free(custom);
+
+ custom = hb_strdup_printf("mode=%d:magnitude-thresh=%d:variance-thresh=%d:"
+ "laplacian-thresh=%d:dilation-thresh=%d:"
+ "erosion-thresh=%d:noise-thresh=%d:"
+ "search-distance=%d:postproc=%d:parity=%d",
+ decomb_mode, magnitude_threshold,
+ variance_threshold, laplacian_threshold,
+ dilation_threshold, erosion_threshold,
+ noise_threshold, maximum_search_distance,
+ post_processing, parity);
+ hb_dict_set(preset, "PictureDeinterlaceCustom", hb_value_string(custom));
+ free(custom);
+}
+
static void import_deint_11_0_0(hb_value_t *preset)
{
hb_value_t *val = hb_dict_get(preset, "PictureDeinterlaceFilter");
@@ -2453,9 +2623,17 @@ static void import_video_0_0_0(hb_value_t *preset)
}
}
+static void import_12_0_0(hb_value_t *preset)
+{
+ import_deint_12_0_0(preset);
+}
+
static void import_11_1_0(hb_value_t *preset)
{
import_filters_11_1_0(preset);
+
+ // Import next...
+ import_12_0_0(preset);
}
static void import_11_0_0(hb_value_t *preset)
@@ -2514,6 +2692,11 @@ static int preset_import(hb_value_t *preset, int major, int minor, int micro)
import_11_1_0(preset);
result = 1;
}
+ else if (major == 12 && minor == 0 && micro == 0)
+ {
+ import_12_0_0(preset);
+ result = 1;
+ }
preset_clean(preset, hb_preset_template);
}
return result;
diff --git a/libhb/preset_builtin.json b/libhb/preset_builtin.json
index 16cc2bdaa..fcd70f638 100644
--- a/libhb/preset_builtin.json
+++ b/libhb/preset_builtin.json
@@ -402,9 +402,10 @@
"PictureAutoCrop": true,
"PictureBottomCrop": 0,
"PictureDeblock": 0,
+ "PictureCombDetectPreset": "fast",
"PictureDeinterlaceCustom": "",
"PictureDeinterlaceFilter": "decomb",
- "PictureDeinterlacePreset": "fast",
+ "PictureDeinterlacePreset": "default",
"PictureDenoiseCustom": "",
"PictureDenoiseFilter": "off",
"PictureDetelecine": "off",
@@ -707,6 +708,7 @@
"PictureAutoCrop": true,
"PictureBottomCrop": 0,
"PictureDeblock": 0,
+ "PictureCombDetectPreset": "default",
"PictureDeinterlaceCustom": "",
"PictureDeinterlaceFilter": "decomb",
"PictureDeinterlacePreset": "default",
diff --git a/libhb/preset_template.json b/libhb/preset_template.json
index da113f88a..a66e1e2e5 100644
--- a/libhb/preset_template.json
+++ b/libhb/preset_template.json
@@ -47,6 +47,8 @@
"PictureDARWidth": 0,
"PictureDeblock": 0,
"PictureDeblockCustom": "qp=0:mode=2",
+ "PictureCombDetectCustom": "",
+ "PictureCombDetectPreset": "off",
"PictureDeinterlaceCustom": "",
"PictureDeinterlaceFilter": "off",
"PictureDeinterlacePreset": "default",
diff --git a/libhb/work.c b/libhb/work.c
index 7a1460eec..4fca293fb 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -11,6 +11,7 @@
#include "libavformat/avformat.h"
#include "openclwrapper.h"
#include "opencl.h"
+#include "decomb.h"
#ifdef USE_QSV
#include "qsv_common.h"
@@ -1166,12 +1167,13 @@ static int sanitize_qsv( hb_job_t * job )
// CPU-based deinterlace (validated)
case HB_FILTER_DEINTERLACE:
- if (filter->settings != NULL &&
- strcasecmp(filter->settings, "qsv") != 0)
+ {
+ int mode = hb_dict_get_int(filter->settings, "mode");
+ if (!(mode & MODE_DEINTERLACE_QSV))
{
encode_only = 1;
}
- break;
+ } break;
// other filters will be removed
default:
@@ -1216,19 +1218,19 @@ static int sanitize_qsv( hb_job_t * job )
{
// cropping and scaling always done via VPP filter
case HB_FILTER_CROP_SCALE:
- if (filter->settings == NULL || *filter->settings == 0)
- {
- // VPP defaults were set above, so not a problem
- // however, this should never happen, print an error
- hb_error("do_job: '%s': no settings!", filter->name);
- }
- else
- {
- sscanf(filter->settings, "%d:%d:%d:%d:%d:%d",
- &vpp_settings[0], &vpp_settings[1],
- &vpp_settings[2], &vpp_settings[3],
- &vpp_settings[4], &vpp_settings[5]);
- }
+ hb_dict_extract_int(&vpp_settings[0], filter->settings,
+ "width");
+ hb_dict_extract_int(&vpp_settings[1], filter->settings,
+ "height");
+ hb_dict_extract_int(&vpp_settings[2], filter->settings,
+ "crop-top");
+ hb_dict_extract_int(&vpp_settings[3], filter->settings,
+ "crop-bottom");
+ hb_dict_extract_int(&vpp_settings[4], filter->settings,
+ "crop-left");
+ hb_dict_extract_int(&vpp_settings[5], filter->settings,
+ "crop-right");
+
// VPP crop/scale takes precedence over OpenCL scale too
if (job->use_opencl)
{
@@ -1241,8 +1243,9 @@ static int sanitize_qsv( hb_job_t * job )
// pick VPP or CPU deinterlace depending on settings
case HB_FILTER_DEINTERLACE:
- if (filter->settings == NULL ||
- strcasecmp(filter->settings, "qsv") == 0)
+ {
+ int mode = hb_dict_get_int(filter->settings, "mode");
+ if (mode & MODE_DEINTERLACE_QSV)
{
// deinterlacing via VPP filter
vpp_settings[6] = 1;
@@ -1254,7 +1257,7 @@ static int sanitize_qsv( hb_job_t * job )
// validated
num_cpu_filters++;
}
- break;
+ } break;
// then, validated filters
case HB_FILTER_ROTATE: // TODO: use Media SDK for this
@@ -1309,6 +1312,31 @@ static int sanitize_qsv( hb_job_t * job )
return 0;
}
+static void sanitize_filter_list(hb_list_t *list)
+{
+ // Add selective deinterlacing mode if comb detection is enabled
+ if (hb_filter_find(list, HB_FILTER_COMB_DETECT) != NULL)
+ {
+ int selective[] = {HB_FILTER_DECOMB, HB_FILTER_DEINTERLACE};
+ int ii, count = sizeof(selective) / sizeof(int);
+
+ for (ii = 0; ii < count; ii++)
+ {
+ hb_filter_object_t * filter = hb_filter_find(list, selective[ii]);
+ if (filter != NULL)
+ {
+ int mode = hb_dict_get_int(filter->settings, "mode");
+ mode |= MODE_DECOMB_SELECTIVE;
+ hb_dict_set(filter->settings, "mode", hb_value_int(mode));
+ break;
+ }
+ }
+ }
+
+ // Combine HB_FILTER_AVFILTERs that are sequential
+ hb_avfilter_combine(list);
+}
+
/**
* Job initialization rountine.
*
@@ -1416,8 +1444,7 @@ static void do_job(hb_job_t *job)
{
hb_filter_init_t init;
- // Combine HB_FILTER_AVFILTERs that are sequential
- hb_avfilter_combine(job->list_filter);
+ sanitize_filter_list(job->list_filter);
memset(&init, 0, sizeof(init));
init.job = job;
diff --git a/macosx/English.lproj/HBPictureViewController.xib b/macosx/English.lproj/HBPictureViewController.xib
index 1f6315a0d..9d12a5738 100644
--- a/macosx/English.lproj/HBPictureViewController.xib
+++ b/macosx/English.lproj/HBPictureViewController.xib
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9531" systemVersion="15E27e" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10115" systemVersion="15E61b" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment identifier="macosx"/>
<development version="6300" identifier="xcode"/>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10115"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="HBPictureViewController">
@@ -1151,6 +1151,79 @@ Frames that are not interlaced will suffer some quality degradation.</string>
<binding destination="-2" name="value" keyPath="self.filters.flip" id="xdh-96-5s5"/>
</connections>
</button>
+ <textField verticalHuggingPriority="750" id="Mg1-Yq-F9S">
+ <rect key="frame" x="200" y="52" width="108" height="14"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="right" title="Interlace Detection:" id="xHD-vC-ePQ">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <connections>
+ <binding destination="-2" name="textColor" keyPath="self.labelColor" id="eKb-Bh-OkE"/>
+ </connections>
+ </textField>
+ <textField verticalHuggingPriority="750" id="RZE-gp-SB7">
+ <rect key="frame" x="237" y="26" width="71" height="14"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="right" title="Custom:" id="Da7-pY-5vu">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <connections>
+ <binding destination="-2" name="hidden" keyPath="self.filters.customCombDetectionSelected" id="x5q-SE-Xds">
+ <dictionary key="options">
+ <string key="NSValueTransformerName">NSNegateBoolean</string>
+ </dictionary>
+ </binding>
+ <binding destination="-2" name="textColor" keyPath="self.labelColor" id="s78-d3-GZY"/>
+ </connections>
+ </textField>
+ <popUpButton verticalHuggingPriority="750" id="IQG-Nn-HTb">
+ <rect key="frame" x="311" y="47" width="114" height="22"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <string key="toolTip">This filter detects interlaced frames.
+If a deinterlace filter is enabled, only frames that this filter finds to be interlaced will be deinterlaced.</string>
+ <popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" controlSize="small" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" inset="2" arrowPosition="arrowAtCenter" preferredEdge="maxY" id="nfb-CJ-1J3">
+ <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="smallSystem"/>
+ <menu key="menu" title="OtherViews" id="Qz4-EY-GFO"/>
+ </popUpButtonCell>
+ <connections>
+ <accessibilityConnection property="title" destination="Mg1-Yq-F9S" id="nhI-oN-u5h"/>
+ <binding destination="-2" name="contentValues" keyPath="self.filters.combDetectionSettings" id="lKn-uj-nGl"/>
+ <binding destination="-2" name="selectedValue" keyPath="self.filters.combDetection" previousBinding="lKn-uj-nGl" id="XIZ-dC-cLu">
+ <dictionary key="options">
+ <string key="NSValueTransformerName">HBCombDetectionTransformer</string>
+ </dictionary>
+ </binding>
+ <binding destination="-2" name="enabled" keyPath="self.filters" id="mJ8-zq-tQ8">
+ <dictionary key="options">
+ <string key="NSValueTransformerName">NSIsNotNil</string>
+ </dictionary>
+ </binding>
+ <outlet property="nextKeyView" destination="rPg-F2-gtl" id="46r-ZD-dTe"/>
+ </connections>
+ </popUpButton>
+ <textField verticalHuggingPriority="750" id="rPg-F2-gtl">
+ <rect key="frame" x="314" y="23" width="108" height="19"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="4YG-Q6-1tM">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <accessibility description="Custom interlace detection settings."/>
+ <connections>
+ <binding destination="-2" name="hidden" keyPath="self.filters.customCombDetectionSelected" id="avq-Zl-5gA">
+ <dictionary key="options">
+ <string key="NSValueTransformerName">NSNegateBoolean</string>
+ </dictionary>
+ </binding>
+ <binding destination="-2" name="value" keyPath="self.filters.combDetectionCustomString" id="pTK-PZ-3ZE"/>
+ </connections>
+ </textField>
</subviews>
<point key="canvasLocation" x="403.5" y="86.5"/>
</customView>
diff --git a/macosx/HBFilters+UIAdditions.h b/macosx/HBFilters+UIAdditions.h
index 71ca839bc..6600e7930 100644
--- a/macosx/HBFilters+UIAdditions.h
+++ b/macosx/HBFilters+UIAdditions.h
@@ -14,6 +14,8 @@
*/
+ (NSDictionary *)detelecinePresetsDict;
++ (NSDictionary *)combDetectionPresetsDict;
+
+ (NSDictionary *)deinterlaceTypesDict;
+ (NSDictionary *)decombPresetsDict;
+ (NSDictionary *)deinterlacePresetsDict;
@@ -24,6 +26,8 @@
- (BOOL)customDetelecineSelected;
+@property (nonatomic, readonly) BOOL customCombDetectionSelected;
+
- (BOOL)deinterlaceEnabled;
- (BOOL)customDeinterlaceSelected;
@@ -35,6 +39,8 @@
@property (nonatomic, readonly) NSArray *detelecineSettings;
+@property (nonatomic, readonly) NSArray *combDetectionSettings;
+
@property (nonatomic, readonly) NSArray *deinterlaceTypes;
@property (nonatomic, readonly) NSArray *deinterlacePresets;
@@ -61,6 +67,9 @@
@interface HBDetelecineTransformer : HBGenericDictionaryTransformer
@end
+@interface HBCombDetectionTransformer : HBGenericDictionaryTransformer
+@end
+
@interface HBDeinterlaceTransformer : HBGenericDictionaryTransformer
@end
diff --git a/macosx/HBFilters+UIAdditions.m b/macosx/HBFilters+UIAdditions.m
index 62fcda372..8141881cc 100644
--- a/macosx/HBFilters+UIAdditions.m
+++ b/macosx/HBFilters+UIAdditions.m
@@ -79,6 +79,18 @@ static NSDictionary * filterParamsToNamesDict(hb_filter_param_t * (f)(int), int
@end
+@implementation HBCombDetectionTransformer
+
+- (instancetype)init
+{
+ if (self = [super init])
+ self.dict = [HBFilters combDetectionPresetsDict];
+
+ return self;
+}
+
+@end
+
@implementation HBDeinterlaceTransformer
- (instancetype)init
@@ -167,6 +179,8 @@ static NSDictionary * filterParamsToNamesDict(hb_filter_param_t * (f)(int), int
static NSDictionary *detelecinePresetsDict = nil;
+static NSDictionary *combDetectionPresetsDict = nil;
+
static NSDictionary *deinterlaceTypesDict = nil;
static NSDictionary *decombPresetsDict = nil;
static NSDictionary *deinterlacePresetsDict = nil;
@@ -188,6 +202,16 @@ static NSDictionary *denoiseTypesDict = nil;
return detelecinePresetsDict;
}
++ (NSDictionary *)combDetectionPresetsDict
+{
+ if (!combDetectionPresetsDict)
+ {
+ combDetectionPresetsDict = filterParamsToNamesDict(hb_filter_param_get_presets, HB_FILTER_COMB_DETECT);
+ }
+ return combDetectionPresetsDict;
+}
+
+
+ (NSDictionary *)deinterlaceTypesDict
{
if (!deinterlaceTypesDict)
@@ -256,6 +280,11 @@ static NSDictionary *denoiseTypesDict = nil;
return filterParamsToNamesArray(hb_filter_param_get_presets, HB_FILTER_DETELECINE);
}
+- (NSArray *)combDetectionSettings
+{
+ return filterParamsToNamesArray(hb_filter_param_get_presets, HB_FILTER_COMB_DETECT);
+}
+
- (NSArray *)deinterlacePresets
{
if ([self.deinterlace isEqualToString:@"deinterlace"])
@@ -288,6 +317,11 @@ static NSDictionary *denoiseTypesDict = nil;
return [self.detelecine isEqualToString:@"custom"] ? YES : NO;
}
+- (BOOL)customCombDetectionSelected
+{
+ return [self.combDetection isEqualToString:@"custom"] ? YES : NO;
+}
+
- (BOOL)customDeinterlaceSelected
{
return [self.deinterlacePreset isEqualToString:@"custom"] && [self deinterlaceEnabled];
diff --git a/macosx/HBFilters.h b/macosx/HBFilters.h
index e173e3630..37416697d 100644
--- a/macosx/HBFilters.h
+++ b/macosx/HBFilters.h
@@ -19,6 +19,9 @@ extern NSString * const HBFiltersChangedNotification;
@property (nonatomic, readwrite, copy) NSString *detelecine;
@property (nonatomic, readwrite, copy) NSString *detelecineCustomString;
+@property (nonatomic, readwrite, copy) NSString *combDetection;
+@property (nonatomic, readwrite, copy) NSString *combDetectionCustomString;
+
@property (nonatomic, readwrite, copy) NSString *deinterlace;
@property (nonatomic, readwrite, copy) NSString *deinterlacePreset;
@property (nonatomic, readwrite, copy) NSString *deinterlaceCustomString;
diff --git a/macosx/HBFilters.m b/macosx/HBFilters.m
index 7df571b8d..910c4186d 100644
--- a/macosx/HBFilters.m
+++ b/macosx/HBFilters.m
@@ -28,6 +28,8 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification";
{
_detelecine = @"off";
_detelecineCustomString = @"";
+ _combDetection = @"off";
+ _combDetectionCustomString = @"";
_deinterlace = @"off";
_deinterlaceCustomString = @"";
_deinterlacePreset = @"default";
@@ -89,6 +91,41 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification";
[self postChangedNotification];
}
+- (void)setCombDetection:(NSString *)combDetection
+{
+ if (![combDetection isEqualToString:_combDetection])
+ {
+ [[self.undo prepareWithInvocationTarget:self] setCombDetection:_combDetection];
+ }
+ if (combDetection)
+ {
+ _combDetection = [combDetection copy];
+ }
+ else
+ {
+ _combDetection = @"off";
+ }
+ [self postChangedNotification];
+}
+
+- (void)setCombDetectionCustomString:(NSString *)combDetectionCustomString
+{
+ if (![combDetectionCustomString isEqualToString:_combDetectionCustomString])
+ {
+ [[self.undo prepareWithInvocationTarget:self] setCombDetectionCustomString:_combDetectionCustomString];
+ }
+ if (combDetectionCustomString)
+ {
+ _combDetectionCustomString = [combDetectionCustomString copy];
+ }
+ else
+ {
+ _combDetectionCustomString = @"";
+ }
+
+ [self postChangedNotification];
+}
+
- (void)setDeinterlace:(NSString *)deinterlace
{
if (![deinterlace isEqualToString:_deinterlace])
@@ -286,8 +323,12 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification";
{
retval = [NSSet setWithObjects:@"detelecine", nil];
}
+ else if ([key isEqualToString:@"customCombDetectionSelected"])
+ {
+ retval = [NSSet setWithObjects:@"combDetection", nil];
+ }
else if ([key isEqualToString:@"denoiseTunesAvailable"] ||
- [key isEqualToString:@"customDenoiseSelected"])
+ [key isEqualToString:@"customDenoiseSelected"])
{
retval = [NSSet setWithObjects:@"denoise", @"denoisePreset", nil];
}
@@ -300,7 +341,7 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification";
retval = [NSSet setWithObject:@"deinterlace"];
}
else if ([key isEqualToString:@"customDeinterlaceSelected"] ||
- [key isEqualToString:@"deinterlacePresets"])
+ [key isEqualToString:@"deinterlacePresets"])
{
retval = [NSSet setWithObjects:@"deinterlace", @"deinterlacePreset", nil];
}
@@ -328,6 +369,9 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification";
copy->_detelecine = [_detelecine copy];
copy->_detelecineCustomString = [_detelecineCustomString copy];
+ copy->_combDetection = [_combDetection copy];
+ copy->_combDetectionCustomString = [_combDetectionCustomString copy];
+
copy->_deinterlace = [_deinterlace copy];
copy->_deinterlacePreset = [_deinterlacePreset copy];
copy->_deinterlaceCustomString = [_deinterlaceCustomString copy];
@@ -360,6 +404,9 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification";
encodeObject(_detelecine);
encodeObject(_detelecineCustomString);
+ encodeObject(_combDetection);
+ encodeObject(_combDetectionCustomString);
+
encodeObject(_deinterlace);
encodeObject(_deinterlacePreset);
encodeObject(_deinterlaceCustomString);
@@ -382,6 +429,9 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification";
decodeObject(_detelecine, NSString);
decodeObject(_detelecineCustomString, NSString);
+ decodeObject(_combDetection, NSString);
+ decodeObject(_combDetectionCustomString, NSString);
+
decodeObject(_deinterlace, NSString);
decodeObject(_deinterlacePreset, NSString)
decodeObject(_deinterlaceCustomString, NSString);
@@ -409,6 +459,9 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification";
preset[@"PictureDeinterlacePreset"] = self.deinterlacePreset;
preset[@"PictureDeinterlaceCustom"] = self.deinterlaceCustomString;
+ preset[@"PictureCombDetectPreset"] = self.combDetection;
+ preset[@"PictureCombDetectCustom"] = self.combDetectionCustomString;
+
preset[@"PictureDetelecine"] = self.detelecine;
preset[@"PictureDetelecineCustom"] = self.detelecineCustomString;
@@ -434,6 +487,10 @@ NSString * const HBFiltersChangedNotification = @"HBFiltersChangedNotification";
self.deinterlacePreset = preset[@"PictureDeinterlacePreset"];
self.deinterlaceCustomString = preset[@"PictureDeinterlaceCustom"];
+ // Comb detection
+ self.combDetection = preset[@"PictureCombDetectPreset"];
+ self.combDetectionCustomString = preset[@"PictureCombDetectCustom"];
+
// Detelecine
self.detelecine = preset[@"PictureDetelecine"];
self.detelecineCustomString = preset[@"PictureDetelecineCustom"];
diff --git a/macosx/HBJob+HBJobConversion.m b/macosx/HBJob+HBJobConversion.m
index aa109971f..8914b34d4 100644
--- a/macosx/HBJob+HBJobConversion.m
+++ b/macosx/HBJob+HBJobConversion.m
@@ -411,6 +411,19 @@
hb_value_free(&filter_dict);
}
+ // Comb Detection
+ if (![self.filters.combDetection isEqualToString:@"off"])
+ {
+ int filter_id = HB_FILTER_COMB_DETECT;
+ hb_dict_t *filter_dict = hb_generate_filter_settings(filter_id,
+ self.filters.combDetection.UTF8String,
+ NULL,
+ self.filters.combDetectionCustomString.UTF8String);
+ filter = hb_filter_init(filter_id);
+ hb_add_filter_dict(job, filter, filter_dict);
+ hb_value_free(&filter_dict);
+ }
+
// Deinterlace
if (![self.filters.deinterlace isEqualToString:@"off"])
{
diff --git a/test/test.c b/test/test.c
index ca4062424..dc53a3e0c 100644
--- a/test/test.c
+++ b/test/test.c
@@ -46,6 +46,7 @@
#define DEINTERLACE_DEFAULT_PRESET "default"
#define DECOMB_DEFAULT_PRESET "default"
#define DETELECINE_DEFAULT_PRESET "default"
+#define COMB_DETECT_DEFAULT_PRESET "default"
#define HQDN3D_DEFAULT_PRESET "medium"
#define ROTATE_DEFAULT "angle=180:hflip=0"
#define DEBLOCK_DEFAULT "qp=5"
@@ -80,6 +81,9 @@ static char * nlmeans_tune = NULL;
static int detelecine_disable = 0;
static int detelecine_custom = 0;
static char * detelecine = NULL;
+static int comb_detect_disable = 0;
+static int comb_detect_custom = 0;
+static char * comb_detect = NULL;
static int decomb_disable = 0;
static int decomb_custom = 0;
static char * decomb = NULL;
@@ -912,13 +916,6 @@ static void showFilterPresets(FILE* const out, int filter_id)
char * slash = "", * newline;
int ii, count = 0, linelen = 0;
-#ifdef USE_QSV
-if (filter_id == HB_FILTER_DEINTERLACE && hb_qsv_available())
-{
- count = 1;
-}
-#endif
-
// Count number of entries we want to display
for (ii = 0; names[ii] != NULL; ii++)
{
@@ -956,12 +953,7 @@ if (filter_id == HB_FILTER_DEINTERLACE && hb_qsv_available())
linelen += len;
slash = "/";
}
-#ifdef USE_QSV
-if (filter_id == HB_FILTER_DEINTERLACE && hb_qsv_available())
-{
- fprintf(out, "/qsv");
-}
-#endif
+
fprintf(out, ">\n");
hb_str_vfree(names);
}
@@ -1018,6 +1010,9 @@ static void showFilterDefault(FILE* const out, int filter_id)
case HB_FILTER_HQDN3D:
preset = HQDN3D_DEFAULT_PRESET;
break;
+ case HB_FILTER_COMB_DETECT:
+ preset = COMB_DETECT_DEFAULT_PRESET;
+ break;
default:
break;
}
@@ -1028,6 +1023,7 @@ static void showFilterDefault(FILE* const out, int filter_id)
case HB_FILTER_DECOMB:
case HB_FILTER_DETELECINE:
case HB_FILTER_HQDN3D:
+ case HB_FILTER_COMB_DETECT:
{
hb_dict_t * settings;
settings = hb_generate_filter_settings(filter_id, preset,
@@ -1434,13 +1430,27 @@ static void ShowHelp()
" (default: detected from source)\n"
"\n"
"### Filters---------------------------------------------------------------\n\n"
-" -d, --deinterlace Unconditionally deinterlaces all frames\n");
+" --comb-detect Detect interlace artifacts in frames.\n"
+" If not accompanied by the decomb or deinterlace\n"
+" filters, this filter only logs the interlaced\n"
+" frame count to the activity log.\n"
+" If accompanied by the decomb or deinterlace\n"
+" filters, it causes these filters to selectively\n"
+" deinterlace only those frames where interlacing\n"
+" is detected.\n");
+ showFilterPresets(out, HB_FILTER_COMB_DETECT);
+ showFilterKeys(out, HB_FILTER_COMB_DETECT);
+ showFilterDefault(out, HB_FILTER_COMB_DETECT);
+ fprintf( out,
+" --no-comb-detect Disable preset comb-detect filter\n"
+" -d, --deinterlace Deinterlaces using libav yadif.\n");
showFilterPresets(out, HB_FILTER_DEINTERLACE);
showFilterKeys(out, HB_FILTER_DEINTERLACE);
showFilterDefault(out, HB_FILTER_DEINTERLACE);
fprintf( out,
" --no-deinterlace Disable preset deinterlace filter\n"
-" -5, --decomb Selectively deinterlaces when it detects combing\n");
+" -5, --decomb Deinterlaces using a combination of yadif,\n"
+" blend, cubic, or EEDI2 interpolation.\n");
showFilterPresets(out, HB_FILTER_DECOMB);
showFilterKeys(out, HB_FILTER_DECOMB);
showFilterDefault(out, HB_FILTER_DECOMB);
@@ -1806,6 +1816,7 @@ static int ParseOptions( int argc, char ** argv )
#define VERSION 307
#define DESCRIBE 308
#define PAD 309
+ #define FILTER_COMB_DETECT 310
for( ;; )
{
@@ -1885,6 +1896,8 @@ static int ParseOptions( int argc, char ** argv )
{ "nlmeans-tune",required_argument, NULL, FILTER_NLMEANS_TUNE },
{ "detelecine", optional_argument, NULL, '9' },
{ "no-detelecine", no_argument, &detelecine_disable, 1 },
+ { "no-comb-detect", no_argument, &comb_detect_disable, 1 },
+ { "comb-detect", optional_argument, NULL, FILTER_COMB_DETECT },
{ "decomb", optional_argument, NULL, '5' },
{ "no-decomb", no_argument, &decomb_disable, 1 },
{ "grayscale", no_argument, NULL, 'g' },
@@ -2313,6 +2326,17 @@ static int ParseOptions( int argc, char ** argv )
detelecine = strdup(DETELECINE_DEFAULT_PRESET);
}
break;
+ case FILTER_COMB_DETECT:
+ free(comb_detect);
+ if (optarg != NULL)
+ {
+ comb_detect = strdup(optarg);
+ }
+ else
+ {
+ comb_detect = strdup(COMB_DETECT_DEFAULT_PRESET);
+ }
+ break;
case '5':
free(decomb);
if (optarg != NULL)
@@ -2672,6 +2696,32 @@ static int ParseOptions( int argc, char ** argv )
}
}
+ if (comb_detect != NULL)
+ {
+ if (comb_detect_disable)
+ {
+ fprintf(stderr,
+ "Incompatible options --comb-detect and --no-comb-detect\n");
+ return -1;
+ }
+ if (!hb_validate_filter_preset(HB_FILTER_COMB_DETECT, comb_detect,
+ NULL, NULL))
+ {
+ // Nothing to do, but must validate preset before
+ // attempting to validate custom settings to prevent potential
+ // false positive
+ }
+ else if (!hb_validate_filter_string(HB_FILTER_COMB_DETECT, comb_detect))
+ {
+ comb_detect_custom = 1;
+ }
+ else
+ {
+ fprintf(stderr, "Invalid comb-detect option %s\n", comb_detect);
+ return -1;
+ }
+ }
+
if (decomb != NULL)
{
if (decomb_disable)
@@ -3523,6 +3573,25 @@ static hb_dict_t * PreparePreset(const char *preset_name)
{
hb_dict_set(preset, "PictureDeinterlaceFilter", hb_value_string("off"));
}
+ if (comb_detect_disable)
+ {
+ hb_dict_set(preset, "PictureCombDetectFilter", hb_value_string("off"));
+ }
+ if (comb_detect != NULL)
+ {
+ if (!comb_detect_custom)
+ {
+ hb_dict_set(preset, "PictureCombDetectPreset",
+ hb_value_string(comb_detect));
+ }
+ else
+ {
+ hb_dict_set(preset, "PictureCombDetectPreset",
+ hb_value_string("custom"));
+ hb_dict_set(preset, "PictureCombDetectCustom",
+ hb_value_string(comb_detect));
+ }
+ }
if (deinterlace != NULL)
{
hb_dict_set(preset, "PictureDeinterlaceFilter",
diff --git a/win/CS/HandBrake.ApplicationServices/HandBrake.ApplicationServices.csproj b/win/CS/HandBrake.ApplicationServices/HandBrake.ApplicationServices.csproj
index 60f7a7b9d..c6fddcfb7 100644
--- a/win/CS/HandBrake.ApplicationServices/HandBrake.ApplicationServices.csproj
+++ b/win/CS/HandBrake.ApplicationServices/HandBrake.ApplicationServices.csproj
@@ -137,6 +137,7 @@
<Compile Include="Interop\Json\Encode\SubtitleTrack.cs" />
<Compile Include="Interop\Json\Encode\Video.cs" />
<Compile Include="Interop\Factories\AnamorphicFactory.cs" />
+ <Compile Include="Interop\Model\Encoding\CombDetect.cs" />
<Compile Include="Interop\Model\Encoding\DeinterlaceFilter.cs" />
<Compile Include="Interop\Model\Encoding\HBPresetTune.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
diff --git a/win/CS/HandBrake.ApplicationServices/Interop/HbLib/hb_filter_ids.cs b/win/CS/HandBrake.ApplicationServices/Interop/HbLib/hb_filter_ids.cs
index d4164ede6..76059fe8b 100644
--- a/win/CS/HandBrake.ApplicationServices/Interop/HbLib/hb_filter_ids.cs
+++ b/win/CS/HandBrake.ApplicationServices/Interop/HbLib/hb_filter_ids.cs
@@ -9,23 +9,36 @@ namespace HandBrake.ApplicationServices.Interop.HbLib
{
public enum hb_filter_ids
{
- HB_FILTER_QSV_PRE = 1, // for QSV - important to have before other filters
+ HB_FILTER_INVALID = 0,
+ // for QSV - important to have before other filters
+ HB_FILTER_FIRST = 1,
+ HB_FILTER_QSV_PRE = 1,
+
// First, filters that may change the framerate (drop or dup frames)
HB_FILTER_DETELECINE,
+ HB_FILTER_COMB_DETECT,
HB_FILTER_DECOMB,
HB_FILTER_DEINTERLACE,
HB_FILTER_VFR,
// Filters that must operate on the original source image are next
HB_FILTER_DEBLOCK,
- HB_FILTER_HQDN3D,
+ HB_FILTER_DENOISE,
+ HB_FILTER_HQDN3D = HB_FILTER_DENOISE,
HB_FILTER_NLMEANS,
HB_FILTER_RENDER_SUB,
HB_FILTER_CROP_SCALE,
- // Finally filters that don't care what order they are in,
- // except that they must be after the above filters
HB_FILTER_ROTATE,
HB_FILTER_GRAYSCALE,
- HB_FILTER_QSV_POST, // for QSV - important to have as a last one
- HB_FILTER_QSV, // default MSDK VPP filter
+ HB_FILTER_PAD,
+
+ // Finally filters that don't care what order they are in,
+ // except that they must be after the above filters
+ HB_FILTER_AVFILTER,
+
+ // for QSV - important to have as a last one
+ HB_FILTER_QSV_POST,
+ // default MSDK VPP filter
+ HB_FILTER_QSV,
+ HB_FILTER_LAST = HB_FILTER_QSV
}
}
diff --git a/win/CS/HandBrake.ApplicationServices/Interop/Json/Presets/HBPreset.cs b/win/CS/HandBrake.ApplicationServices/Interop/Json/Presets/HBPreset.cs
index 260b55960..7e1e94697 100644
--- a/win/CS/HandBrake.ApplicationServices/Interop/Json/Presets/HBPreset.cs
+++ b/win/CS/HandBrake.ApplicationServices/Interop/Json/Presets/HBPreset.cs
@@ -127,6 +127,16 @@ namespace HandBrake.ApplicationServices.Interop.Json.Presets
public string PictureDeinterlaceFilter { get; set; }
/// <summary>
+ /// Gets or sets the picture comb detect preset.
+ /// </summary>
+ public string PictureCombDetectPreset { get; set; }
+
+ /// <summary>
+ /// Gets or sets the picture comb detect custom.
+ /// </summary>
+ public string PictureCombDetectCustom { get; set; }
+
+ /// <summary>
/// Gets or sets the picture deinterlace preset.
/// </summary>
public string PictureDeinterlacePreset { get; set; }
diff --git a/win/CS/HandBrake.ApplicationServices/Interop/Model/Encoding/CombDetect.cs b/win/CS/HandBrake.ApplicationServices/Interop/Model/Encoding/CombDetect.cs
new file mode 100644
index 000000000..d98d0d2ec
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Interop/Model/Encoding/CombDetect.cs
@@ -0,0 +1,34 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="CombDetect.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// Defines the CombDetect type.
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrake.ApplicationServices.Interop.Model.Encoding
+{
+ using HandBrake.ApplicationServices.Attributes;
+
+ /// <summary>
+ /// The CombDetect Type.
+ /// </summary>
+ public enum CombDetect
+ {
+ [ShortName("off")]
+ Off,
+
+ [ShortName("custom")]
+ Custom,
+
+ [ShortName("default")]
+ Default,
+
+ [ShortName("permissive")]
+ LessSensitive,
+
+ [ShortName("fast")]
+ Fast
+ }
+}
diff --git a/win/CS/HandBrake.ApplicationServices/Interop/Model/Encoding/Decomb.cs b/win/CS/HandBrake.ApplicationServices/Interop/Model/Encoding/Decomb.cs
index eab94ef2a..a2b544029 100644
--- a/win/CS/HandBrake.ApplicationServices/Interop/Model/Encoding/Decomb.cs
+++ b/win/CS/HandBrake.ApplicationServices/Interop/Model/Encoding/Decomb.cs
@@ -19,13 +19,16 @@ namespace HandBrake.ApplicationServices.Interop.Model.Encoding
[ShortName("default")]
Default,
- [ShortName("fast")]
- Fast,
-
[ShortName("bob")]
Bob,
[ShortName("custom")]
- Custom
+ Custom,
+
+ [ShortName("eedi2")]
+ EEDI2,
+
+ [ShortName("eedi2bob")]
+ EEDI2Bob
}
}
diff --git a/win/CS/HandBrakeWPF/Converters/EnumComboConverter.cs b/win/CS/HandBrakeWPF/Converters/EnumComboConverter.cs
index b154c4dce..a25cd1100 100644
--- a/win/CS/HandBrakeWPF/Converters/EnumComboConverter.cs
+++ b/win/CS/HandBrakeWPF/Converters/EnumComboConverter.cs
@@ -15,7 +15,6 @@ namespace HandBrakeWPF.Converters
using System;
using HandBrake.ApplicationServices.Model;
- using HandBrake.ApplicationServices.Utilities;
using HandBrake.ApplicationServices.Interop.Model.Encoding;
using HandBrakeWPF.Services.Queue.Model;
@@ -95,6 +94,10 @@ namespace HandBrakeWPF.Converters
{
return EnumHelper<DeinterlaceFilter>.GetEnumDisplayValues(typeof(DeinterlaceFilter));
}
+ if (value is IEnumerable<CombDetect>)
+ {
+ return EnumHelper<CombDetect>.GetEnumDisplayValues(typeof(CombDetect));
+ }
// Single Items
if (targetType == typeof(VideoEncoder) || value.GetType() == typeof(VideoEncoder))
@@ -145,6 +148,10 @@ namespace HandBrakeWPF.Converters
{
return EnumHelper<DeinterlaceFilter>.GetDisplay((DeinterlaceFilter)value);
}
+ if (targetType == typeof(CombDetect) || value.GetType() == typeof(CombDetect))
+ {
+ return EnumHelper<CombDetect>.GetDisplay((CombDetect)value);
+ }
return null;
}
@@ -215,6 +222,10 @@ namespace HandBrakeWPF.Converters
return EnumHelper<DeinterlaceFilter>.GetValue(value.ToString());
}
+ if (targetType == typeof(CombDetect) || value.GetType() == typeof(CombDetect))
+ {
+ return EnumHelper<CombDetect>.GetValue(value.ToString());
+ }
return null;
}
}
diff --git a/win/CS/HandBrakeWPF/Services/Encode/Factories/EncodeFactory.cs b/win/CS/HandBrakeWPF/Services/Encode/Factories/EncodeFactory.cs
index 6304c4af3..5167e7218 100644
--- a/win/CS/HandBrakeWPF/Services/Encode/Factories/EncodeFactory.cs
+++ b/win/CS/HandBrakeWPF/Services/Encode/Factories/EncodeFactory.cs
@@ -435,6 +435,19 @@ namespace HandBrakeWPF.Services.Encode.Factories
filter.FilterList.Add(filterItem);
}
+ if (job.DeinterlaceFilter == DeinterlaceFilter.Decomb || job.DeinterlaceFilter == DeinterlaceFilter.Yadif)
+ {
+ if (job.CombDetect != CombDetect.Off)
+ {
+ IntPtr settingsPtr = HBFunctions.hb_generate_filter_settings_json((int)hb_filter_ids.HB_FILTER_COMB_DETECT, EnumHelper<CombDetect>.GetShortName(job.CombDetect), null, job.CustomCombDetect);
+ string unparsedJson = Marshal.PtrToStringAnsi(settingsPtr);
+ JToken settings = JObject.Parse(unparsedJson);
+
+ Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_COMB_DETECT, Settings = settings };
+ filter.FilterList.Add(filterItem);
+ }
+ }
+
// Denoise
if (job.Denoise != Denoise.Off)
{
diff --git a/win/CS/HandBrakeWPF/Services/Encode/Model/EncodeTask.cs b/win/CS/HandBrakeWPF/Services/Encode/Model/EncodeTask.cs
index 0ae588bc7..6ea377bc6 100644
--- a/win/CS/HandBrakeWPF/Services/Encode/Model/EncodeTask.cs
+++ b/win/CS/HandBrakeWPF/Services/Encode/Model/EncodeTask.cs
@@ -92,6 +92,8 @@ namespace HandBrakeWPF.Services.Encode.Model
this.CustomDeinterlace = task.CustomDeinterlace;
this.CustomDenoise = task.CustomDenoise;
this.CustomDetelecine = task.CustomDetelecine;
+ this.CustomCombDetect = task.CustomCombDetect;
+ this.CombDetect = task.CombDetect;
this.Deblock = task.Deblock;
this.Decomb = task.Decomb;
this.Deinterlace = task.Deinterlace;
@@ -298,11 +300,21 @@ namespace HandBrakeWPF.Services.Encode.Model
public Decomb Decomb { get; set; }
/// <summary>
+ /// Gets or sets the comb detect.
+ /// </summary>
+ public CombDetect CombDetect { get; set; }
+
+ /// <summary>
/// Gets or sets CustomDecomb.
/// </summary>
public string CustomDecomb { get; set; }
/// <summary>
+ /// Gets or sets the custom comb detect.
+ /// </summary>
+ public string CustomCombDetect { get; set; }
+
+ /// <summary>
/// Gets or sets Detelecine.
/// </summary>
public Detelecine Detelecine { get; set; }
diff --git a/win/CS/HandBrakeWPF/Services/Presets/Factories/JsonPresetFactory.cs b/win/CS/HandBrakeWPF/Services/Presets/Factories/JsonPresetFactory.cs
index a92a67adb..8b323d396 100644
--- a/win/CS/HandBrakeWPF/Services/Presets/Factories/JsonPresetFactory.cs
+++ b/win/CS/HandBrakeWPF/Services/Presets/Factories/JsonPresetFactory.cs
@@ -13,7 +13,6 @@ namespace HandBrakeWPF.Services.Presets.Factories
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
- using System.Windows.Forms.VisualStyles;
using HandBrake.ApplicationServices.Interop.Json.Presets;
using HandBrake.ApplicationServices.Interop.Model;
@@ -132,8 +131,11 @@ namespace HandBrakeWPF.Services.Presets.Factories
case "bob":
preset.Task.Decomb = Decomb.Bob;
break;
- case "fast":
- preset.Task.Decomb = Decomb.Fast;
+ case "eedi2":
+ preset.Task.Decomb = Decomb.EEDI2;
+ break;
+ case "eedi2bob":
+ preset.Task.Decomb = Decomb.EEDI2Bob;
break;
default:
preset.Task.Decomb = Decomb.Default;
@@ -173,9 +175,35 @@ namespace HandBrakeWPF.Services.Presets.Factories
}
}
+ if (preset.Task.DeinterlaceFilter == DeinterlaceFilter.Yadif || preset.Task.DeinterlaceFilter == DeinterlaceFilter.Decomb)
+ {
+ switch (importedPreset.PictureCombDetectPreset)
+ {
+ case "off":
+ preset.Task.CombDetect = CombDetect.Off;
+ break;
+ case "custom":
+ preset.Task.CombDetect = CombDetect.Custom;
+ break;
+ case "default":
+ preset.Task.CombDetect = CombDetect.Default;
+ break;
+ case "permissive":
+ preset.Task.CombDetect = CombDetect.LessSensitive;
+ break;
+ case "fast":
+ preset.Task.CombDetect = CombDetect.Fast;
+ break;
+ default:
+ preset.Task.CombDetect = CombDetect.Off;
+ break;
+ }
+ }
+
preset.Task.CustomDeinterlace = importedPreset.PictureDetelecineCustom;
preset.Task.CustomDenoise = importedPreset.PictureDenoiseCustom;
preset.Task.CustomDetelecine = importedPreset.PictureDetelecineCustom;
+ preset.Task.CustomCombDetect = importedPreset.PictureCombDetectCustom;
switch (importedPreset.PictureDetelecine)
{
@@ -579,6 +607,8 @@ namespace HandBrakeWPF.Services.Presets.Factories
preset.PictureDenoiseTune = EnumHelper<DenoiseTune>.GetShortName(export.Task.DenoiseTune);
preset.PictureDetelecine = EnumHelper<Detelecine>.GetShortName(export.Task.Detelecine);
preset.PictureDetelecineCustom = export.Task.CustomDetelecine;
+ preset.PictureCombDetectPreset = EnumHelper<CombDetect>.GetShortName(export.Task.CombDetect);
+ preset.PictureCombDetectCustom = export.Task.CustomCombDetect;
// Video
preset.VideoEncoder = EnumHelper<VideoEncoder>.GetShortName(export.Task.VideoEncoder);
diff --git a/win/CS/HandBrakeWPF/ViewModels/FiltersViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/FiltersViewModel.cs
index 4dd88f6be..7cb50ac37 100644
--- a/win/CS/HandBrakeWPF/ViewModels/FiltersViewModel.cs
+++ b/win/CS/HandBrakeWPF/ViewModels/FiltersViewModel.cs
@@ -216,6 +216,17 @@ namespace HandBrakeWPF.ViewModels
}
/// <summary>
+ /// Comb Detection Presets
+ /// </summary>
+ public IEnumerable<CombDetect> CombDetectPresets
+ {
+ get
+ {
+ return EnumHelper<CombDetect>.GetEnumList();
+ }
+ }
+
+ /// <summary>
/// Gets or sets a value indicating whether Grayscale.
/// </summary>
public bool Grayscale
@@ -256,6 +267,48 @@ namespace HandBrakeWPF.ViewModels
}
}
+ public CombDetect SelectedCombDetectPreset
+ {
+ get
+ {
+ return this.CurrentTask.CombDetect;
+ }
+
+ set
+ {
+ this.CurrentTask.CombDetect = value;
+ this.NotifyOfPropertyChange(() => this.SelectedCombDetectPreset);
+
+ // Show / Hide the Custom Control
+ this.NotifyOfPropertyChange(() => this.ShowCombDetectCustom);
+ }
+ }
+
+ /// <summary>
+ /// Show the CombDetect Custom Box.
+ /// </summary>
+ public bool ShowCombDetectCustom
+ {
+ get
+ {
+ return this.SelectedCombDetectPreset == CombDetect.Custom;
+ }
+ }
+
+ public string CustomCombDetect
+ {
+ get
+ {
+ return this.CurrentTask.CustomCombDetect;
+ }
+
+ set
+ {
+ this.CurrentTask.CustomCombDetect = value;
+ this.NotifyOfPropertyChange(() => this.CustomCombDetect);
+ }
+ }
+
/// <summary>
/// Gets or sets SelectedDecomb.
/// </summary>
diff --git a/win/CS/HandBrakeWPF/Views/FiltersView.xaml b/win/CS/HandBrakeWPF/Views/FiltersView.xaml
index 494adae51..d40567fcc 100644
--- a/win/CS/HandBrakeWPF/Views/FiltersView.xaml
+++ b/win/CS/HandBrakeWPF/Views/FiltersView.xaml
@@ -10,7 +10,6 @@
<UserControl.Resources>
<Converters:BooleanToVisibilityConverter x:Key="boolToVisConverter" />
<Converters:EnumComboConverter x:Key="boolComboConverter" />
- <Converters:InverseBooleanConverter x:Key="inverseBooleanConverter" />
<filters:DenoisePresetConverter x:Key="DenoisePresetConverter" />
</UserControl.Resources>
@@ -56,31 +55,58 @@
Visibility="{Binding ShowDetelecineCustom, Converter={StaticResource boolToVisConverter}}"/>
<!-- Deinterlace -->
- <TextBlock Text="{x:Static Properties:ResourcesUI.FiltersView_Deinterlace}" Grid.Row="1" Grid.Column="0" Margin="0,0,0,10" />
- <StackPanel Grid.Row="1" Grid.Column="1" >
+ <TextBlock Text="{x:Static Properties:ResourcesUI.FiltersView_Deinterlace}" VerticalAlignment="Center" Grid.Row="1" Grid.Column="0" Margin="0,0,0,10" />
+ <StackPanel Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Margin="0,0,0,10">
<ComboBox Width="120" ItemsSource="{Binding DeinterlaceFilterOptions, Converter={StaticResource boolComboConverter}}" HorizontalAlignment="Left"
- SelectedItem="{Binding SelectedDeinterlaceFilter, Converter={StaticResource boolComboConverter}}" Margin="0,0,0,10" />
+ SelectedItem="{Binding SelectedDeinterlaceFilter, Converter={StaticResource boolComboConverter}}" />
</StackPanel>
- <StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="2">
- <TextBlock Text="Preset:" VerticalAlignment="Center" Margin="0,0,5,10" Visibility="{Binding IsDeinterlaceDecomb, Converter={StaticResource boolToVisConverter}}" />
- <ComboBox Width="120" ItemsSource="{Binding DecombOptions, Converter={StaticResource boolComboConverter}}" HorizontalAlignment="Left" VerticalAlignment="Center"
- SelectedItem="{Binding SelectedDecomb, Converter={StaticResource boolComboConverter}}"
- Visibility="{Binding IsDecombMode, Converter={StaticResource boolToVisConverter}}" Margin="0,0,0,10" />
+ <Grid Grid.Row="1" Grid.Column="2" Margin="0,0,0,10">
+ <Grid.RowDefinitions>
+ <RowDefinition />
+ <RowDefinition />
+ </Grid.RowDefinitions>
+
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition />
+ <ColumnDefinition Width="15"/>
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition />
+ </Grid.ColumnDefinitions>
+
+
+ <TextBlock Text="Preset:" VerticalAlignment="Center" Margin="0,0,5,0" Grid.Column="0" Grid.Row="0" Visibility="{Binding IsDeinterlaceDecomb, Converter={StaticResource boolToVisConverter}}" />
+ <StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal">
+ <ComboBox Width="120" ItemsSource="{Binding DecombOptions, Converter={StaticResource boolComboConverter}}" HorizontalAlignment="Left" VerticalAlignment="Center"
+ SelectedItem="{Binding SelectedDecomb, Converter={StaticResource boolComboConverter}}"
+ Visibility="{Binding IsDecombMode, Converter={StaticResource boolToVisConverter}}" />
+
+ <ComboBox Width="120" ItemsSource="{Binding DeInterlaceOptions, Converter={StaticResource boolComboConverter}}" HorizontalAlignment="Left" VerticalAlignment="Center"
+ SelectedItem="{Binding SelectedDeInterlace, Converter={StaticResource boolComboConverter}}"
+ Visibility="{Binding IsDeinterlaceMode, Converter={StaticResource boolToVisConverter}}" />
+ </StackPanel>
- <ComboBox Width="120" ItemsSource="{Binding DeInterlaceOptions, Converter={StaticResource boolComboConverter}}" HorizontalAlignment="Left" VerticalAlignment="Center"
- SelectedItem="{Binding SelectedDeInterlace, Converter={StaticResource boolComboConverter}}"
- Visibility="{Binding IsDeinterlaceMode, Converter={StaticResource boolToVisConverter}}" Margin="0,0,0,10" />
+ <TextBlock Text="Custom:" VerticalAlignment="Center" Margin="0,0,5,0" Grid.Column="0" Grid.Row="1" Visibility="{Binding ShowDeinterlaceDecombCustom, Converter={StaticResource boolToVisConverter}}" />
+ <StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal">
+ <TextBox Width="120" Text="{Binding CustomDecomb, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" HorizontalAlignment="Left"
+ Visibility="{Binding ShowDecombCustom, Converter={StaticResource boolToVisConverter}}" Margin="0,5,0,0" MinHeight="22" />
- <TextBlock Text="Custom:" VerticalAlignment="Center" Margin="5,0,5,10" Visibility="{Binding ShowDeinterlaceDecombCustom, Converter={StaticResource boolToVisConverter}}" />
- <TextBox Width="120" Text="{Binding CustomDecomb, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" HorizontalAlignment="Left"
- Visibility="{Binding ShowDecombCustom, Converter={StaticResource boolToVisConverter}}" Margin="0,0,0,10" />
+ <TextBox Width="120" Text="{Binding CustomDeinterlace, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" HorizontalAlignment="Left"
+ Visibility="{Binding ShowDeinterlaceCustom, Converter={StaticResource boolToVisConverter}}" Margin="0,5,0,0" MinHeight="22" />
+ </StackPanel>
- <TextBox Width="120" Text="{Binding CustomDeinterlace, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" HorizontalAlignment="Left"
- Visibility="{Binding ShowDeinterlaceCustom, Converter={StaticResource boolToVisConverter}}" Margin="0,0,0,10" />
- </StackPanel>
+ <TextBlock Text="Interlace Detection:" VerticalAlignment="Center" Margin="0,0,5,0" Grid.Column="3" Grid.Row="0" Visibility="{Binding IsDeinterlaceDecomb, Converter={StaticResource boolToVisConverter}}" />
+ <TextBlock Text="Custom:" VerticalAlignment="Center" Margin="0,0,5,0" Grid.Column="3" Grid.Row="1" Visibility="{Binding ShowCombDetectCustom, Converter={StaticResource boolToVisConverter}}" />
+
+ <ComboBox Width="120" Grid.Row="0" Grid.Column="4" ItemsSource="{Binding CombDetectPresets, Converter={StaticResource boolComboConverter}}" HorizontalAlignment="Left" VerticalAlignment="Center"
+ SelectedItem="{Binding SelectedCombDetectPreset, Converter={StaticResource boolComboConverter}}"
+ Visibility="{Binding IsDeinterlaceDecomb, Converter={StaticResource boolToVisConverter}}"/>
+ <TextBox Width="120" Grid.Row="1" Grid.Column="4" Text="{Binding CustomCombDetect, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" HorizontalAlignment="Left"
+ Visibility="{Binding ShowCombDetectCustom, Converter={StaticResource boolToVisConverter}}" Margin="0,5,0,0" MinHeight="22" />
+ </Grid>
@@ -112,7 +138,7 @@
<StackPanel Orientation="Horizontal" Visibility="{Binding ShowDenoiseCustom, Converter={StaticResource boolToVisConverter}}">
<TextBlock Text="{x:Static Properties:ResourcesUI.FiltersView_Custom}" Margin="5,0,5,0" />
- <TextBox Width="120" Margin="0" Text="{Binding CustomDenoise, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" />
+ <TextBox Width="120" Margin="0" Text="{Binding CustomDenoise, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" MinHeight="22" />
</StackPanel>
</StackPanel>
diff --git a/win/CS/HandBrakeWPF/Views/Styles/Styles.xaml b/win/CS/HandBrakeWPF/Views/Styles/Styles.xaml
index 247819000..3b4b0eeb9 100644
--- a/win/CS/HandBrakeWPF/Views/Styles/Styles.xaml
+++ b/win/CS/HandBrakeWPF/Views/Styles/Styles.xaml
@@ -13,6 +13,10 @@
<Style TargetType="{x:Type TextBlock}">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
+
+ <Style TargetType="{x:Type TextBox}">
+ <Setter Property="VerticalContentAlignment" Value="Center"/>
+ </Style>
<Style TargetType="{x:Type Button}">
<Setter Property="MinHeight" Value="22"/>