1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
6 * x264handler.c is free software.
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
18 #include "callbacks.h"
20 #include "hb-backend.h"
21 #include "x264handler.h"
23 gint ghb_lookup_bframes(const gchar *options);
24 static void x264_opt_update(signal_user_data_t *ud, GtkWidget *widget);
25 static gchar* sanitize_x264opts(signal_user_data_t *ud, const gchar *options);
27 // Flag needed to prevent x264 options processing from chasing its tail
28 static gboolean ignore_options_update = FALSE;
31 x264_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
33 ghb_widget_to_setting(ud->settings, widget);
34 if (!ignore_options_update)
36 ignore_options_update = TRUE;
37 x264_opt_update(ud, widget);
38 ignore_options_update = FALSE;
40 ghb_check_dependency(ud, widget, NULL);
41 ghb_clear_presets_selection(ud);
45 x264_me_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
49 ghb_widget_to_setting(ud->settings, widget);
50 if (!ignore_options_update)
52 ignore_options_update = TRUE;
53 x264_opt_update(ud, widget);
54 ignore_options_update = FALSE;
56 ghb_check_dependency(ud, widget, NULL);
57 ghb_clear_presets_selection(ud);
58 widget = GHB_WIDGET(ud->builder, "x264_merange");
59 me = ghb_settings_combo_int(ud->settings, "x264_me");
63 gtk_spin_button_set_range(GTK_SPIN_BUTTON(widget), 4, 16);
68 gtk_spin_button_set_range(GTK_SPIN_BUTTON(widget), 4, 64);
73 x264_entry_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
75 g_debug("x264_entry_changed_cb ()");
76 if (!ignore_options_update)
81 textview = GTK_WIDGET(GHB_WIDGET(ud->builder, "x264Option"));
82 ghb_widget_to_setting(ud->settings, textview);
83 options = ghb_settings_get_string(ud->settings, "x264Option");
84 ignore_options_update = TRUE;
85 ghb_x264_parse_options(ud, options);
86 if (!GTK_WIDGET_HAS_FOCUS(textview))
90 sopts = sanitize_x264opts(ud, options);
91 ghb_ui_update(ud, "x264Option", ghb_string_value(sopts));
92 ghb_x264_parse_options(ud, sopts);
96 ignore_options_update = FALSE;
100 G_MODULE_EXPORT gboolean
101 x264_focus_out_cb(GtkWidget *widget, GdkEventFocus *event,
102 signal_user_data_t *ud)
104 gchar *options, *sopts;
106 ghb_widget_to_setting(ud->settings, widget);
107 options = ghb_settings_get_string(ud->settings, "x264Option");
108 sopts = sanitize_x264opts(ud, options);
109 ignore_options_update = TRUE;
110 if (sopts != NULL && strcmp(sopts, options) != 0)
112 ghb_ui_update(ud, "x264Option", ghb_string_value(sopts));
113 ghb_x264_parse_options(ud, sopts);
117 ignore_options_update = FALSE;
133 struct x264_opt_map_s
142 static gchar *x264_ref_syns[] = {"ref", "frameref", NULL};
143 static gchar *x264_mixed_syns[] = {"mixed-refs", "mixed_refs", NULL};
144 static gchar *x264_bframes_syns[] = {"bframes", NULL};
145 static gchar *x264_badapt_syns[] = {"b-adapt", "b_adapt", NULL};
146 static gchar *x264_direct_syns[] =
147 {"direct", "direct-pred", "direct_pred", NULL};
148 static gchar *x264_weightb_syns[] = {"weightb", "weight-b", "weight_b", NULL};
149 static gchar *x264_bpyramid_syns[] = {"b-pyramid", "b_pyramid", NULL};
150 static gchar *x264_me_syns[] = {"me", NULL};
151 static gchar *x264_merange_syns[] = {"merange", "me-range", "me_range", NULL};
152 static gchar *x264_subme_syns[] = {"subme", "subq", NULL};
153 static gchar *x264_aqmode_syns[] = {"aq-mode", NULL};
154 static gchar *x264_analyse_syns[] = {"analyse", "partitions", NULL};
155 static gchar *x264_8x8dct_syns[] = {"8x8dct", NULL};
156 static gchar *x264_deblock_syns[] = {"deblock", "filter", NULL};
157 static gchar *x264_trellis_syns[] = {"trellis", NULL};
158 static gchar *x264_pskip_syns[] = {"no-fast-pskip", "no_fast_pskip", NULL};
159 static gchar *x264_psy_syns[] = {"psy-rd", "psy_rd", NULL};
160 static gchar *x264_mbtree_syns[] = {"mbtree", NULL};
161 static gchar *x264_decimate_syns[] =
162 {"no-dct-decimate", "no_dct_decimate", NULL};
163 static gchar *x264_cabac_syns[] = {"cabac", NULL};
166 find_syn_match(const gchar *opt, gchar **syns)
169 for (ii = 0; syns[ii] != NULL; ii++)
171 if (strcmp(opt, syns[ii]) == 0)
177 struct x264_opt_map_s x264_opt_map[] =
179 {x264_ref_syns, "x264_refs", "3", X264_OPT_INT},
180 {x264_mixed_syns, "x264_mixed_refs", "1", X264_OPT_BOOL},
181 {x264_bframes_syns, "x264_bframes", "3", X264_OPT_INT},
182 {x264_direct_syns, "x264_direct", "spatial", X264_OPT_COMBO},
183 {x264_badapt_syns, "x264_b_adapt", "1", X264_OPT_COMBO},
184 {x264_weightb_syns, "x264_weighted_bframes", "1", X264_OPT_BOOL},
185 {x264_bpyramid_syns, "x264_bpyramid", "0", X264_OPT_BOOL},
186 {x264_me_syns, "x264_me", "hex", X264_OPT_COMBO},
187 {x264_merange_syns, "x264_merange", "16", X264_OPT_INT},
188 {x264_subme_syns, "x264_subme", "7", X264_OPT_COMBO},
189 {x264_aqmode_syns, "x264_aqmode", "1", X264_OPT_INT_NONE},
190 {x264_analyse_syns, "x264_analyse", "some", X264_OPT_COMBO},
191 {x264_8x8dct_syns, "x264_8x8dct", "1", X264_OPT_BOOL},
192 {x264_deblock_syns, "x264_deblock_alpha", "0,0", X264_OPT_DEBLOCK},
193 {x264_deblock_syns, "x264_deblock_beta", "0,0", X264_OPT_DEBLOCK},
194 {x264_trellis_syns, "x264_trellis", "1", X264_OPT_COMBO},
195 {x264_pskip_syns, "x264_no_fast_pskip", "0", X264_OPT_BOOL},
196 {x264_decimate_syns, "x264_no_dct_decimate", "0", X264_OPT_BOOL},
197 {x264_cabac_syns, "x264_cabac", "1", X264_OPT_BOOL},
198 {x264_psy_syns, "x264_psy_rd", "1,0", X264_OPT_PSY},
199 {x264_psy_syns, "x264_psy_trell", "1,0", X264_OPT_PSY},
200 {x264_mbtree_syns, "x264_mbtree", "1", X264_OPT_BOOL_NONE},
202 #define X264_OPT_MAP_SIZE (sizeof(x264_opt_map)/sizeof(struct x264_opt_map_s))
205 x264_opt_get_default(const gchar *opt)
208 for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
210 if (find_syn_match(opt, x264_opt_map[jj].opt_syns) >= 0)
212 return x264_opt_map[jj].def_val;
219 x264_update_int(signal_user_data_t *ud, const gchar *name, const gchar *val)
223 if (val == NULL) return;
224 ival = g_strtod (val, NULL);
225 ghb_ui_update(ud, name, ghb_int64_value(ival));
229 x264_update_int_setting(signal_user_data_t *ud, const gchar *name, const gchar *val)
233 if (val == NULL) return;
234 ival = g_strtod (val, NULL);
235 ghb_settings_set_value(ud->settings, name, ghb_int64_value(ival));
236 ghb_check_dependency(ud, NULL, name);
239 static gchar *true_str[] =
248 str_is_true(const gchar *str)
251 for (ii = 0; true_str[ii]; ii++)
253 if (g_ascii_strcasecmp(str, true_str[ii]) == 0)
260 x264_update_bool(signal_user_data_t *ud, const gchar *name, const gchar *val)
263 ghb_ui_update(ud, name, ghb_boolean_value(1));
265 ghb_ui_update(ud, name, ghb_boolean_value(str_is_true(val)));
269 x264_update_bool_setting(signal_user_data_t *ud, const gchar *name, const gchar *val)
272 ghb_settings_set_value(ud->settings, name, ghb_boolean_value(1));
274 ghb_settings_set_value(ud->settings, name, ghb_boolean_value(str_is_true(val)));
276 ghb_check_dependency(ud, NULL, name);
280 x264_update_combo(signal_user_data_t *ud, const gchar *name, const gchar *val)
286 gboolean foundit = FALSE;
289 if (val == NULL) return;
290 widget = GHB_WIDGET(ud->builder, name);
293 g_debug("Failed to find widget for key: %s\n", name);
296 store = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
297 if (gtk_tree_model_get_iter_first (store, &iter))
301 gtk_tree_model_get(store, &iter, 2, &shortOpt, 3, &ivalue, -1);
302 if (strcmp(shortOpt, val) == 0)
304 gtk_combo_box_set_active_iter (GTK_COMBO_BOX(widget), &iter);
310 } while (gtk_tree_model_iter_next (store, &iter));
314 if (gtk_tree_model_get_iter_first (store, &iter))
318 gtk_tree_model_get(store, &iter, 2, &shortOpt, 3, &ivalue, -1);
319 if (strcmp(shortOpt, "custom") == 0)
321 gtk_list_store_set(GTK_LIST_STORE(store), &iter, 4, val, -1);
322 gtk_combo_box_set_active_iter (GTK_COMBO_BOX(widget), &iter);
328 } while (gtk_tree_model_iter_next (store, &iter));
331 // Its possible the value hasn't changed. Since settings are only
332 // updated when the value changes, I'm initializing settings here as well.
333 ghb_widget_to_setting(ud->settings, widget);
337 x264_update_deblock(signal_user_data_t *ud, const gchar *xval)
339 gdouble avalue, bvalue;
344 if (xval == NULL) return;
345 val = g_strdup(xval);
349 gchar *pos = strchr(val, ',');
355 avalue = g_strtod (val, &end);
358 bvalue = g_strtod (bval, &end);
362 ghb_ui_update(ud, "x264_deblock_alpha", ghb_int64_value(avalue));
363 ghb_ui_update(ud, "x264_deblock_beta", ghb_int64_value(bvalue));
367 x264_parse_psy(const gchar *psy, gdouble *psy_rd, gdouble *psy_trell)
370 gchar *trell_val = NULL;
375 if (psy == NULL) return;
377 gchar *pos = strchr(val, ',');
383 *psy_rd = g_strtod (val, &end);
384 if (trell_val != NULL)
386 *psy_trell = g_strtod (trell_val, &end);
392 x264_update_psy(signal_user_data_t *ud, const gchar *xval)
394 gdouble rd_value, trell_value;
396 if (xval == NULL) return;
397 x264_parse_psy(xval, &rd_value, &trell_value);
398 ghb_ui_update(ud, "x264_psy_rd", ghb_double_value(rd_value));
399 ghb_ui_update(ud, "x264_psy_trell", ghb_double_value(trell_value));
403 ghb_x264_parse_options(signal_user_data_t *ud, const gchar *options)
405 gchar **split = g_strsplit(options, ":", -1);
406 if (split == NULL) return;
411 for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
412 x264_opt_map[jj].found = FALSE;
414 for (ii = 0; split[ii] != NULL; ii++)
417 gchar *pos = strchr(split[ii], '=');
423 for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
425 if (find_syn_match(split[ii], x264_opt_map[jj].opt_syns) >= 0)
427 x264_opt_map[jj].found = TRUE;
428 switch(x264_opt_map[jj].type)
431 x264_update_int(ud, x264_opt_map[jj].name, val);
434 x264_update_bool(ud, x264_opt_map[jj].name, val);
437 x264_update_combo(ud, x264_opt_map[jj].name, val);
439 case X264_OPT_DEBLOCK:
440 // dirty little hack. mark deblock_beta found as well
441 x264_opt_map[jj+1].found = TRUE;
442 x264_update_deblock(ud, val);
445 // dirty little hack. mark psy_trell found as well
446 x264_opt_map[jj+1].found = TRUE;
447 x264_update_psy(ud, val);
449 case X264_OPT_BOOL_NONE:
450 x264_update_bool_setting(ud, x264_opt_map[jj].name, val);
452 case X264_OPT_INT_NONE:
453 x264_update_int_setting(ud, x264_opt_map[jj].name, val);
460 // For any options not found in the option string, set ui to
462 for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
464 if (!x264_opt_map[jj].found)
466 gchar *val = strdup(x264_opt_map[jj].def_val);
467 switch(x264_opt_map[jj].type)
470 x264_update_int(ud, x264_opt_map[jj].name, val);
473 x264_update_bool(ud, x264_opt_map[jj].name, val);
476 x264_update_combo(ud, x264_opt_map[jj].name, val);
478 case X264_OPT_DEBLOCK:
479 x264_update_deblock(ud, val);
482 x264_update_psy(ud, val);
484 case X264_OPT_BOOL_NONE:
485 x264_update_bool_setting(ud, x264_opt_map[jj].name, val);
487 case X264_OPT_INT_NONE:
488 x264_update_int_setting(ud, x264_opt_map[jj].name, val);
491 x264_opt_map[jj].found = TRUE;
499 get_deblock_val(signal_user_data_t *ud)
503 alpha = ghb_settings_get_string(ud->settings, "x264_deblock_alpha");
504 beta = ghb_settings_get_string(ud->settings, "x264_deblock_beta");
505 result = g_strdup_printf("%s,%s", alpha, beta);
512 get_psy_val(signal_user_data_t *ud)
516 rd = ghb_settings_get_double(ud->settings, "x264_psy_rd");
517 trell = ghb_settings_get_double(ud->settings, "x264_psy_trell");
518 result = g_strdup_printf("%g,%g", rd, trell);
523 x264_opt_update(signal_user_data_t *ud, GtkWidget *widget)
526 const gchar *name = gtk_widget_get_name(widget);
527 gchar **opt_syns = NULL;
528 const gchar *def_val = NULL;
531 for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
533 if (strcmp(name, x264_opt_map[jj].name) == 0)
535 // found the options that needs updating
536 opt_syns = x264_opt_map[jj].opt_syns;
537 def_val = x264_opt_map[jj].def_val;
538 type = x264_opt_map[jj].type;
542 if (opt_syns != NULL)
544 GString *x264opts = g_string_new("");
546 gchar **split = NULL;
548 gboolean foundit = FALSE;
550 options = ghb_settings_get_string(ud->settings, "x264Option");
553 split = g_strsplit(options, ":", -1);
556 for (ii = 0; split && split[ii] != NULL; ii++)
560 gchar *pos = strchr(split[ii], '=');
566 syn = find_syn_match(split[ii], opt_syns);
568 { // Updating this option
571 if (type == X264_OPT_DEBLOCK)
572 val = get_deblock_val(ud);
573 else if (type == X264_OPT_PSY)
574 val = get_psy_val(ud);
578 gval = ghb_widget_value(widget);
579 if (G_VALUE_TYPE(gval) == G_TYPE_BOOLEAN)
581 if (ghb_value_boolean(gval))
588 val = ghb_widget_string(widget);
590 ghb_value_free(gval);
592 if (strcmp(def_val, val) != 0)
594 g_string_append_printf(x264opts, "%s=%s:", opt_syns[syn], val);
598 else if (val != NULL)
599 g_string_append_printf(x264opts, "%s=%s:", split[ii], val);
601 g_string_append_printf(x264opts, "%s:", split[ii]);
604 if (split) g_strfreev(split);
608 if (type == X264_OPT_DEBLOCK)
609 val = get_deblock_val(ud);
610 else if (type == X264_OPT_PSY)
611 val = get_psy_val(ud);
615 gval = ghb_widget_value(widget);
616 if (G_VALUE_TYPE(gval) == G_TYPE_BOOLEAN)
618 if (ghb_value_boolean(gval))
625 val = ghb_widget_string(widget);
627 ghb_value_free(gval);
629 if (strcmp(def_val, val) != 0)
631 g_string_append_printf(x264opts, "%s=%s:", opt_syns[0], val);
635 // Update the options value
636 // strip the trailing ":"
639 result = g_string_free(x264opts, FALSE);
640 len = strlen(result);
641 if (len > 0) result[len - 1] = 0;
643 sopts = sanitize_x264opts(ud, result);
644 ghb_ui_update(ud, "x264Option", ghb_string_value(sopts));
645 ghb_x264_parse_options(ud, sopts);
652 x264_find_opt(gchar **opts, gchar **opt_syns)
655 for (ii = 0; opts[ii] != NULL; ii++)
658 opt = g_strdup(opts[ii]);
659 gchar *pos = strchr(opt, '=');
664 if (find_syn_match(opt, opt_syns) >= 0)
675 x264_remove_opt(gchar **opts, gchar **opt_syns)
678 for (ii = 0; opts[ii] != NULL; ii++)
681 opt = g_strdup(opts[ii]);
682 gchar *pos = strchr(opt, '=');
687 if (find_syn_match(opt, opt_syns) >= 0)
697 x264_lookup_value(gchar **opts, gchar **opt_syns)
702 const gchar *def_val = x264_opt_get_default(opt_syns[0]);
704 pos = x264_find_opt(opts, opt_syns);
707 gchar *cpos = strchr(opts[pos], '=');
710 ret = g_strdup(cpos+1);
717 else if (def_val != NULL)
719 ret = g_strdup(def_val);
725 ghb_lookup_badapt(const gchar *options)
734 split = g_strsplit(options, ":", -1);
736 result = x264_lookup_value(split, x264_badapt_syns);
740 ret = g_strtod(result, NULL);
747 ghb_lookup_aqmode(const gchar *options)
756 split = g_strsplit(options, ":", -1);
758 result = x264_lookup_value(split, x264_aqmode_syns);
762 ret = g_strtod(result, NULL);
769 ghb_lookup_bframes(const gchar *options)
778 split = g_strsplit(options, ":", -1);
780 result = x264_lookup_value(split, x264_bframes_syns);
784 ret = g_strtod(result, NULL);
791 ghb_lookup_mbtree(const gchar *options)
793 gint ret = ghb_lookup_bframes(options) != 0;
800 split = g_strsplit(options, ":", -1);
802 result = x264_lookup_value(split, x264_mbtree_syns);
806 ret = g_strtod(result, NULL);
812 // Construct the x264 options string
813 // The result is allocated, so someone must free it at some point.
815 sanitize_x264opts(signal_user_data_t *ud, const gchar *options)
817 GString *x264opts = g_string_new("");
818 gchar **split = g_strsplit(options, ":", -1);
821 // Fix up option dependencies
822 gint subme = ghb_settings_combo_int(ud->settings, "x264_subme");
825 x264_remove_opt(split, x264_psy_syns);
827 gint trell = ghb_settings_combo_int(ud->settings, "x264_trellis");
830 gint aqmode = ghb_lookup_aqmode(options);
831 if (trell != 2 || aqmode == 0)
833 gint pos = x264_find_opt(split, x264_subme_syns);
835 split[pos] = g_strdup_printf("subme=9");
841 gdouble psy_rd = 0., psy_trell;
843 psy = x264_find_opt(split, x264_psy_syns);
846 gchar *pos = strchr(split[psy], '=');
849 x264_parse_psy(pos+1, &psy_rd, &psy_trell);
852 split[psy] = g_strdup_printf("psy-rd=%g,0", psy_rd);
855 gint refs = ghb_settings_get_int(ud->settings, "x264_refs");
858 x264_remove_opt(split, x264_mixed_syns);
860 gint bframes = ghb_settings_get_int(ud->settings, "x264_bframes");
863 x264_remove_opt(split, x264_weightb_syns);
864 x264_remove_opt(split, x264_direct_syns);
865 x264_remove_opt(split, x264_badapt_syns);
869 x264_remove_opt(split, x264_bpyramid_syns);
871 if (!ghb_settings_get_boolean(ud->settings, "x264_cabac"))
873 x264_remove_opt(split, x264_trellis_syns);
875 // Remove entries that match the defaults
876 for (ii = 0; split[ii] != NULL; ii++)
879 gchar *opt = g_strdup(split[ii]);
880 gchar *pos = strchr(opt, '=');
890 const gchar *def_val;
891 def_val = x264_opt_get_default(opt);
892 if (strcmp(val, def_val) == 0)
894 // Matches the default, so remove it
899 for (ii = 0; split[ii] != NULL; ii++)
901 if (split[ii][0] != 0)
902 g_string_append_printf(x264opts, "%s:", split[ii]);
905 // strip the trailing ":"
908 result = g_string_free(x264opts, FALSE);
909 len = strlen(result);
910 if (len > 0) result[len - 1] = 0;