OSDN Git Service

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