summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gtk/src/Makefile.am2
-rw-r--r--gtk/src/callbacks.c125
-rw-r--r--gtk/src/callbacks.h1
-rw-r--r--gtk/src/main.c1
-rw-r--r--gtk/src/settings.c532
-rw-r--r--gtk/src/settings.h3
-rw-r--r--gtk/src/x264handler.c620
-rw-r--r--gtk/src/x264handler.h32
8 files changed, 678 insertions, 638 deletions
diff --git a/gtk/src/Makefile.am b/gtk/src/Makefile.am
index 24d386682..7d4d1d3ba 100644
--- a/gtk/src/Makefile.am
+++ b/gtk/src/Makefile.am
@@ -90,6 +90,8 @@ ghb_SOURCES = \
queuehandler.h \
audiohandler.c \
audiohandler.h \
+ x264handler.c \
+ x264handler.h \
main.c \
settings.c \
settings.h \
diff --git a/gtk/src/callbacks.c b/gtk/src/callbacks.c
index a26343268..2fc7e3979 100644
--- a/gtk/src/callbacks.c
+++ b/gtk/src/callbacks.c
@@ -210,14 +210,14 @@ ghb_check_all_depencencies(signal_user_data_t *ud)
}
}
-static void
-clear_presets_selection(signal_user_data_t *ud)
+void
+ghb_clear_presets_selection(signal_user_data_t *ud)
{
GtkTreeView *treeview;
GtkTreeSelection *selection;
if (ud->dont_clear_presets) return;
- g_debug("clear_presets_selection()");
+ g_debug("ghb_clear_presets_selection()");
treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
selection = gtk_tree_view_get_selection (treeview);
gtk_tree_selection_unselect_all (selection);
@@ -839,7 +839,7 @@ container_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
update_destination_extension(ud);
ghb_check_dependency(ud, widget);
update_acodec_combo(ud);
- clear_presets_selection(ud);
+ ghb_clear_presets_selection(ud);
audio_list = ghb_settings_get_value(ud->settings, "audio_list");
if (ghb_ac3_in_audio_list (audio_list))
@@ -1045,18 +1045,11 @@ title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
}
void
-generic_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
-{
- g_debug("generic_widget_changed_cb ()");
- ghb_check_dependency(ud, widget);
-}
-
-void
setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
{
ghb_widget_to_setting(ud->settings, widget);
ghb_check_dependency(ud, widget);
- clear_presets_selection(ud);
+ ghb_clear_presets_selection(ud);
}
static void
@@ -1117,7 +1110,8 @@ http_opt_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
{
ghb_widget_to_setting(ud->settings, widget);
ghb_check_dependency(ud, widget);
- clear_presets_selection(ud);
+ ghb_clear_presets_selection(ud);
+ // AC3 is not allowed when Web optimized
ghb_grey_combo_options (ud->builder);
}
@@ -1128,7 +1122,7 @@ vcodec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
ghb_widget_to_setting(ud->settings, widget);
ghb_check_dependency(ud, widget);
- clear_presets_selection(ud);
+ ghb_clear_presets_selection(ud);
ghb_vquality_range(ud, &vqmin, &vqmax);
GtkWidget *qp = GHB_WIDGET(ud->builder, "video_quality");
gtk_range_set_range (GTK_RANGE(qp), vqmin, vqmax);
@@ -1141,7 +1135,7 @@ target_size_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
g_debug("setting_widget_changed_cb () %s", name);
ghb_widget_to_setting(ud->settings, widget);
ghb_check_dependency(ud, widget);
- clear_presets_selection(ud);
+ ghb_clear_presets_selection(ud);
if (ghb_settings_get_boolean(ud->settings, "vquality_type_target"))
{
gint titleindex;
@@ -1265,7 +1259,7 @@ scale_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
g_debug("scale_changed_cb ()");
ghb_widget_to_setting(ud->settings, widget);
ghb_check_dependency(ud, widget);
- clear_presets_selection(ud);
+ ghb_clear_presets_selection(ud);
ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
update_preview = TRUE;
@@ -1299,81 +1293,6 @@ generic_entry_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
}
}
-gboolean
-generic_focus_out_cb(GtkWidget *widget, GdkEventFocus *event,
- signal_user_data_t *ud)
-{
- g_debug("generic_focus_out_cb ()");
- ghb_widget_to_setting(ud->settings, widget);
- return FALSE;
-}
-
-// Flag needed to prevent x264 options processing from chasing its tail
-static gboolean ignore_options_update = FALSE;
-
-void
-x264_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
-{
- ghb_widget_to_setting(ud->settings, widget);
- if (!ignore_options_update)
- {
- ignore_options_update = TRUE;
- ghb_x264_opt_update(ud, widget);
- ignore_options_update = FALSE;
- }
- ghb_check_dependency(ud, widget);
- clear_presets_selection(ud);
-}
-
-void
-x264_entry_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
-{
- g_debug("x264_entry_changed_cb ()");
- if (!ignore_options_update)
- {
- GtkWidget *textview;
- gchar *options;
-
- textview = GTK_WIDGET(GHB_WIDGET(ud->builder, "x264_options"));
- ghb_widget_to_setting(ud->settings, textview);
- options = ghb_settings_get_string(ud->settings, "x264_options");
- ignore_options_update = TRUE;
- ghb_x264_parse_options(ud, options);
- if (!GTK_WIDGET_HAS_FOCUS(textview))
- {
- gchar *sopts;
-
- sopts = ghb_sanitize_x264opts(ud, options);
- ghb_ui_update(ud, "x264_options", ghb_string_value(sopts));
- ghb_x264_parse_options(ud, sopts);
- g_free(sopts);
- }
- g_free(options);
- ignore_options_update = FALSE;
- }
-}
-
-gboolean
-x264_focus_out_cb(GtkWidget *widget, GdkEventFocus *event,
- signal_user_data_t *ud)
-{
- gchar *options, *sopts;
-
- ghb_widget_to_setting(ud->settings, widget);
- options = ghb_settings_get_string(ud->settings, "x264_options");
- sopts = ghb_sanitize_x264opts(ud, options);
- ignore_options_update = TRUE;
- if (sopts != NULL)
- {
- ghb_ui_update(ud, "x264_options", ghb_string_value(sopts));
- ghb_x264_parse_options(ud, sopts);
- }
- g_free(options);
- g_free(sopts);
- ignore_options_update = FALSE;
- return FALSE;
-}
-
void
ghb_presets_list_update(signal_user_data_t *ud)
{
@@ -1550,18 +1469,6 @@ presets_restore_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
}
void
-prefs_dialog_cb(GtkWidget *xwidget, signal_user_data_t *ud)
-{
- GtkWidget *dialog;
- GtkResponseType response;
-
- g_debug("prefs_dialog_cb ()");
- dialog = GHB_WIDGET(ud->builder, "prefs_dialog");
- response = gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_hide(dialog);
-}
-
-void
presets_remove_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
{
GtkTreeView *treeview;
@@ -1696,6 +1603,18 @@ presets_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_
}
}
+void
+prefs_dialog_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+{
+ GtkWidget *dialog;
+ GtkResponseType response;
+
+ g_debug("prefs_dialog_cb ()");
+ dialog = GHB_WIDGET(ud->builder, "prefs_dialog");
+ response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_hide(dialog);
+}
+
gboolean
ghb_message_dialog(GtkMessageType type, const gchar *message, const gchar *no, const gchar *yes)
{
diff --git a/gtk/src/callbacks.h b/gtk/src/callbacks.h
index d49144d1b..7bc793642 100644
--- a/gtk/src/callbacks.h
+++ b/gtk/src/callbacks.h
@@ -46,6 +46,7 @@ gboolean ghb_reload_queue(signal_user_data_t *ud);
gboolean ghb_cancel_encode(const gchar *extra_msg);
GValue* ghb_start_next_job(signal_user_data_t *ud, gboolean find_first);
void ghb_check_dependency(signal_user_data_t *ud, GtkWidget *widget);
+void ghb_clear_presets_selection(signal_user_data_t *ud);
#endif // _CALLBACKS_H_
diff --git a/gtk/src/main.c b/gtk/src/main.c
index 94e538672..52c581ca9 100644
--- a/gtk/src/main.c
+++ b/gtk/src/main.c
@@ -42,6 +42,7 @@
#include "values.h"
#include "icons.h"
#include "callbacks.h"
+#include "x264handler.h"
#include "settings.h"
#include "resources.h"
#include "presets.h"
diff --git a/gtk/src/settings.c b/gtk/src/settings.c
index d108814d5..e3bec95ea 100644
--- a/gtk/src/settings.c
+++ b/gtk/src/settings.c
@@ -587,538 +587,6 @@ ghb_ui_update(signal_user_data_t *ud, const gchar *name, const GValue *value)
return 0;
}
-enum
-{
- X264_OPT_DEBLOCK,
- X264_OPT_INT,
- X264_OPT_COMBO,
- X264_OPT_BOOL,
-};
-
-struct x264_opt_map_s
-{
- gchar **opt_syns;
- gchar *name;
- gchar *def_val;
- gint type;
- gboolean found;
-};
-
-static gchar *x264_ref_syns[] = {"ref", "frameref", NULL};
-static gchar *x264_mixed_syns[] = {"mixed-refs", "mixed_refs", NULL};
-static gchar *x264_bframes_syns[] = {"bframes", NULL};
-static gchar *x264_direct_syns[] =
- {"direct", "direct-pred", "direct_pred", NULL};
-static gchar *x264_weightb_syns[] = {"weightb", "weight-b", "weight_b", NULL};
-static gchar *x264_brdo_syns[] = {"brdo", "b-rdo", "b_rdo", NULL};
-static gchar *x264_bime_syns[] = {"bime", NULL};
-static gchar *x264_bpyramid_syns[] = {"b-pyramid", "b_pyramid", NULL};
-static gchar *x264_me_syns[] = {"me", NULL};
-static gchar *x264_merange_syns[] = {"merange", "me-range", "me_range", NULL};
-static gchar *x264_subme_syns[] = {"subme", "subq", NULL};
-static gchar *x264_analyse_syns[] = {"analyse", "partitions", NULL};
-static gchar *x264_8x8dct_syns[] = {"8x8dct", NULL};
-static gchar *x264_deblock_syns[] = {"deblock", "filter", NULL};
-static gchar *x264_trellis_syns[] = {"trellis", NULL};
-static gchar *x264_pskip_syns[] = {"no-fast-pskip", "no_fast_pskip", NULL};
-static gchar *x264_decimate_syns[] =
- {"no-dct-decimate", "no_dct_decimate", NULL};
-static gchar *x264_cabac_syns[] = {"cabac", NULL};
-
-static gint
-find_syn_match(const gchar *opt, gchar **syns)
-{
- gint ii;
- for (ii = 0; syns[ii] != NULL; ii++)
- {
- if (strcmp(opt, syns[ii]) == 0)
- return ii;
- }
- return -1;
-}
-
-struct x264_opt_map_s x264_opt_map[] =
-{
- {x264_ref_syns, "x264_refs", "1", X264_OPT_INT},
- {x264_mixed_syns, "x264_mixed_refs", "0", X264_OPT_BOOL},
- {x264_bframes_syns, "x264_bframes", "0", X264_OPT_INT},
- {x264_direct_syns, "x264_direct", "spatial", X264_OPT_COMBO},
- {x264_weightb_syns, "x264_weighted_bframes", "0", X264_OPT_BOOL},
- {x264_brdo_syns, "x264_brdo", "0", X264_OPT_BOOL},
- {x264_bime_syns, "x264_bime", "0", X264_OPT_BOOL},
- {x264_bpyramid_syns, "x264_bpyramid", "0", X264_OPT_BOOL},
- {x264_me_syns, "x264_me", "hex", X264_OPT_COMBO},
- {x264_merange_syns, "x264_merange", "16", X264_OPT_INT},
- {x264_subme_syns, "x264_subme", "6", X264_OPT_COMBO},
- {x264_analyse_syns, "x264_analyse", "some", X264_OPT_COMBO},
- {x264_8x8dct_syns, "x264_8x8dct", "0", X264_OPT_BOOL},
- {x264_deblock_syns, "x264_deblock_alpha", "0,0", X264_OPT_DEBLOCK},
- {x264_deblock_syns, "x264_deblock_beta", "0,0", X264_OPT_DEBLOCK},
- {x264_trellis_syns, "x264_trellis", "0", X264_OPT_COMBO},
- {x264_pskip_syns, "x264_no_fast_pskip", "0", X264_OPT_BOOL},
- {x264_decimate_syns, "x264_no_dct_decimate", "0", X264_OPT_BOOL},
- {x264_cabac_syns, "x264_cabac", "1", X264_OPT_BOOL},
-};
-#define X264_OPT_MAP_SIZE (sizeof(x264_opt_map)/sizeof(struct x264_opt_map_s))
-
-static const gchar*
-x264_opt_get_default(const gchar *opt)
-{
- gint jj;
- for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
- {
- if (find_syn_match(opt, x264_opt_map[jj].opt_syns) >= 0)
- {
- return x264_opt_map[jj].def_val;
- }
- }
- return "";
-}
-
-static void
-x264_update_int(signal_user_data_t *ud, const gchar *name, const gchar *val)
-{
- gint ival;
-
- if (val == NULL) return;
- ival = g_strtod (val, NULL);
- ghb_ui_update(ud, name, ghb_int64_value(ival));
-}
-
-static gchar *true_str[] =
-{
- "true",
- "yes",
- "1",
- NULL
-};
-
-static gboolean
-str_is_true(const gchar *str)
-{
- gint ii;
- for (ii = 0; true_str[ii]; ii++)
- {
- if (g_ascii_strcasecmp(str, true_str[ii]) == 0)
- return TRUE;
- }
- return FALSE;
-}
-
-static void
-x264_update_bool(signal_user_data_t *ud, const gchar *name, const gchar *val)
-{
- if (val == NULL)
- ghb_ui_update(ud, name, ghb_boolean_value(1));
- else
- ghb_ui_update(ud, name, ghb_boolean_value(str_is_true(val)));
-}
-
-static void
-x264_update_combo(signal_user_data_t *ud, const gchar *name, const gchar *val)
-{
- GtkTreeModel *store;
- GtkTreeIter iter;
- gchar *shortOpt;
- gint ivalue;
- gboolean foundit = FALSE;
- GtkWidget *widget;
-
- if (val == NULL) return;
- widget = GHB_WIDGET(ud->builder, name);
- if (widget == NULL)
- {
- g_debug("Failed to find widget for key: %s\n", name);
- return;
- }
- store = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
- if (gtk_tree_model_get_iter_first (store, &iter))
- {
- do
- {
- gtk_tree_model_get(store, &iter, 2, &shortOpt, 3, &ivalue, -1);
- if (strcmp(shortOpt, val) == 0)
- {
- gtk_combo_box_set_active_iter (GTK_COMBO_BOX(widget), &iter);
- g_free(shortOpt);
- foundit = TRUE;
- break;
- }
- g_free(shortOpt);
- } while (gtk_tree_model_iter_next (store, &iter));
- }
- if (!foundit)
- {
- if (gtk_tree_model_get_iter_first (store, &iter))
- {
- do
- {
- gtk_tree_model_get(store, &iter, 2, &shortOpt, 3, &ivalue, -1);
- if (strcmp(shortOpt, "custom") == 0)
- {
- gtk_list_store_set(GTK_LIST_STORE(store), &iter, 4, val, -1);
- gtk_combo_box_set_active_iter (GTK_COMBO_BOX(widget), &iter);
- g_free(shortOpt);
- foundit = TRUE;
- break;
- }
- g_free(shortOpt);
- } while (gtk_tree_model_iter_next (store, &iter));
- }
- }
- // Its possible the value hasn't changed. Since settings are only
- // updated when the value changes, I'm initializing settings here as well.
- ghb_widget_to_setting(ud->settings, widget);
-}
-
-static void
-x264_update_deblock(signal_user_data_t *ud, const gchar *xval)
-{
- gdouble avalue, bvalue;
- gchar *end;
- gchar *val;
- gchar *bval = NULL;
-
- if (xval == NULL) return;
- val = g_strdup(xval);
- bvalue = avalue = 0;
- if (val != NULL)
- {
- gchar *pos = strchr(val, ',');
- if (pos != NULL)
- {
- bval = pos + 1;
- *pos = 0;
- }
- avalue = g_strtod (val, &end);
- if (bval != NULL)
- {
- bvalue = g_strtod (bval, &end);
- }
- }
- g_free(val);
- ghb_ui_update(ud, "x264_deblock_alpha", ghb_int64_value(avalue));
- ghb_ui_update(ud, "x264_deblock_beta", ghb_int64_value(bvalue));
-}
-
-void
-ghb_x264_parse_options(signal_user_data_t *ud, const gchar *options)
-{
- gchar **split = g_strsplit(options, ":", -1);
- if (split == NULL) return;
-
- gint ii;
- gint jj;
-
- for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
- x264_opt_map[jj].found = FALSE;
-
- for (ii = 0; split[ii] != NULL; ii++)
- {
- gchar *val = NULL;
- gchar *pos = strchr(split[ii], '=');
- if (pos != NULL)
- {
- val = pos + 1;
- *pos = 0;
- }
- for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
- {
- if (find_syn_match(split[ii], x264_opt_map[jj].opt_syns) >= 0)
- {
- x264_opt_map[jj].found = TRUE;
- switch(x264_opt_map[jj].type)
- {
- case X264_OPT_INT:
- x264_update_int(ud, x264_opt_map[jj].name, val);
- break;
- case X264_OPT_BOOL:
- x264_update_bool(ud, x264_opt_map[jj].name, val);
- break;
- case X264_OPT_COMBO:
- x264_update_combo(ud, x264_opt_map[jj].name, val);
- break;
- case X264_OPT_DEBLOCK:
- // dirty little hack. mark deblock_beta found as well
- x264_opt_map[jj+1].found = TRUE;
- x264_update_deblock(ud, val);
- break;
- }
- break;
- }
- }
- }
- // For any options not found in the option string, set ui to
- // default values
- for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
- {
- if (!x264_opt_map[jj].found)
- {
- gchar *val = strdup(x264_opt_map[jj].def_val);
- switch(x264_opt_map[jj].type)
- {
- case X264_OPT_INT:
- x264_update_int(ud, x264_opt_map[jj].name, val);
- break;
- case X264_OPT_BOOL:
- x264_update_bool(ud, x264_opt_map[jj].name, val);
- break;
- case X264_OPT_COMBO:
- x264_update_combo(ud, x264_opt_map[jj].name, val);
- break;
- case X264_OPT_DEBLOCK:
- x264_update_deblock(ud, val);
- break;
- }
- x264_opt_map[jj].found = TRUE;
- g_free(val);
- }
- }
- g_strfreev(split);
-}
-
-gchar*
-get_deblock_val(signal_user_data_t *ud)
-{
- gchar *alpha, *beta;
- gchar *result;
- alpha = ghb_settings_get_string(ud->settings, "x264_deblock_alpha");
- beta = ghb_settings_get_string(ud->settings, "x264_deblock_beta");
- result = g_strdup_printf("%s,%s", alpha, beta);
- g_free(alpha);
- g_free(beta);
- return result;
-}
-
-void
-ghb_x264_opt_update(signal_user_data_t *ud, GtkWidget *widget)
-{
- gint jj;
- const gchar *name = gtk_widget_get_name(widget);
- gchar **opt_syns = NULL;
- const gchar *def_val = NULL;
- gint type;
-
- for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
- {
- if (strcmp(name, x264_opt_map[jj].name) == 0)
- {
- // found the options that needs updating
- opt_syns = x264_opt_map[jj].opt_syns;
- def_val = x264_opt_map[jj].def_val;
- type = x264_opt_map[jj].type;
- break;
- }
- }
- if (opt_syns != NULL)
- {
- GString *x264opts = g_string_new("");
- gchar *options;
- gchar **split = NULL;
- gint ii;
- gboolean foundit = FALSE;
-
- options = ghb_settings_get_string(ud->settings, "x264_options");
- if (options)
- {
- split = g_strsplit(options, ":", -1);
- g_free(options);
- }
- for (ii = 0; split && split[ii] != NULL; ii++)
- {
- gint syn;
- gchar *val = NULL;
- gchar *pos = strchr(split[ii], '=');
- if (pos != NULL)
- {
- val = pos + 1;
- *pos = 0;
- }
- syn = find_syn_match(split[ii], opt_syns);
- if (syn >= 0)
- { // Updating this option
- gchar *val;
- foundit = TRUE;
- if (type == X264_OPT_DEBLOCK)
- val = get_deblock_val(ud);
- else
- {
- GValue *gval;
- gval = ghb_widget_value(widget);
- if (G_VALUE_TYPE(gval) == G_TYPE_BOOLEAN)
- {
- if (ghb_value_boolean(gval))
- val = g_strdup("1");
- else
- val = g_strdup("0");
- }
- else
- {
- val = ghb_widget_string(widget);
- }
- ghb_value_free(gval);
- }
- if (strcmp(def_val, val) != 0)
- {
- g_string_append_printf(x264opts, "%s=%s:", opt_syns[syn], val);
- }
- g_free(val);
- }
- else if (val != NULL)
- g_string_append_printf(x264opts, "%s=%s:", split[ii], val);
- else
- g_string_append_printf(x264opts, "%s:", split[ii]);
-
- }
- if (split) g_strfreev(split);
- if (!foundit)
- {
- gchar *val;
- if (type == X264_OPT_DEBLOCK)
- val = get_deblock_val(ud);
- else
- {
- GValue *gval;
- gval = ghb_widget_value(widget);
- if (G_VALUE_TYPE(gval) == G_TYPE_BOOLEAN)
- {
- if (ghb_value_boolean(gval))
- val = g_strdup("1");
- else
- val = g_strdup("0");
- }
- else
- {
- val = ghb_widget_string(widget);
- }
- ghb_value_free(gval);
- }
- if (strcmp(def_val, val) != 0)
- {
- g_string_append_printf(x264opts, "%s=%s:", opt_syns[0], val);
- }
- g_free(val);
- }
- // Update the options value
- // strip the trailing ":"
- gchar *result;
- gint len;
- result = g_string_free(x264opts, FALSE);
- len = strlen(result);
- if (len > 0) result[len - 1] = 0;
- gchar *sopts;
- sopts = ghb_sanitize_x264opts(ud, result);
- ghb_ui_update(ud, "x264_options", ghb_string_value(sopts));
- ghb_x264_parse_options(ud, sopts);
- g_free(sopts);
- g_free(result);
- }
-}
-
-static void
-x264_remove_opt(gchar **opts, gchar **opt_syns)
-{
- gint ii;
- for (ii = 0; opts[ii] != NULL; ii++)
- {
- gchar *opt;
- opt = g_strdup(opts[ii]);
- gchar *pos = strchr(opt, '=');
- if (pos != NULL)
- {
- *pos = 0;
- }
- if (find_syn_match(opt, opt_syns) >= 0)
- {
- // Mark as deleted
- opts[ii][0] = 0;
- }
- g_free(opt);
- }
-}
-
-// Construct the x264 options string
-// The result is allocated, so someone must free it at some point.
-gchar*
-ghb_sanitize_x264opts(signal_user_data_t *ud, const gchar *options)
-{
- GString *x264opts = g_string_new("");
- gchar **split = g_strsplit(options, ":", -1);
-
- // Remove entries that match the defaults
- gint ii;
- for (ii = 0; split[ii] != NULL; ii++)
- {
- gchar *val = NULL;
- gchar *opt = g_strdup(split[ii]);
- gchar *pos = strchr(opt, '=');
- if (pos != NULL)
- {
- val = pos + 1;
- *pos = 0;
- }
- else
- {
- val = "1";
- }
- const gchar *def_val = x264_opt_get_default(opt);
- if (strcmp(val, def_val) == 0)
- {
- // Matches the default, so remove it
- split[ii][0] = 0;
- }
- g_free(opt);
- }
- gint refs = ghb_settings_get_int(ud->settings, "x264_refs");
- if (refs <= 1)
- {
- x264_remove_opt(split, x264_mixed_syns);
- }
- gint subme;
-
- subme = ghb_lookup_combo_int("x264_subme",
- ghb_settings_get_value(ud->settings, "x264_subme"));
- if (subme < 6)
- {
- x264_remove_opt(split, x264_brdo_syns);
- }
- gint bframes = ghb_settings_get_int(ud->settings, "x264_bframes");
- if (bframes == 0)
- {
- x264_remove_opt(split, x264_weightb_syns);
- x264_remove_opt(split, x264_brdo_syns);
- x264_remove_opt(split, x264_bime_syns);
- x264_remove_opt(split, x264_direct_syns);
- }
- if (bframes <= 1)
- {
- x264_remove_opt(split, x264_bpyramid_syns);
- }
- gchar *me = ghb_settings_get_string(ud->settings, "x264_me");
- if (!(strcmp(me, "umh") == 0 || strcmp(me, "esa") == 0))
- {
- x264_remove_opt(split, x264_merange_syns);
- }
- g_free(me);
- if (!ghb_settings_get_boolean(ud->settings, "x264_cabac"))
- {
- x264_remove_opt(split, x264_trellis_syns);
- }
- for (ii = 0; split[ii] != NULL; ii++)
- {
- if (split[ii][0] != 0)
- g_string_append_printf(x264opts, "%s:", split[ii]);
- }
- g_strfreev(split);
- // strip the trailing ":"
- gchar *result;
- gint len;
- result = g_string_free(x264opts, FALSE);
- len = strlen(result);
- if (len > 0) result[len - 1] = 0;
- return result;
-}
-
gint
ghb_pref_acount(GValue *settings)
{
diff --git a/gtk/src/settings.h b/gtk/src/settings.h
index ca3ffa1e2..69a79c2a3 100644
--- a/gtk/src/settings.h
+++ b/gtk/src/settings.h
@@ -94,9 +94,6 @@ void ghb_widget_to_setting(GValue *settings, GtkWidget *widget);
int ghb_ui_update(
signal_user_data_t *ud, const gchar *name, const GValue *value);
-void ghb_x264_parse_options(signal_user_data_t *ud, const gchar *options);
-void ghb_x264_opt_update(signal_user_data_t *ud, GtkWidget *widget);
-gchar* ghb_sanitize_x264opts(signal_user_data_t *ud, const gchar *options);
gint ghb_pref_acount(GValue *settings);
gint ghb_pref_acodec(GValue *settings, gint index);
diff --git a/gtk/src/x264handler.c b/gtk/src/x264handler.c
new file mode 100644
index 000000000..4a75520f0
--- /dev/null
+++ b/gtk/src/x264handler.c
@@ -0,0 +1,620 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * x264handler.c
+ * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
+ *
+ * x264handler.c is free software.
+ *
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <gtk/gtk.h>
+#include <string.h>
+#include "settings.h"
+#include "values.h"
+#include "callbacks.h"
+#include "x264handler.h"
+
+static void x264_opt_update(signal_user_data_t *ud, GtkWidget *widget);
+static gchar* sanitize_x264opts(signal_user_data_t *ud, const gchar *options);
+
+// Flag needed to prevent x264 options processing from chasing its tail
+static gboolean ignore_options_update = FALSE;
+
+void
+x264_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+ ghb_widget_to_setting(ud->settings, widget);
+ if (!ignore_options_update)
+ {
+ ignore_options_update = TRUE;
+ x264_opt_update(ud, widget);
+ ignore_options_update = FALSE;
+ }
+ ghb_check_dependency(ud, widget);
+ ghb_clear_presets_selection(ud);
+}
+
+void
+x264_entry_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+ g_debug("x264_entry_changed_cb ()");
+ if (!ignore_options_update)
+ {
+ GtkWidget *textview;
+ gchar *options;
+
+ textview = GTK_WIDGET(GHB_WIDGET(ud->builder, "x264_options"));
+ ghb_widget_to_setting(ud->settings, textview);
+ options = ghb_settings_get_string(ud->settings, "x264_options");
+ ignore_options_update = TRUE;
+ ghb_x264_parse_options(ud, options);
+ if (!GTK_WIDGET_HAS_FOCUS(textview))
+ {
+ gchar *sopts;
+
+ sopts = sanitize_x264opts(ud, options);
+ ghb_ui_update(ud, "x264_options", ghb_string_value(sopts));
+ ghb_x264_parse_options(ud, sopts);
+ g_free(sopts);
+ }
+ g_free(options);
+ ignore_options_update = FALSE;
+ }
+}
+
+gboolean
+x264_focus_out_cb(GtkWidget *widget, GdkEventFocus *event,
+ signal_user_data_t *ud)
+{
+ gchar *options, *sopts;
+
+ ghb_widget_to_setting(ud->settings, widget);
+ options = ghb_settings_get_string(ud->settings, "x264_options");
+ sopts = sanitize_x264opts(ud, options);
+ ignore_options_update = TRUE;
+ if (sopts != NULL)
+ {
+ ghb_ui_update(ud, "x264_options", ghb_string_value(sopts));
+ ghb_x264_parse_options(ud, sopts);
+ }
+ g_free(options);
+ g_free(sopts);
+ ignore_options_update = FALSE;
+ return FALSE;
+}
+
+enum
+{
+ X264_OPT_DEBLOCK,
+ X264_OPT_INT,
+ X264_OPT_COMBO,
+ X264_OPT_BOOL,
+};
+
+struct x264_opt_map_s
+{
+ gchar **opt_syns;
+ gchar *name;
+ gchar *def_val;
+ gint type;
+ gboolean found;
+};
+
+static gchar *x264_ref_syns[] = {"ref", "frameref", NULL};
+static gchar *x264_mixed_syns[] = {"mixed-refs", "mixed_refs", NULL};
+static gchar *x264_bframes_syns[] = {"bframes", NULL};
+static gchar *x264_direct_syns[] =
+ {"direct", "direct-pred", "direct_pred", NULL};
+static gchar *x264_weightb_syns[] = {"weightb", "weight-b", "weight_b", NULL};
+static gchar *x264_brdo_syns[] = {"brdo", "b-rdo", "b_rdo", NULL};
+static gchar *x264_bime_syns[] = {"bime", NULL};
+static gchar *x264_bpyramid_syns[] = {"b-pyramid", "b_pyramid", NULL};
+static gchar *x264_me_syns[] = {"me", NULL};
+static gchar *x264_merange_syns[] = {"merange", "me-range", "me_range", NULL};
+static gchar *x264_subme_syns[] = {"subme", "subq", NULL};
+static gchar *x264_analyse_syns[] = {"analyse", "partitions", NULL};
+static gchar *x264_8x8dct_syns[] = {"8x8dct", NULL};
+static gchar *x264_deblock_syns[] = {"deblock", "filter", NULL};
+static gchar *x264_trellis_syns[] = {"trellis", NULL};
+static gchar *x264_pskip_syns[] = {"no-fast-pskip", "no_fast_pskip", NULL};
+static gchar *x264_decimate_syns[] =
+ {"no-dct-decimate", "no_dct_decimate", NULL};
+static gchar *x264_cabac_syns[] = {"cabac", NULL};
+
+static gint
+find_syn_match(const gchar *opt, gchar **syns)
+{
+ gint ii;
+ for (ii = 0; syns[ii] != NULL; ii++)
+ {
+ if (strcmp(opt, syns[ii]) == 0)
+ return ii;
+ }
+ return -1;
+}
+
+struct x264_opt_map_s x264_opt_map[] =
+{
+ {x264_ref_syns, "x264_refs", "1", X264_OPT_INT},
+ {x264_mixed_syns, "x264_mixed_refs", "0", X264_OPT_BOOL},
+ {x264_bframes_syns, "x264_bframes", "0", X264_OPT_INT},
+ {x264_direct_syns, "x264_direct", "spatial", X264_OPT_COMBO},
+ {x264_weightb_syns, "x264_weighted_bframes", "0", X264_OPT_BOOL},
+ {x264_brdo_syns, "x264_brdo", "0", X264_OPT_BOOL},
+ {x264_bime_syns, "x264_bime", "0", X264_OPT_BOOL},
+ {x264_bpyramid_syns, "x264_bpyramid", "0", X264_OPT_BOOL},
+ {x264_me_syns, "x264_me", "hex", X264_OPT_COMBO},
+ {x264_merange_syns, "x264_merange", "16", X264_OPT_INT},
+ {x264_subme_syns, "x264_subme", "6", X264_OPT_COMBO},
+ {x264_analyse_syns, "x264_analyse", "some", X264_OPT_COMBO},
+ {x264_8x8dct_syns, "x264_8x8dct", "0", X264_OPT_BOOL},
+ {x264_deblock_syns, "x264_deblock_alpha", "0,0", X264_OPT_DEBLOCK},
+ {x264_deblock_syns, "x264_deblock_beta", "0,0", X264_OPT_DEBLOCK},
+ {x264_trellis_syns, "x264_trellis", "0", X264_OPT_COMBO},
+ {x264_pskip_syns, "x264_no_fast_pskip", "0", X264_OPT_BOOL},
+ {x264_decimate_syns, "x264_no_dct_decimate", "0", X264_OPT_BOOL},
+ {x264_cabac_syns, "x264_cabac", "1", X264_OPT_BOOL},
+};
+#define X264_OPT_MAP_SIZE (sizeof(x264_opt_map)/sizeof(struct x264_opt_map_s))
+
+static const gchar*
+x264_opt_get_default(const gchar *opt)
+{
+ gint jj;
+ for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
+ {
+ if (find_syn_match(opt, x264_opt_map[jj].opt_syns) >= 0)
+ {
+ return x264_opt_map[jj].def_val;
+ }
+ }
+ return "";
+}
+
+static void
+x264_update_int(signal_user_data_t *ud, const gchar *name, const gchar *val)
+{
+ gint ival;
+
+ if (val == NULL) return;
+ ival = g_strtod (val, NULL);
+ ghb_ui_update(ud, name, ghb_int64_value(ival));
+}
+
+static gchar *true_str[] =
+{
+ "true",
+ "yes",
+ "1",
+ NULL
+};
+
+static gboolean
+str_is_true(const gchar *str)
+{
+ gint ii;
+ for (ii = 0; true_str[ii]; ii++)
+ {
+ if (g_ascii_strcasecmp(str, true_str[ii]) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+x264_update_bool(signal_user_data_t *ud, const gchar *name, const gchar *val)
+{
+ if (val == NULL)
+ ghb_ui_update(ud, name, ghb_boolean_value(1));
+ else
+ ghb_ui_update(ud, name, ghb_boolean_value(str_is_true(val)));
+}
+
+static void
+x264_update_combo(signal_user_data_t *ud, const gchar *name, const gchar *val)
+{
+ GtkTreeModel *store;
+ GtkTreeIter iter;
+ gchar *shortOpt;
+ gint ivalue;
+ gboolean foundit = FALSE;
+ GtkWidget *widget;
+
+ if (val == NULL) return;
+ widget = GHB_WIDGET(ud->builder, name);
+ if (widget == NULL)
+ {
+ g_debug("Failed to find widget for key: %s\n", name);
+ return;
+ }
+ store = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
+ if (gtk_tree_model_get_iter_first (store, &iter))
+ {
+ do
+ {
+ gtk_tree_model_get(store, &iter, 2, &shortOpt, 3, &ivalue, -1);
+ if (strcmp(shortOpt, val) == 0)
+ {
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX(widget), &iter);
+ g_free(shortOpt);
+ foundit = TRUE;
+ break;
+ }
+ g_free(shortOpt);
+ } while (gtk_tree_model_iter_next (store, &iter));
+ }
+ if (!foundit)
+ {
+ if (gtk_tree_model_get_iter_first (store, &iter))
+ {
+ do
+ {
+ gtk_tree_model_get(store, &iter, 2, &shortOpt, 3, &ivalue, -1);
+ if (strcmp(shortOpt, "custom") == 0)
+ {
+ gtk_list_store_set(GTK_LIST_STORE(store), &iter, 4, val, -1);
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX(widget), &iter);
+ g_free(shortOpt);
+ foundit = TRUE;
+ break;
+ }
+ g_free(shortOpt);
+ } while (gtk_tree_model_iter_next (store, &iter));
+ }
+ }
+ // Its possible the value hasn't changed. Since settings are only
+ // updated when the value changes, I'm initializing settings here as well.
+ ghb_widget_to_setting(ud->settings, widget);
+}
+
+static void
+x264_update_deblock(signal_user_data_t *ud, const gchar *xval)
+{
+ gdouble avalue, bvalue;
+ gchar *end;
+ gchar *val;
+ gchar *bval = NULL;
+
+ if (xval == NULL) return;
+ val = g_strdup(xval);
+ bvalue = avalue = 0;
+ if (val != NULL)
+ {
+ gchar *pos = strchr(val, ',');
+ if (pos != NULL)
+ {
+ bval = pos + 1;
+ *pos = 0;
+ }
+ avalue = g_strtod (val, &end);
+ if (bval != NULL)
+ {
+ bvalue = g_strtod (bval, &end);
+ }
+ }
+ g_free(val);
+ ghb_ui_update(ud, "x264_deblock_alpha", ghb_int64_value(avalue));
+ ghb_ui_update(ud, "x264_deblock_beta", ghb_int64_value(bvalue));
+}
+
+void
+ghb_x264_parse_options(signal_user_data_t *ud, const gchar *options)
+{
+ gchar **split = g_strsplit(options, ":", -1);
+ if (split == NULL) return;
+
+ gint ii;
+ gint jj;
+
+ for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
+ x264_opt_map[jj].found = FALSE;
+
+ for (ii = 0; split[ii] != NULL; ii++)
+ {
+ gchar *val = NULL;
+ gchar *pos = strchr(split[ii], '=');
+ if (pos != NULL)
+ {
+ val = pos + 1;
+ *pos = 0;
+ }
+ for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
+ {
+ if (find_syn_match(split[ii], x264_opt_map[jj].opt_syns) >= 0)
+ {
+ x264_opt_map[jj].found = TRUE;
+ switch(x264_opt_map[jj].type)
+ {
+ case X264_OPT_INT:
+ x264_update_int(ud, x264_opt_map[jj].name, val);
+ break;
+ case X264_OPT_BOOL:
+ x264_update_bool(ud, x264_opt_map[jj].name, val);
+ break;
+ case X264_OPT_COMBO:
+ x264_update_combo(ud, x264_opt_map[jj].name, val);
+ break;
+ case X264_OPT_DEBLOCK:
+ // dirty little hack. mark deblock_beta found as well
+ x264_opt_map[jj+1].found = TRUE;
+ x264_update_deblock(ud, val);
+ break;
+ }
+ break;
+ }
+ }
+ }
+ // For any options not found in the option string, set ui to
+ // default values
+ for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
+ {
+ if (!x264_opt_map[jj].found)
+ {
+ gchar *val = strdup(x264_opt_map[jj].def_val);
+ switch(x264_opt_map[jj].type)
+ {
+ case X264_OPT_INT:
+ x264_update_int(ud, x264_opt_map[jj].name, val);
+ break;
+ case X264_OPT_BOOL:
+ x264_update_bool(ud, x264_opt_map[jj].name, val);
+ break;
+ case X264_OPT_COMBO:
+ x264_update_combo(ud, x264_opt_map[jj].name, val);
+ break;
+ case X264_OPT_DEBLOCK:
+ x264_update_deblock(ud, val);
+ break;
+ }
+ x264_opt_map[jj].found = TRUE;
+ g_free(val);
+ }
+ }
+ g_strfreev(split);
+}
+
+gchar*
+get_deblock_val(signal_user_data_t *ud)
+{
+ gchar *alpha, *beta;
+ gchar *result;
+ alpha = ghb_settings_get_string(ud->settings, "x264_deblock_alpha");
+ beta = ghb_settings_get_string(ud->settings, "x264_deblock_beta");
+ result = g_strdup_printf("%s,%s", alpha, beta);
+ g_free(alpha);
+ g_free(beta);
+ return result;
+}
+
+static void
+x264_opt_update(signal_user_data_t *ud, GtkWidget *widget)
+{
+ gint jj;
+ const gchar *name = gtk_widget_get_name(widget);
+ gchar **opt_syns = NULL;
+ const gchar *def_val = NULL;
+ gint type;
+
+ for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
+ {
+ if (strcmp(name, x264_opt_map[jj].name) == 0)
+ {
+ // found the options that needs updating
+ opt_syns = x264_opt_map[jj].opt_syns;
+ def_val = x264_opt_map[jj].def_val;
+ type = x264_opt_map[jj].type;
+ break;
+ }
+ }
+ if (opt_syns != NULL)
+ {
+ GString *x264opts = g_string_new("");
+ gchar *options;
+ gchar **split = NULL;
+ gint ii;
+ gboolean foundit = FALSE;
+
+ options = ghb_settings_get_string(ud->settings, "x264_options");
+ if (options)
+ {
+ split = g_strsplit(options, ":", -1);
+ g_free(options);
+ }
+ for (ii = 0; split && split[ii] != NULL; ii++)
+ {
+ gint syn;
+ gchar *val = NULL;
+ gchar *pos = strchr(split[ii], '=');
+ if (pos != NULL)
+ {
+ val = pos + 1;
+ *pos = 0;
+ }
+ syn = find_syn_match(split[ii], opt_syns);
+ if (syn >= 0)
+ { // Updating this option
+ gchar *val;
+ foundit = TRUE;
+ if (type == X264_OPT_DEBLOCK)
+ val = get_deblock_val(ud);
+ else
+ {
+ GValue *gval;
+ gval = ghb_widget_value(widget);
+ if (G_VALUE_TYPE(gval) == G_TYPE_BOOLEAN)
+ {
+ if (ghb_value_boolean(gval))
+ val = g_strdup("1");
+ else
+ val = g_strdup("0");
+ }
+ else
+ {
+ val = ghb_widget_string(widget);
+ }
+ ghb_value_free(gval);
+ }
+ if (strcmp(def_val, val) != 0)
+ {
+ g_string_append_printf(x264opts, "%s=%s:", opt_syns[syn], val);
+ }
+ g_free(val);
+ }
+ else if (val != NULL)
+ g_string_append_printf(x264opts, "%s=%s:", split[ii], val);
+ else
+ g_string_append_printf(x264opts, "%s:", split[ii]);
+
+ }
+ if (split) g_strfreev(split);
+ if (!foundit)
+ {
+ gchar *val;
+ if (type == X264_OPT_DEBLOCK)
+ val = get_deblock_val(ud);
+ else
+ {
+ GValue *gval;
+ gval = ghb_widget_value(widget);
+ if (G_VALUE_TYPE(gval) == G_TYPE_BOOLEAN)
+ {
+ if (ghb_value_boolean(gval))
+ val = g_strdup("1");
+ else
+ val = g_strdup("0");
+ }
+ else
+ {
+ val = ghb_widget_string(widget);
+ }
+ ghb_value_free(gval);
+ }
+ if (strcmp(def_val, val) != 0)
+ {
+ g_string_append_printf(x264opts, "%s=%s:", opt_syns[0], val);
+ }
+ g_free(val);
+ }
+ // Update the options value
+ // strip the trailing ":"
+ gchar *result;
+ gint len;
+ result = g_string_free(x264opts, FALSE);
+ len = strlen(result);
+ if (len > 0) result[len - 1] = 0;
+ gchar *sopts;
+ sopts = sanitize_x264opts(ud, result);
+ ghb_ui_update(ud, "x264_options", ghb_string_value(sopts));
+ ghb_x264_parse_options(ud, sopts);
+ g_free(sopts);
+ g_free(result);
+ }
+}
+
+static void
+x264_remove_opt(gchar **opts, gchar **opt_syns)
+{
+ gint ii;
+ for (ii = 0; opts[ii] != NULL; ii++)
+ {
+ gchar *opt;
+ opt = g_strdup(opts[ii]);
+ gchar *pos = strchr(opt, '=');
+ if (pos != NULL)
+ {
+ *pos = 0;
+ }
+ if (find_syn_match(opt, opt_syns) >= 0)
+ {
+ // Mark as deleted
+ opts[ii][0] = 0;
+ }
+ g_free(opt);
+ }
+}
+
+// Construct the x264 options string
+// The result is allocated, so someone must free it at some point.
+static gchar*
+sanitize_x264opts(signal_user_data_t *ud, const gchar *options)
+{
+ GString *x264opts = g_string_new("");
+ gchar **split = g_strsplit(options, ":", -1);
+
+ // Remove entries that match the defaults
+ gint ii;
+ for (ii = 0; split[ii] != NULL; ii++)
+ {
+ gchar *val = NULL;
+ gchar *opt = g_strdup(split[ii]);
+ gchar *pos = strchr(opt, '=');
+ if (pos != NULL)
+ {
+ val = pos + 1;
+ *pos = 0;
+ }
+ else
+ {
+ val = "1";
+ }
+ const gchar *def_val = x264_opt_get_default(opt);
+ if (strcmp(val, def_val) == 0)
+ {
+ // Matches the default, so remove it
+ split[ii][0] = 0;
+ }
+ g_free(opt);
+ }
+ gint refs = ghb_settings_get_int(ud->settings, "x264_refs");
+ if (refs <= 1)
+ {
+ x264_remove_opt(split, x264_mixed_syns);
+ }
+ gint subme;
+
+ subme = ghb_settings_combo_int(ud->settings, "x264_subme");
+ if (subme < 6)
+ {
+ x264_remove_opt(split, x264_brdo_syns);
+ }
+ gint bframes = ghb_settings_get_int(ud->settings, "x264_bframes");
+ if (bframes == 0)
+ {
+ x264_remove_opt(split, x264_weightb_syns);
+ x264_remove_opt(split, x264_brdo_syns);
+ x264_remove_opt(split, x264_bime_syns);
+ x264_remove_opt(split, x264_direct_syns);
+ }
+ if (bframes <= 1)
+ {
+ x264_remove_opt(split, x264_bpyramid_syns);
+ }
+ gchar *me = ghb_settings_get_string(ud->settings, "x264_me");
+ if (!(strcmp(me, "umh") == 0 || strcmp(me, "esa") == 0))
+ {
+ x264_remove_opt(split, x264_merange_syns);
+ }
+ g_free(me);
+ if (!ghb_settings_get_boolean(ud->settings, "x264_cabac"))
+ {
+ x264_remove_opt(split, x264_trellis_syns);
+ }
+ for (ii = 0; split[ii] != NULL; ii++)
+ {
+ if (split[ii][0] != 0)
+ g_string_append_printf(x264opts, "%s:", split[ii]);
+ }
+ g_strfreev(split);
+ // strip the trailing ":"
+ gchar *result;
+ gint len;
+ result = g_string_free(x264opts, FALSE);
+ len = strlen(result);
+ if (len > 0) result[len - 1] = 0;
+ return result;
+}
+
diff --git a/gtk/src/x264handler.h b/gtk/src/x264handler.h
new file mode 100644
index 000000000..89e906ab3
--- /dev/null
+++ b/gtk/src/x264handler.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * x264handler.h
+ * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
+ *
+ * x264handler.h is free software.
+ *
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * callbacks.h is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with callbacks.h. If not, write to:
+ * The Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+
+#if !defined(_X264HANDLER_H_)
+#define _X264HANDLER_H_
+
+#include "settings.h"
+
+void ghb_x264_parse_options(signal_user_data_t *ud, const gchar *options);
+
+#endif // _X264HANDLER_H_