OSDN Git Service

LinGui: Make preset key/values mostly align with macui presets.
[handbrake-jp/handbrake-jp-git.git] / gtk / src / x264handler.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * x264handler.c
4  * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
5  * 
6  * x264handler.c is free software.
7  * 
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)
11  * any later version.
12  */
13
14 #include <gtk/gtk.h>
15 #include <string.h>
16 #include "settings.h"
17 #include "values.h"
18 #include "callbacks.h"
19 #include "presets.h"
20 #include "x264handler.h"
21
22 static void x264_opt_update(signal_user_data_t *ud, GtkWidget *widget);
23 static gchar* sanitize_x264opts(signal_user_data_t *ud, const gchar *options);
24
25 // Flag needed to prevent x264 options processing from chasing its tail
26 static gboolean ignore_options_update = FALSE;
27
28 void
29 x264_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
30 {
31         ghb_widget_to_setting(ud->settings, widget);
32         if (!ignore_options_update)
33         {
34                 ignore_options_update = TRUE;
35                 x264_opt_update(ud, widget);
36                 ignore_options_update = FALSE;
37         }
38         ghb_check_dependency(ud, widget);
39         ghb_clear_presets_selection(ud);
40 }
41
42 void
43 x264_entry_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
44 {
45         g_debug("x264_entry_changed_cb ()");
46         if (!ignore_options_update)
47         {
48                 GtkWidget *textview;
49                 gchar *options;
50
51                 textview = GTK_WIDGET(GHB_WIDGET(ud->builder, "x264Option"));
52                 ghb_widget_to_setting(ud->settings, textview);
53                 options = ghb_settings_get_string(ud->settings, "x264Option");
54                 ignore_options_update = TRUE;
55                 ghb_x264_parse_options(ud, options);
56                 if (!GTK_WIDGET_HAS_FOCUS(textview))
57                 {
58                         gchar *sopts;
59
60                         sopts = sanitize_x264opts(ud, options);
61                         ghb_ui_update(ud, "x264Option", ghb_string_value(sopts));
62                         ghb_x264_parse_options(ud, sopts);
63                         g_free(sopts);
64                 }
65                 g_free(options);
66                 ignore_options_update = FALSE;
67         }
68 }
69
70 gboolean
71 x264_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, 
72         signal_user_data_t *ud)
73 {
74         gchar *options, *sopts;
75
76         ghb_widget_to_setting(ud->settings, widget);
77         options = ghb_settings_get_string(ud->settings, "x264Option");
78         sopts = sanitize_x264opts(ud, options);
79         ignore_options_update = TRUE;
80         if (sopts != NULL)
81         {
82                 ghb_ui_update(ud, "x264Option", ghb_string_value(sopts));
83                 ghb_x264_parse_options(ud, sopts);
84         }
85         g_free(options);
86         g_free(sopts);
87         ignore_options_update = FALSE;
88         return FALSE;
89 }
90
91 enum
92 {
93         X264_OPT_DEBLOCK,
94         X264_OPT_INT,
95         X264_OPT_COMBO,
96         X264_OPT_BOOL,
97 };
98
99 struct x264_opt_map_s
100 {
101         gchar **opt_syns;
102         gchar *name;
103         gchar *def_val;
104         gint type;
105         gboolean found;
106 };
107
108 static gchar *x264_ref_syns[] = {"ref", "frameref", NULL};
109 static gchar *x264_mixed_syns[] = {"mixed-refs", "mixed_refs", NULL};
110 static gchar *x264_bframes_syns[] = {"bframes", NULL};
111 static gchar *x264_direct_syns[] = 
112         {"direct", "direct-pred", "direct_pred", NULL};
113 static gchar *x264_weightb_syns[] = {"weightb", "weight-b", "weight_b", NULL};
114 static gchar *x264_bpyramid_syns[] = {"b-pyramid", "b_pyramid", NULL};
115 static gchar *x264_me_syns[] = {"me", NULL};
116 static gchar *x264_merange_syns[] = {"merange", "me-range", "me_range", NULL};
117 static gchar *x264_subme_syns[] = {"subme", "subq", NULL};
118 static gchar *x264_analyse_syns[] = {"analyse", "partitions", NULL};
119 static gchar *x264_8x8dct_syns[] = {"8x8dct", NULL};
120 static gchar *x264_deblock_syns[] = {"deblock", "filter", NULL};
121 static gchar *x264_trellis_syns[] = {"trellis", NULL};
122 static gchar *x264_pskip_syns[] = {"no-fast-pskip", "no_fast_pskip", NULL};
123 static gchar *x264_decimate_syns[] = 
124         {"no-dct-decimate", "no_dct_decimate", NULL};
125 static gchar *x264_cabac_syns[] = {"cabac", NULL};
126
127 static gint
128 find_syn_match(const gchar *opt, gchar **syns)
129 {
130         gint ii;
131         for (ii = 0; syns[ii] != NULL; ii++)
132         {
133                 if (strcmp(opt, syns[ii]) == 0)
134                         return ii;
135         }
136         return -1;
137 }
138
139 struct x264_opt_map_s x264_opt_map[] =
140 {
141         {x264_ref_syns, "x264_refs", "1", X264_OPT_INT},
142         {x264_mixed_syns, "x264_mixed_refs", "0", X264_OPT_BOOL},
143         {x264_bframes_syns, "x264_bframes", "0", X264_OPT_INT},
144         {x264_direct_syns, "x264_direct", "spatial", X264_OPT_COMBO},
145         {x264_weightb_syns, "x264_weighted_bframes", "0", X264_OPT_BOOL},
146         {x264_bpyramid_syns, "x264_bpyramid", "0", X264_OPT_BOOL},
147         {x264_me_syns, "x264_me", "hex", X264_OPT_COMBO},
148         {x264_merange_syns, "x264_merange", "16", X264_OPT_INT},
149         {x264_subme_syns, "x264_subme", "6", X264_OPT_COMBO},
150         {x264_analyse_syns, "x264_analyse", "some", X264_OPT_COMBO},
151         {x264_8x8dct_syns, "x264_8x8dct", "0", X264_OPT_BOOL},
152         {x264_deblock_syns, "x264_deblock_alpha", "0,0", X264_OPT_DEBLOCK},
153         {x264_deblock_syns, "x264_deblock_beta", "0,0", X264_OPT_DEBLOCK},
154         {x264_trellis_syns, "x264_trellis", "0", X264_OPT_COMBO},
155         {x264_pskip_syns, "x264_no_fast_pskip", "0", X264_OPT_BOOL},
156         {x264_decimate_syns, "x264_no_dct_decimate", "0", X264_OPT_BOOL},
157         {x264_cabac_syns, "x264_cabac", "1", X264_OPT_BOOL},
158 };
159 #define X264_OPT_MAP_SIZE (sizeof(x264_opt_map)/sizeof(struct x264_opt_map_s))
160
161 static const gchar*
162 x264_opt_get_default(const gchar *opt)
163 {
164         gint jj;
165         for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
166         {
167                 if (find_syn_match(opt, x264_opt_map[jj].opt_syns) >= 0)
168                 {
169                         return x264_opt_map[jj].def_val;
170                 }
171         }
172         return "";
173 }
174
175 static void
176 x264_update_int(signal_user_data_t *ud, const gchar *name, const gchar *val)
177 {
178         gint ival;
179
180         if (val == NULL) return;
181         ival = g_strtod (val, NULL);
182         ghb_ui_update(ud, name, ghb_int64_value(ival));
183 }
184
185 static gchar *true_str[] =
186 {
187         "true",
188         "yes",
189         "1",
190         NULL
191 };
192
193 static gboolean 
194 str_is_true(const gchar *str)
195 {
196         gint ii;
197         for (ii = 0; true_str[ii]; ii++)
198         {
199                 if (g_ascii_strcasecmp(str, true_str[ii]) == 0)
200                         return TRUE;
201         }
202         return FALSE;
203 }
204
205 static void
206 x264_update_bool(signal_user_data_t *ud, const gchar *name, const gchar *val)
207 {
208         if (val == NULL)
209                 ghb_ui_update(ud, name, ghb_boolean_value(1));
210         else
211                 ghb_ui_update(ud, name, ghb_boolean_value(str_is_true(val)));
212 }
213
214 static void
215 x264_update_combo(signal_user_data_t *ud, const gchar *name, const gchar *val)
216 {
217         GtkTreeModel *store;
218         GtkTreeIter iter;
219         gchar *shortOpt;
220         gint ivalue;
221         gboolean foundit = FALSE;
222         GtkWidget *widget;
223
224         if (val == NULL) return;
225         widget = GHB_WIDGET(ud->builder, name);
226         if (widget == NULL)
227         {
228                 g_debug("Failed to find widget for key: %s\n", name);
229                 return;
230         }
231         store = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
232         if (gtk_tree_model_get_iter_first (store, &iter))
233         {
234                 do
235                 {
236                         gtk_tree_model_get(store, &iter, 2, &shortOpt, 3, &ivalue, -1);
237                         if (strcmp(shortOpt, val) == 0)
238                         {
239                                 gtk_combo_box_set_active_iter (GTK_COMBO_BOX(widget), &iter);
240                                 g_free(shortOpt);
241                                 foundit = TRUE;
242                                 break;
243                         }
244                         g_free(shortOpt);
245                 } while (gtk_tree_model_iter_next (store, &iter));
246         }
247         if (!foundit)
248         {
249                 if (gtk_tree_model_get_iter_first (store, &iter))
250                 {
251                         do
252                         {
253                                 gtk_tree_model_get(store, &iter, 2, &shortOpt, 3, &ivalue, -1);
254                                 if (strcmp(shortOpt, "custom") == 0)
255                                 {
256                                         gtk_list_store_set(GTK_LIST_STORE(store), &iter, 4, val, -1);
257                                         gtk_combo_box_set_active_iter (GTK_COMBO_BOX(widget), &iter);
258                                         g_free(shortOpt);
259                                         foundit = TRUE;
260                                         break;
261                                 }
262                                 g_free(shortOpt);
263                         } while (gtk_tree_model_iter_next (store, &iter));
264                 }
265         }
266         // Its possible the value hasn't changed. Since settings are only
267         // updated when the value changes, I'm initializing settings here as well.
268         ghb_widget_to_setting(ud->settings, widget);
269 }
270
271 static void
272 x264_update_deblock(signal_user_data_t *ud, const gchar *xval)
273 {
274         gdouble avalue, bvalue;
275         gchar *end;
276         gchar *val;
277         gchar *bval = NULL;
278
279         if (xval == NULL) return;
280         val = g_strdup(xval);
281         bvalue = avalue = 0;
282         if (val != NULL) 
283         {
284                 gchar *pos = strchr(val, ',');
285                 if (pos != NULL)
286                 {
287                         bval = pos + 1;
288                         *pos = 0;
289                 }
290                 avalue = g_strtod (val, &end);
291                 if (bval != NULL)
292                 {
293                         bvalue = g_strtod (bval, &end);
294                 }
295         }
296         g_free(val);
297         ghb_ui_update(ud, "x264_deblock_alpha", ghb_int64_value(avalue));
298         ghb_ui_update(ud, "x264_deblock_beta", ghb_int64_value(bvalue));
299 }
300
301 void
302 ghb_x264_parse_options(signal_user_data_t *ud, const gchar *options)
303 {
304         gchar **split = g_strsplit(options, ":", -1);
305         if (split == NULL) return;
306
307         gint ii;
308         gint jj;
309
310         for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
311                 x264_opt_map[jj].found = FALSE;
312
313         for (ii = 0; split[ii] != NULL; ii++)
314         {
315                 gchar *val = NULL;
316                 gchar *pos = strchr(split[ii], '=');
317                 if (pos != NULL)
318                 {
319                         val = pos + 1;
320                         *pos = 0;
321                 }
322                 for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
323                 {
324                         if (find_syn_match(split[ii], x264_opt_map[jj].opt_syns) >= 0)
325                         {
326                                 x264_opt_map[jj].found = TRUE;
327                                 switch(x264_opt_map[jj].type)
328                                 {
329                                 case X264_OPT_INT:
330                                         x264_update_int(ud, x264_opt_map[jj].name, val);
331                                         break;
332                                 case X264_OPT_BOOL:
333                                         x264_update_bool(ud, x264_opt_map[jj].name, val);
334                                         break;
335                                 case X264_OPT_COMBO:
336                                         x264_update_combo(ud, x264_opt_map[jj].name, val);
337                                         break;
338                                 case X264_OPT_DEBLOCK:
339                                         // dirty little hack.  mark deblock_beta found as well
340                                         x264_opt_map[jj+1].found = TRUE;
341                                         x264_update_deblock(ud, val);
342                                         break;
343                                 }
344                                 break;
345                         }
346                 }
347         }
348         // For any options not found in the option string, set ui to
349         // default values
350         for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
351         {
352                 if (!x264_opt_map[jj].found)
353                 {
354                         gchar *val = strdup(x264_opt_map[jj].def_val);
355                         switch(x264_opt_map[jj].type)
356                         {
357                         case X264_OPT_INT:
358                                 x264_update_int(ud, x264_opt_map[jj].name, val);
359                                 break;
360                         case X264_OPT_BOOL:
361                                 x264_update_bool(ud, x264_opt_map[jj].name, val);
362                                 break;
363                         case X264_OPT_COMBO:
364                                 x264_update_combo(ud, x264_opt_map[jj].name, val);
365                                 break;
366                         case X264_OPT_DEBLOCK:
367                                 x264_update_deblock(ud, val);
368                                 break;
369                         }
370                         x264_opt_map[jj].found = TRUE;
371                         g_free(val);
372                 }
373         }
374         g_strfreev(split);
375 }
376
377 gchar*
378 get_deblock_val(signal_user_data_t *ud)
379 {
380         gchar *alpha, *beta;
381         gchar *result;
382         alpha = ghb_settings_get_string(ud->settings, "x264_deblock_alpha");
383         beta = ghb_settings_get_string(ud->settings, "x264_deblock_beta");
384         result = g_strdup_printf("%s,%s", alpha, beta);
385         g_free(alpha);
386         g_free(beta);
387         return result;
388 }
389
390 static void
391 x264_opt_update(signal_user_data_t *ud, GtkWidget *widget)
392 {
393         gint jj;
394         const gchar *name = gtk_widget_get_name(widget);
395         gchar **opt_syns = NULL;
396         const gchar *def_val = NULL;
397         gint type;
398
399         for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++)
400         {
401                 if (strcmp(name, x264_opt_map[jj].name) == 0)
402                 {
403                         // found the options that needs updating
404                         opt_syns = x264_opt_map[jj].opt_syns;
405                         def_val = x264_opt_map[jj].def_val;
406                         type = x264_opt_map[jj].type;
407                         break;
408                 }
409         }
410         if (opt_syns != NULL)
411         {
412                 GString *x264opts = g_string_new("");
413                 gchar *options;
414                 gchar **split = NULL;
415                 gint ii;
416                 gboolean foundit = FALSE;
417
418                 options = ghb_settings_get_string(ud->settings, "x264Option");
419                 if (options)
420                 {
421                         split = g_strsplit(options, ":", -1);
422                         g_free(options);
423                 }
424                 for (ii = 0; split && split[ii] != NULL; ii++)
425                 {
426                         gint syn;
427                         gchar *val = NULL;
428                         gchar *pos = strchr(split[ii], '=');
429                         if (pos != NULL)
430                         {
431                                 val = pos + 1;
432                                 *pos = 0;
433                         }
434                         syn = find_syn_match(split[ii], opt_syns);
435                         if (syn >= 0)
436                         { // Updating this option
437                                 gchar *val;
438                                 foundit = TRUE;
439                                 if (type == X264_OPT_DEBLOCK)
440                                         val = get_deblock_val(ud);
441                                 else
442                                 {
443                                         GValue *gval;
444                                         gval = ghb_widget_value(widget);
445                                         if (G_VALUE_TYPE(gval) == G_TYPE_BOOLEAN)
446                                         {
447                                                 if (ghb_value_boolean(gval))
448                                                         val = g_strdup("1");
449                                                 else
450                                                         val = g_strdup("0");
451                                         }
452                                         else
453                                         {
454                                                 val = ghb_widget_string(widget);
455                                         }
456                                         ghb_value_free(gval);
457                                 }
458                                 if (strcmp(def_val, val) != 0)
459                                 {
460                                         g_string_append_printf(x264opts, "%s=%s:", opt_syns[syn], val);
461                                 }
462                                 g_free(val);
463                         }
464                         else if (val != NULL)
465                                 g_string_append_printf(x264opts, "%s=%s:", split[ii], val);
466                         else
467                                 g_string_append_printf(x264opts, "%s:", split[ii]);
468
469                 }
470                 if (split) g_strfreev(split);
471                 if (!foundit)
472                 {
473                         gchar *val;
474                         if (type == X264_OPT_DEBLOCK)
475                                 val = get_deblock_val(ud);
476                         else
477                         {
478                                 GValue *gval;
479                                 gval = ghb_widget_value(widget);
480                                 if (G_VALUE_TYPE(gval) == G_TYPE_BOOLEAN)
481                                 {
482                                         if (ghb_value_boolean(gval))
483                                                 val = g_strdup("1");
484                                         else
485                                                 val = g_strdup("0");
486                                 }
487                                 else
488                                 {
489                                         val = ghb_widget_string(widget);
490                                 }
491                                 ghb_value_free(gval);
492                         }
493                         if (strcmp(def_val, val) != 0)
494                         {
495                                 g_string_append_printf(x264opts, "%s=%s:", opt_syns[0], val);
496                         }
497                         g_free(val);
498                 }
499                 // Update the options value
500                 // strip the trailing ":"
501                 gchar *result;
502                 gint len;
503                 result = g_string_free(x264opts, FALSE);
504                 len = strlen(result);
505                 if (len > 0) result[len - 1] = 0;
506                 gchar *sopts;
507                 sopts = sanitize_x264opts(ud, result);
508                 ghb_ui_update(ud, "x264Option", ghb_string_value(sopts));
509                 ghb_x264_parse_options(ud, sopts);
510                 g_free(sopts);
511                 g_free(result);
512         }
513 }
514
515 static void
516 x264_remove_opt(gchar **opts, gchar **opt_syns)
517 {
518         gint ii;
519         for (ii = 0; opts[ii] != NULL; ii++)
520         {
521                 gchar *opt;
522                 opt = g_strdup(opts[ii]);
523                 gchar *pos = strchr(opt, '=');
524                 if (pos != NULL)
525                 {
526                         *pos = 0;
527                 }
528                 if (find_syn_match(opt, opt_syns) >= 0)
529                 {
530                         // Mark as deleted
531                         opts[ii][0] = 0;
532                 }
533                 g_free(opt);
534         }
535 }
536
537 // Construct the x264 options string
538 // The result is allocated, so someone must free it at some point.
539 static gchar*
540 sanitize_x264opts(signal_user_data_t *ud, const gchar *options)
541 {
542         GString *x264opts = g_string_new("");
543         gchar **split = g_strsplit(options, ":", -1);
544
545         // Remove entries that match the defaults
546         gint ii;
547         for (ii = 0; split[ii] != NULL; ii++)
548         {
549                 gchar *val = NULL;
550                 gchar *opt = g_strdup(split[ii]);
551                 gchar *pos = strchr(opt, '=');
552                 if (pos != NULL)
553                 {
554                         val = pos + 1;
555                         *pos = 0;
556                 }
557                 else
558                 {
559                         val = "1";
560                 }
561                 const gchar *def_val = x264_opt_get_default(opt);
562                 if (strcmp(val, def_val) == 0)
563                 {
564                         // Matches the default, so remove it
565                         split[ii][0] = 0;
566                 }
567                 g_free(opt);
568         }
569         gint refs = ghb_settings_get_int(ud->settings, "x264_refs");
570         if (refs <= 1)
571         {
572                 x264_remove_opt(split, x264_mixed_syns);
573         }
574         gint bframes = ghb_settings_get_int(ud->settings, "x264_bframes");
575         if (bframes == 0)
576         {
577                 x264_remove_opt(split, x264_weightb_syns);
578                 x264_remove_opt(split, x264_direct_syns);
579         }
580         if (bframes <= 1)
581         {
582                 x264_remove_opt(split, x264_bpyramid_syns);
583         }
584         gchar *me = ghb_settings_get_string(ud->settings, "x264_me");
585         if (!(strcmp(me, "umh") == 0 || strcmp(me, "esa") == 0))
586         {
587                 x264_remove_opt(split, x264_merange_syns);
588         }
589         g_free(me);
590         if (!ghb_settings_get_boolean(ud->settings, "x264_cabac"))
591         {
592                 x264_remove_opt(split, x264_trellis_syns);
593         }
594         for (ii = 0; split[ii] != NULL; ii++)
595         {
596                 if (split[ii][0] != 0)
597                         g_string_append_printf(x264opts, "%s:", split[ii]);
598         }
599         g_strfreev(split);
600         // strip the trailing ":"
601         gchar *result;
602         gint len;
603         result = g_string_free(x264opts, FALSE);
604         len = strlen(result);
605         if (len > 0) result[len - 1] = 0;
606         return result;
607 }
608