/* param.c * * Copyright (c) 2003-2016 HandBrake Team * This file is part of the HandBrake source code * Homepage: . * 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 */ #include "hb_dict.h" #include "param.h" #include "common.h" #include "colormap.h" #ifdef USE_QSV #include "qsv_common.h" #endif #include static hb_filter_param_t nlmeans_presets[] = { { 1, "Custom", "custom", NULL }, { 5, "Ultralight", "ultralight", NULL }, { 2, "Light", "light", NULL }, { 3, "Medium", "medium", NULL }, { 4, "Strong", "strong", NULL }, { 0, NULL, NULL, NULL } }; static hb_filter_param_t nlmeans_tunes[] = { { 0, "None", "none", NULL }, { 1, "Film", "film", NULL }, { 2, "Grain", "grain", NULL }, { 3, "High Motion", "highmotion", NULL }, { 4, "Animation", "animation", NULL }, { 0, NULL, NULL, NULL } }; static hb_filter_param_t hqdn3d_presets[] = { { 1, "Custom", "custom", NULL }, { 5, "Ultralight", "ultralight", "y-spatial=1:cb-spatial=0.7:cr-spatial=0.7:" "y-temporal=1:cb-temporal=2:cr-temporal=2" }, { 2, "Light", "light", "y-spatial=2:cb-spatial=1:cr-spatial=1:" "y-temporal=2:cb-temporal=3:cr-temporal=3" }, { 3, "Medium", "medium", "y-spatial=3:cb-spatial=2:cr-spatial=2:" "y-temporal=2:cb-temporal=3:cr-temporal=3" }, { 4, "Strong", "strong", "y-spatial=7:cb-spatial=7:cr-spatial=7:" "y-temporal=5:cb-temporal=5:cr-temporal=5" }, { 0, NULL, NULL, NULL }, // Legacy and aliases go below the NULL { 2, "Weak", "weak", "y-spatial=2:cb-spatial=1:cr-spatial=1:" "y-temporal=2:cb-temporal=3:cr-temporal=3" }, { 2, "Default", "default", "y-spatial=2:cb-spatial=1:cr-spatial=1:" "y-temporal=2:cb-temporal=3:cr-temporal=3" }, }; static hb_filter_param_t detelecine_presets[] = { { 0, "Off", "off", "disable=1" }, { 1, "Custom", "custom", NULL }, { 2, "Default", "default", "skip-top=4:skip-bottom=4:skip-left=1:skip-right=1:plane=0" }, { 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=7" }, { 4, "Bob", "bob", "mode=23" }, { 3, "EEDI2", "eedi2", "mode=15" }, { 4, "EEDI2 Bob", "eedi2bob", "mode=31" }, { 0, NULL, NULL, NULL } }; static hb_filter_param_t deinterlace_presets[] = { { 1, "Custom", "custom", NULL }, { 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" }, { 7, "QSV", "qsv", "mode=3" } }; typedef struct { int filter_id; hb_filter_param_t *presets; hb_filter_param_t *tunes; int count; } filter_param_map_t; static filter_param_map_t param_map[] = { { HB_FILTER_NLMEANS, nlmeans_presets, nlmeans_tunes, sizeof(nlmeans_presets) / sizeof(hb_filter_param_t) }, { HB_FILTER_HQDN3D, hqdn3d_presets, NULL, sizeof(hqdn3d_presets) / sizeof(hb_filter_param_t) }, { 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) }, { HB_FILTER_DEINTERLACE, deinterlace_presets, NULL, sizeof(deinterlace_presets) / sizeof(hb_filter_param_t) }, { 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: * ultralight - visually transparent * light * medium * strong * * Tunes adjust settings to the specified content type: * none * film - most content, live action * grain - like film but preserves luma grain * highmotion - like film but avoids color smearing with stronger settings * animation - cel animation such as cartoons, anime */ static hb_dict_t * generate_nlmeans_settings(const char *preset, const char *tune, const char *custom) { hb_dict_t * settings; if (preset == NULL) return NULL; if (preset == NULL || !strcasecmp(preset, "custom")) { return hb_parse_filter_settings(custom); } if (!strcasecmp(preset, "ultralight") || !strcasecmp(preset, "light") || !strcasecmp(preset, "medium") || !strcasecmp(preset, "strong")) { double strength[2], origin_tune[2]; int patch_size[2], range[2], frames[2], prefilter[2]; if (tune == NULL || !strcasecmp(tune, "none")) { strength[0] = strength[1] = 6; origin_tune[0] = origin_tune[1] = 1; patch_size[0] = patch_size[1] = 7; range[0] = range[1] = 3; frames[0] = frames[1] = 2; prefilter[0] = prefilter[1] = 0; if (!strcasecmp(preset, "ultralight")) { strength[0] = strength[1] = 1.5; } else if (!strcasecmp(preset, "light")) { strength[0] = strength[1] = 3; } else if (!strcasecmp(preset, "strong")) { strength[0] = strength[1] = 10; } } else if (!strcasecmp(tune, "film")) { strength[0] = 6; strength[1] = 8; origin_tune[0] = origin_tune[1] = 0.8; patch_size[0] = patch_size[1] = 7; range[0] = range[1] = 3; frames[0] = frames[1] = 2; prefilter[0] = prefilter[1] = 0; if (!strcasecmp(preset, "ultralight")) { strength[0] = 1.5; strength[1] = 2.4; origin_tune[0] = 0.9; origin_tune[1] = 0.9; } else if (!strcasecmp(preset, "light")) { strength[0] = 3; strength[1] = 4; origin_tune[0] = 0.9; origin_tune[1] = 0.9; } else if (!strcasecmp(preset, "strong")) { strength[0] = 8; strength[1] = 10; origin_tune[0] = 0.6; origin_tune[1] = 0.6; } } else if (!strcasecmp(tune, "grain")) { strength[0] = 0; strength[1] = 6; origin_tune[0] = origin_tune[1] = 0.8; patch_size[0] = patch_size[1] = 7; range[0] = range[1] = 3; frames[0] = frames[1] = 2; prefilter[0] = prefilter[1] = 0; if (!strcasecmp(preset, "ultralight")) { strength[0] = 0; strength[1] = 2.4; origin_tune[0] = 0.9; origin_tune[1] = 0.9; } else if (!strcasecmp(preset, "light")) { strength[0] = 0; strength[1] = 3.5; origin_tune[0] = 0.9; origin_tune[1] = 0.9; } else if (!strcasecmp(preset, "strong")) { strength[0] = 0; strength[1] = 8; origin_tune[0] = 0.6; origin_tune[1] = 0.6; } } else if (!strcasecmp(tune, "highmotion")) { strength[0] = 6; strength[1] = 6; origin_tune[0] = 0.8; origin_tune[1] = 0.7; patch_size[0] = 7; patch_size[1] = 7; range[0] = 3; range[1] = 5; frames[0] = 2; frames[1] = 1; prefilter[0] = 0; prefilter[1] = 0; if (!strcasecmp(preset, "ultralight")) { strength[0] = 1.5; strength[1] = 2.4; origin_tune[0] = 0.9; origin_tune[1] = 0.9; } else if (!strcasecmp(preset, "light")) { strength[0] = 3; strength[1] = 3.25; origin_tune[0] = 0.9; origin_tune[1] = 0.8; } else if (!strcasecmp(preset, "strong")) { strength[0] = 8; strength[1] = 6.75; origin_tune[0] = 0.6; origin_tune[1] = 0.5; } } else if (!strcasecmp(tune, "animation")) { strength[0] = 5; strength[1] = 4; origin_tune[0] = origin_tune[1] = 0.15; patch_size[0] = patch_size[1] = 5; range[0] = range[1] = 7; frames[0] = frames[1] = 4; prefilter[0] = prefilter[1] = 0; if (!strcasecmp(preset, "ultralight")) { strength[0] = 2.5; strength[1] = 2; frames[0] = 2; frames[1] = 2; } else if (!strcasecmp(preset, "light")) { strength[0] = 3; strength[1] = 2.25; frames[0] = 3; frames[1] = 3; } else if (!strcasecmp(preset, "strong")) { strength[0] = 10; strength[1] = 8; } } else { fprintf(stderr, "Unrecognized nlmeans tune (%s).\n", tune); return NULL; } settings = hb_dict_init(); hb_dict_set(settings, "y-strength", hb_value_double(strength[0])); hb_dict_set(settings, "y-origin-tune", hb_value_double(origin_tune[0])); hb_dict_set(settings, "y-patch-size", hb_value_int(patch_size[0])); hb_dict_set(settings, "y-range", hb_value_int(range[0])); hb_dict_set(settings, "y-frame-count", hb_value_int(frames[0])); hb_dict_set(settings, "y-prefilter", hb_value_int(prefilter[0])); hb_dict_set(settings, "cb-strength", hb_value_double(strength[1])); hb_dict_set(settings, "cb-origin-tune", hb_value_double(origin_tune[1])); hb_dict_set(settings, "cb-patch-size", hb_value_int(patch_size[1])); hb_dict_set(settings, "cb-range", hb_value_int(range[1])); hb_dict_set(settings, "cb-frame-count", hb_value_int(frames[1])); hb_dict_set(settings, "cb-prefilter", hb_value_int(prefilter[1])); } else { settings = hb_parse_filter_settings(preset); if (tune != NULL) { fprintf(stderr, "Custom nlmeans parameters specified; ignoring nlmeans tune (%s).\n", tune); } } return settings; } int hb_validate_param_string(const char *regex_pattern, const char *param_string) { regex_t regex_temp; if (regcomp(®ex_temp, regex_pattern, REG_EXTENDED|REG_ICASE) == 0) { if (regexec(®ex_temp, param_string, 0, NULL, 0) == 0) { regfree(®ex_temp); return 0; } } else { hb_log("hb_validate_param_string: Error compiling regex for pattern (%s).\n", param_string); } regfree(®ex_temp); return 1; } int hb_validate_filter_settings(int filter_id, const hb_dict_t * settings) { hb_filter_object_t * filter; hb_dict_t * settings_template; hb_dict_iter_t iter; if (settings == NULL) return 0; // Verify that all keys in settings are in the filter settings template filter = hb_filter_get(filter_id); if (filter == NULL) { hb_log("hb_validate_filter_settings: Unrecognized filter (%d).\n", filter_id); return 1; } if (filter->settings_template == NULL) { // filter has no template to verify settings against return 0; } settings_template = hb_parse_filter_settings(filter->settings_template); if (settings_template == NULL) { hb_log("hb_validate_filter_settings: invalid template!"); return 0; } for (iter = hb_dict_iter_init(settings); iter != HB_DICT_ITER_DONE; iter = hb_dict_iter_next(settings, iter)) { const char * key; hb_value_t * val; key = hb_dict_iter_key(iter); // Check if key found in settings is also found in the template val = hb_dict_get(settings_template, key); if (val == NULL) { // Key is missing from template, indicate invalid settings hb_log("Invalid filter key (%s) for filter %s", key, filter->name); return 1; } // If a string value is found, and it is non-empty, // it is a regex pattern for allowed values. const char * regex_pattern = hb_value_get_string(val); if (regex_pattern != NULL && regex_pattern[0] != 0) { char * param; param = hb_value_get_string_xform(hb_dict_get(settings, key)); if (hb_validate_param_string(regex_pattern, param) != 0) { hb_log("Invalid filter value (%s) for key %s filter %s", param, key, filter->name); free(param); return 1; } free(param); } } hb_value_free(&settings_template); return 0; } int hb_validate_filter_settings_json(int filter_id, const char * json) { hb_value_t * value = hb_value_json(json); int result = hb_validate_filter_settings(filter_id, value); hb_value_free(&value); return result; } static hb_filter_param_t* filter_param_get_presets_internal(int filter_id, int *count) { int ii; if (count != NULL) { *count = 0; } for (ii = 0; param_map[ii].filter_id != HB_FILTER_INVALID; ii++) { if (param_map[ii].filter_id == filter_id) { if (count != NULL) { *count = param_map[ii].count; } return param_map[ii].presets; } } return NULL; } static hb_filter_param_t* filter_param_get_tunes_internal(int filter_id, int *count) { int ii; if (count != NULL) { *count = 0; } for (ii = 0; param_map[ii].filter_id != HB_FILTER_INVALID; ii++) { if (param_map[ii].filter_id == filter_id) { if (count != NULL) { *count = param_map[ii].count; } return param_map[ii].tunes; } } return NULL; } static hb_filter_param_t* filter_param_get_entry(hb_filter_param_t *table, const char *name, int count) { if (table == NULL || name == NULL) return NULL; int ii; for (ii = 0; ii < count; ii++) { if ((table[ii].name != NULL && !strcasecmp(name, table[ii].name)) || (table[ii].short_name != NULL && !strcasecmp(name, table[ii].short_name))) { return &table[ii]; } } return NULL; } static hb_dict_t * generate_generic_settings(int filter_id, const char *preset, const char *custom) { int preset_count; hb_filter_param_t *preset_table; hb_filter_param_t *preset_entry; if (preset == NULL || !strcasecmp(preset, "custom")) { return hb_parse_filter_settings(custom); } preset_table = filter_param_get_presets_internal(filter_id, &preset_count); preset_entry = filter_param_get_entry(preset_table, preset, preset_count); if (preset_entry != NULL && preset_entry->settings != NULL) { return hb_parse_filter_settings(preset_entry->settings); } return NULL; } static hb_value_t * generate_deblock_settings(const char * preset, const char * custom) { hb_dict_t * settings = NULL; // Deblock "presets" are just the QP value. 0 disables. if ((preset == NULL || !strcasecmp(preset, "custom"))) { settings = hb_parse_filter_settings(custom); } else { settings = hb_dict_init(); int qp = strtol(preset, NULL, 0); if (qp < 5) { hb_dict_set(settings, "disable", hb_value_bool(1)); } hb_dict_set(settings, "qp", hb_value_int(qp)); } return settings; } hb_value_t * hb_generate_filter_settings(int filter_id, const char *preset, const char *tune, const char *custom) { hb_value_t * settings = NULL; switch (filter_id) { case HB_FILTER_DEBLOCK: settings = generate_deblock_settings(preset, custom); break; case HB_FILTER_PAD: case HB_FILTER_ROTATE: case HB_FILTER_CROP_SCALE: case HB_FILTER_VFR: case HB_FILTER_RENDER_SUB: case HB_FILTER_GRAYSCALE: case HB_FILTER_QSV: settings = hb_parse_filter_settings(custom); break; 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: case HB_FILTER_DEINTERLACE: settings = generate_generic_settings(filter_id, preset, custom); break; default: fprintf(stderr, "hb_generate_filter_settings: Unrecognized filter (%d).\n", filter_id); break; } if (settings != NULL && hb_validate_filter_settings(filter_id, settings) == 0) { return settings; } hb_value_free(&settings); return NULL; } char * hb_generate_filter_settings_json(int filter_id, const char *preset, const char *tune, const char *custom) { hb_value_t * settings; settings = hb_generate_filter_settings(filter_id, preset, tune, custom); if (settings == NULL) { return NULL; } char * result = hb_value_get_json(settings); hb_value_free(&settings); return result; } int hb_validate_filter_string(int filter_id, const char * filter_str) { hb_dict_t * settings = hb_parse_filter_settings(filter_str); if (settings == NULL) { return 1; } int result = hb_validate_filter_settings(filter_id, settings); hb_value_free(&settings); return result; } int hb_validate_filter_preset(int filter_id, const char *preset, const char *tune, const char *custom) { if (preset == NULL && tune == NULL) return 1; int preset_count, tune_count; hb_filter_param_t *preset_table, *tune_table; hb_filter_param_t *preset_entry, *tune_entry; preset_table = filter_param_get_presets_internal(filter_id, &preset_count); preset_entry = filter_param_get_entry(preset_table, preset, preset_count); if (preset_entry == NULL || preset_entry->name == NULL) return 1; if (!strcasecmp(preset, "custom") && custom != NULL) { hb_dict_t * settings = hb_parse_filter_settings(custom); if (settings == NULL) { return 1; } int result = hb_validate_filter_settings(filter_id, settings); hb_value_free(&settings); return result; } if (tune != NULL) { tune_table = filter_param_get_tunes_internal(filter_id, &tune_count); tune_entry = filter_param_get_entry(tune_table, tune, tune_count); if (tune_entry == NULL) { return 1; } } return 0; } int hb_filter_preset_index(int filter_id, const char *preset) { if (preset == NULL) return -1; int preset_count; hb_filter_param_t *preset_table; hb_filter_param_t *preset_entry; preset_table = filter_param_get_presets_internal(filter_id, &preset_count); preset_entry = filter_param_get_entry(preset_table, preset, preset_count); if (preset_entry == NULL) return -1; return preset_entry->index; } int hb_filter_tune_index(int filter_id, const char *tune) { if (tune == NULL) return -1; int tune_count; hb_filter_param_t *tune_table; hb_filter_param_t *tune_entry; tune_table = filter_param_get_tunes_internal(filter_id, &tune_count); tune_entry = filter_param_get_entry(tune_table, tune, tune_count); if (tune_entry == NULL) { return -1; } return tune_entry->index; } hb_filter_param_t* hb_filter_param_get_presets(int filter_id) { return filter_param_get_presets_internal(filter_id, NULL); } hb_filter_param_t* hb_filter_param_get_tunes(int filter_id) { return filter_param_get_tunes_internal(filter_id, NULL); } // Get json array of filter preset name and short_name char * hb_filter_get_presets_json(int filter_id) { hb_value_array_t * array = hb_value_array_init(); int ii, count = 0; hb_filter_param_t * table; table = filter_param_get_presets_internal(filter_id, NULL); for (count = 0; table[count].name != NULL; count++); for (ii = 0; ii < count; ii++) { hb_dict_t * dict = hb_dict_init(); hb_dict_set(dict, "short_name", hb_value_string(table[ii].short_name)); hb_dict_set(dict, "name", hb_value_string(table[ii].name)); hb_value_array_append(array, dict); } char * result = hb_value_get_json(array); hb_value_free(&array); return result; } // Get json array of filter tune name and short_name char * hb_filter_get_tunes_json(int filter_id) { hb_value_array_t * array = hb_value_array_init(); int ii, count = 0; hb_filter_param_t * table; table = filter_param_get_tunes_internal(filter_id, NULL); for (count = 0; table[count].name != NULL; count++); for (ii = 0; ii < count; ii++) { hb_dict_t * dict = hb_dict_init(); hb_dict_set(dict, "short_name", hb_value_string(table[ii].short_name)); hb_dict_set(dict, "name", hb_value_string(table[ii].name)); hb_value_array_append(array, dict); } char * result = hb_value_get_json(array); hb_value_free(&array); return result; } char ** hb_filter_get_presets_short_name(int filter_id) { int ii, count = 0; hb_filter_param_t * table; table = filter_param_get_presets_internal(filter_id, NULL); for (count = 0; table[count].name != NULL; count++); char ** result = calloc(count + 1, sizeof(char*)); for (ii = 0; ii < count; ii++) { result[ii] = strdup(table[ii].short_name); } result[ii] = NULL; return result; } char ** hb_filter_get_presets_name(int filter_id) { int ii, count = 0; hb_filter_param_t * table; table = filter_param_get_presets_internal(filter_id, NULL); for (count = 0; table[count].name != NULL; count++); char ** result = calloc(count + 1, sizeof(char*)); for (ii = 0; ii < count; ii++) { result[ii] = strdup(table[ii].name); } result[ii] = NULL; return result; } char ** hb_filter_get_keys(int filter_id) { hb_filter_object_t * filter = hb_filter_get(filter_id); if (filter == NULL || filter->settings_template == NULL) { return NULL; } char ** tmpl = hb_str_vsplit(filter->settings_template, ':'); int ii, count = 0; for (ii = 0; tmpl[ii] != NULL; ii++) { count++; } char ** result = calloc(count + 1, sizeof(char*)); for (ii = 0; tmpl[ii] != NULL; ii++) { char ** pair = hb_str_vsplit(tmpl[ii], '='); result[ii] = strdup(pair[0]); hb_str_vfree(pair); } result[ii] = NULL; hb_str_vfree(tmpl); return result; }