OSDN Git Service

bff39740105766d4db504821e3c51c275853fc25
[handbrake-jp/handbrake-jp-git.git] / gtk / src / hb-backend.c
1 /***************************************************************************
2  *            hb-backend.c
3  *
4  *  Fri Mar 28 10:38:44 2008
5  *  Copyright  2008  John Stebbins
6  *  <john at stebbins dot name>
7  ****************************************************************************/
8
9 /*
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Library General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
23  */
24 #define _GNU_SOURCE
25 #include <limits.h>
26 #include <math.h>
27 #include "hb.h"
28 #include "hbversion.h"
29 #include <gtk/gtk.h>
30 #include <glib/gstdio.h>
31 #include "hb-backend.h"
32 #include "settings.h"
33 #include "callbacks.h"
34 #include "preview.h"
35 #include "values.h"
36 #include "lang.h"
37
38 typedef struct
39 {
40         const gchar *option;
41         const gchar *shortOpt;
42         gint ivalue;
43         const gchar *svalue;
44 } options_map_t;
45
46 typedef struct
47 {
48         gint count;
49         options_map_t *map;
50 } combo_opts_t;
51
52 static const gchar *index_str[] =
53 {
54         "0",
55         "1",
56         "2",
57         "3",
58         "4",
59         "5",
60         "6",
61         "7",
62         "8",
63         "9",
64         "10",
65 };
66
67 static options_map_t d_logging_opts[] =
68 {
69         {"0", "0", 0, "0"},
70         {"1", "1", 1, "1"},
71         {"2", "2", 2, "2"},
72 };
73 combo_opts_t logging_opts =
74 {
75         sizeof(d_logging_opts)/sizeof(options_map_t),
76         d_logging_opts
77 };
78
79 static options_map_t d_container_opts[] =
80 {
81         {"MKV", "mkv", HB_MUX_MKV, "mkv"},
82         {"MP4", "mp4", HB_MUX_MP4, "mp4"},
83         {"M4V", "m4v", HB_MUX_MP4, "m4v"},
84         {"AVI", "avi", HB_MUX_AVI, "avi"},
85         {"OGM", "ogm", HB_MUX_OGM, "ogm"},
86 };
87 combo_opts_t container_opts =
88 {
89         sizeof(d_container_opts)/sizeof(options_map_t),
90         d_container_opts
91 };
92
93 static options_map_t d_deint_opts[] =
94 {
95         {"None",   "none",   0, ""},
96         {"Fast",   "fast",   1, "-1:-1:-1:0:1"},
97         {"Slow",   "slow",   2, "2:-1:-1:0:1"},
98         {"Slower", "slower", 3, "0:-1:-1:0:1"},
99 };
100 combo_opts_t deint_opts =
101 {
102         sizeof(d_deint_opts)/sizeof(options_map_t),
103         d_deint_opts
104 };
105
106 static options_map_t d_denoise_opts[] =
107 {
108         {"None",   "none",   0, ""},
109         {"Weak",   "weak",   1, "2:1:2:3"},
110         {"Medium", "medium", 2, "3:2:2:3"},
111         {"Strong", "strong", 3, "7:7:5:5"},
112 };
113 combo_opts_t denoise_opts =
114 {
115         sizeof(d_denoise_opts)/sizeof(options_map_t),
116         d_denoise_opts
117 };
118
119 static options_map_t d_vcodec_opts[] =
120 {
121         {"H.264 (x264)",    "x264",   HB_VCODEC_X264, ""},
122         {"MPEG-4 (XviD)",   "xvid",   HB_VCODEC_XVID, ""},
123         {"MPEG-4 (FFMPEG)", "ffmpeg", HB_VCODEC_FFMPEG, ""},
124         {"Theora",          "theora", HB_VCODEC_THEORA, ""},
125 };
126 combo_opts_t vcodec_opts =
127 {
128         sizeof(d_vcodec_opts)/sizeof(options_map_t),
129         d_vcodec_opts
130 };
131
132 static options_map_t d_acodec_opts[] =
133 {
134         {"AAC (faac)",      "faac",   HB_ACODEC_FAAC, "faac"},
135         {"MP3 (lame)",      "lame",   HB_ACODEC_LAME, "lame"},
136         {"Vorbis",          "vorbis", HB_ACODEC_VORBIS, "vorbis"},
137         {"AC3 (pass-thru)", "ac3",    HB_ACODEC_AC3, "ac3"},
138 //      {"DTS (pass-thru)", "dts",    HB_ACODEC_DCA, "dts"},
139 };
140 combo_opts_t acodec_opts =
141 {
142         sizeof(d_acodec_opts)/sizeof(options_map_t),
143         d_acodec_opts
144 };
145
146 static options_map_t d_direct_opts[] =
147 {
148         {"None",      "none",     0, "none"},
149         {"Spatial",   "spatial",  1, "spatial"},
150         {"Temporal",  "temporal", 2, "temporal"},
151         {"Automatic", "auto",     3, "auto"},
152 };
153 combo_opts_t direct_opts =
154 {
155         sizeof(d_direct_opts)/sizeof(options_map_t),
156         d_direct_opts
157 };
158
159 static options_map_t d_me_opts[] =
160 {
161         {"Diamond",              "dia",  0, "dia"},
162         {"Hexagon",              "hex",  1, "hex"},
163         {"Uneven Multi-Hexagon", "umh",  2, "umh"},
164         {"Exhaustive",           "esa",  3, "esa"},
165         {"Hadamard Exhaustive",  "tesa", 4, "tesa"},
166 };
167 combo_opts_t me_opts =
168 {
169         sizeof(d_me_opts)/sizeof(options_map_t),
170         d_me_opts
171 };
172
173 static options_map_t d_subme_opts[] =
174 {
175         {"1", "1", 1, "1"},
176         {"2", "2", 2, "2"},
177         {"3", "3", 3, "3"},
178         {"4", "4", 4, "4"},
179         {"5", "5", 5, "5"},
180         {"6", "6", 6, "6"},
181         {"7", "7", 7, "7"},
182         {"8", "8", 8, "8"},
183         {"9", "9", 9, "9"},
184 };
185 combo_opts_t subme_opts =
186 {
187         sizeof(d_subme_opts)/sizeof(options_map_t),
188         d_subme_opts
189 };
190
191 static options_map_t d_analyse_opts[] =
192 {
193         {"Some", "some", 0, "some"},
194         {"None", "none", 1, "none"},
195         {"All",  "all",  2, "all"},
196         {"Custom",  "custom",  3, "all"},
197 };
198 combo_opts_t analyse_opts =
199 {
200         sizeof(d_analyse_opts)/sizeof(options_map_t),
201         d_analyse_opts
202 };
203
204 static options_map_t d_trellis_opts[] =
205 {
206         {"Disabled",          "0",    0, "0"},
207         {"Final Macro Block", "1",    1, "1"},
208         {"Always",            "2", 2, "2"},
209 };
210 combo_opts_t trellis_opts =
211 {
212         sizeof(d_trellis_opts)/sizeof(options_map_t),
213         d_trellis_opts
214 };
215
216 combo_opts_t subtitle_opts =
217 {
218         0,
219         NULL
220 };
221
222 combo_opts_t title_opts =
223 {
224         0,
225         NULL
226 };
227
228 combo_opts_t audio_track_opts =
229 {
230         0,
231         NULL
232 };
233
234 typedef struct
235 {
236         const gchar *name;
237         combo_opts_t *opts;
238 } combo_name_map_t;
239
240 combo_name_map_t combo_name_map[] =
241 {
242         {"LoggingLevel", &logging_opts},
243         {"FileFormat", &container_opts},
244         {"PictureDeinterlace", &deint_opts},
245         {"tweak_PictureDeinterlace", &deint_opts},
246         {"PictureDenoise", &denoise_opts},
247         {"tweak_PictureDenoise", &denoise_opts},
248         {"VideoEncoder", &vcodec_opts},
249         {"AudioEncoder", &acodec_opts},
250         {"x264_direct", &direct_opts},
251         {"x264_me", &me_opts},
252         {"x264_subme", &subme_opts},
253         {"x264_analyse", &analyse_opts},
254         {"x264_trellis", &trellis_opts},
255         {"Subtitles", &subtitle_opts},
256         {"title", &title_opts},
257         {"AudioTrack", &audio_track_opts},
258         {NULL, NULL}
259 };
260
261 #if 0
262 typedef struct iso639_lang_t
263 {
264     char * eng_name;        /* Description in English */
265     char * native_name;     /* Description in native language */
266     char * iso639_1;       /* ISO-639-1 (2 characters) code */
267     char * iso639_2;        /* ISO-639-2/t (3 character) code */
268     char * iso639_2b;       /* ISO-639-2/b code (if different from above) */
269 } iso639_lang_t;
270 #endif
271
272 const iso639_lang_t ghb_language_table[] =
273
274         { "Any", "", "zz", "und" },
275         { "Afar", "", "aa", "aar" },
276         { "Abkhazian", "", "ab", "abk" },
277         { "Afrikaans", "", "af", "afr" },
278         { "Akan", "", "ak", "aka" },
279         { "Albanian", "", "sq", "sqi", "alb" },
280         { "Amharic", "", "am", "amh" },
281         { "Arabic", "", "ar", "ara" },
282         { "Aragonese", "", "an", "arg" },
283         { "Armenian", "", "hy", "hye", "arm" },
284         { "Assamese", "", "as", "asm" },
285         { "Avaric", "", "av", "ava" },
286         { "Avestan", "", "ae", "ave" },
287         { "Aymara", "", "ay", "aym" },
288         { "Azerbaijani", "", "az", "aze" },
289         { "Bashkir", "", "ba", "bak" },
290         { "Bambara", "", "bm", "bam" },
291         { "Basque", "", "eu", "eus", "baq" },
292         { "Belarusian", "", "be", "bel" },
293         { "Bengali", "", "bn", "ben" },
294         { "Bihari", "", "bh", "bih" },
295         { "Bislama", "", "bi", "bis" },
296         { "Bosnian", "", "bs", "bos" },
297         { "Breton", "", "br", "bre" },
298         { "Bulgarian", "", "bg", "bul" },
299         { "Burmese", "", "my", "mya", "bur" },
300         { "Catalan", "", "ca", "cat" },
301         { "Chamorro", "", "ch", "cha" },
302         { "Chechen", "", "ce", "che" },
303         { "Chinese", "", "zh", "zho", "chi" },
304         { "Church Slavic", "", "cu", "chu" },
305         { "Chuvash", "", "cv", "chv" },
306         { "Cornish", "", "kw", "cor" },
307         { "Corsican", "", "co", "cos" },
308         { "Cree", "", "cr", "cre" },
309         { "Czech", "", "cs", "ces", "cze" },
310         { "Danish", "Dansk", "da", "dan" },
311         { "Divehi", "", "dv", "div" },
312         { "Dutch", "Nederlands", "nl", "nld", "dut" },
313         { "Dzongkha", "", "dz", "dzo" },
314         { "English", "English", "en", "eng" },
315         { "Esperanto", "", "eo", "epo" },
316         { "Estonian", "", "et", "est" },
317         { "Ewe", "", "ee", "ewe" },
318         { "Faroese", "", "fo", "fao" },
319         { "Fijian", "", "fj", "fij" },
320         { "Finnish", "Suomi", "fi", "fin" },
321         { "French", "Francais", "fr", "fra", "fre" },
322         { "Western Frisian", "", "fy", "fry" },
323         { "Fulah", "", "ff", "ful" },
324         { "Georgian", "", "ka", "kat", "geo" },
325         { "German", "Deutsch", "de", "deu", "ger" },
326         { "Gaelic (Scots)", "", "gd", "gla" },
327         { "Irish", "", "ga", "gle" },
328         { "Galician", "", "gl", "glg" },
329         { "Manx", "", "gv", "glv" },
330         { "Greek, Modern", "", "el", "ell", "gre" },
331         { "Guarani", "", "gn", "grn" },
332         { "Gujarati", "", "gu", "guj" },
333         { "Haitian", "", "ht", "hat" },
334         { "Hausa", "", "ha", "hau" },
335         { "Hebrew", "", "he", "heb" },
336         { "Herero", "", "hz", "her" },
337         { "Hindi", "", "hi", "hin" },
338         { "Hiri Motu", "", "ho", "hmo" },
339         { "Hungarian", "Magyar", "hu", "hun" },
340         { "Igbo", "", "ig", "ibo" },
341         { "Icelandic", "Islenska", "is", "isl", "ice" },
342         { "Ido", "", "io", "ido" },
343         { "Sichuan Yi", "", "ii", "iii" },
344         { "Inuktitut", "", "iu", "iku" },
345         { "Interlingue", "", "ie", "ile" },
346         { "Interlingua", "", "ia", "ina" },
347         { "Indonesian", "", "id", "ind" },
348         { "Inupiaq", "", "ik", "ipk" },
349         { "Italian", "Italiano", "it", "ita" },
350         { "Javanese", "", "jv", "jav" },
351         { "Japanese", "", "ja", "jpn" },
352         { "Kalaallisut", "", "kl", "kal" },
353         { "Kannada", "", "kn", "kan" },
354         { "Kashmiri", "", "ks", "kas" },
355         { "Kanuri", "", "kr", "kau" },
356         { "Kazakh", "", "kk", "kaz" },
357         { "Central Khmer", "", "km", "khm" },
358         { "Kikuyu", "", "ki", "kik" },
359         { "Kinyarwanda", "", "rw", "kin" },
360         { "Kirghiz", "", "ky", "kir" },
361         { "Komi", "", "kv", "kom" },
362         { "Kongo", "", "kg", "kon" },
363         { "Korean", "", "ko", "kor" },
364         { "Kuanyama", "", "kj", "kua" },
365         { "Kurdish", "", "ku", "kur" },
366         { "Lao", "", "lo", "lao" },
367         { "Latin", "", "la", "lat" },
368         { "Latvian", "", "lv", "lav" },
369         { "Limburgan", "", "li", "lim" },
370         { "Lingala", "", "ln", "lin" },
371         { "Lithuanian", "", "lt", "lit" },
372         { "Luxembourgish", "", "lb", "ltz" },
373         { "Luba-Katanga", "", "lu", "lub" },
374         { "Ganda", "", "lg", "lug" },
375         { "Macedonian", "", "mk", "mkd", "mac" },
376         { "Marshallese", "", "mh", "mah" },
377         { "Malayalam", "", "ml", "mal" },
378         { "Maori", "", "mi", "mri", "mao" },
379         { "Marathi", "", "mr", "mar" },
380         { "Malay", "", "ms", "msa", "msa" },
381         { "Malagasy", "", "mg", "mlg" },
382         { "Maltese", "", "mt", "mlt" },
383         { "Moldavian", "", "mo", "mol" },
384         { "Mongolian", "", "mn", "mon" },
385         { "Nauru", "", "na", "nau" },
386         { "Navajo", "", "nv", "nav" },
387         { "Ndebele, South", "", "nr", "nbl" },
388         { "Ndebele, North", "", "nd", "nde" },
389         { "Ndonga", "", "ng", "ndo" },
390         { "Nepali", "", "ne", "nep" },
391         { "Norwegian Nynorsk", "", "nn", "nno" },
392         { "Norwegian Bokmål", "", "nb", "nob" },
393         { "Norwegian", "Norsk", "no", "nor" },
394         { "Chichewa; Nyanja", "", "ny", "nya" },
395         { "Occitan", "", "oc", "oci" },
396         { "Ojibwa", "", "oj", "oji" },
397         { "Oriya", "", "or", "ori" },
398         { "Oromo", "", "om", "orm" },
399         { "Ossetian", "", "os", "oss" },
400         { "Panjabi", "", "pa", "pan" },
401         { "Persian", "", "fa", "fas", "per" },
402         { "Pali", "", "pi", "pli" },
403         { "Polish", "", "pl", "pol" },
404         { "Portuguese", "Portugues", "pt", "por" },
405         { "Pushto", "", "ps", "pus" },
406         { "Quechua", "", "qu", "que" },
407         { "Romansh", "", "rm", "roh" },
408         { "Romanian", "", "ro", "ron", "rum" },
409         { "Rundi", "", "rn", "run" },
410         { "Russian", "", "ru", "rus" },
411         { "Sango", "", "sg", "sag" },
412         { "Sanskrit", "", "sa", "san" },
413         { "Serbian", "", "sr", "srp", "scc" },
414         { "Croatian", "Hrvatski", "hr", "hrv", "scr" },
415         { "Sinhala", "", "si", "sin" },
416         { "Slovak", "", "sk", "slk", "slo" },
417         { "Slovenian", "", "sl", "slv" },
418         { "Northern Sami", "", "se", "sme" },
419         { "Samoan", "", "sm", "smo" },
420         { "Shona", "", "sn", "sna" },
421         { "Sindhi", "", "sd", "snd" },
422         { "Somali", "", "so", "som" },
423         { "Sotho, Southern", "", "st", "sot" },
424         { "Spanish", "Espanol", "es", "spa" },
425         { "Sardinian", "", "sc", "srd" },
426         { "Swati", "", "ss", "ssw" },
427         { "Sundanese", "", "su", "sun" },
428         { "Swahili", "", "sw", "swa" },
429         { "Swedish", "Svenska", "sv", "swe" },
430         { "Tahitian", "", "ty", "tah" },
431         { "Tamil", "", "ta", "tam" },
432         { "Tatar", "", "tt", "tat" },
433         { "Telugu", "", "te", "tel" },
434         { "Tajik", "", "tg", "tgk" },
435         { "Tagalog", "", "tl", "tgl" },
436         { "Thai", "", "th", "tha" },
437         { "Tibetan", "", "bo", "bod", "tib" },
438         { "Tigrinya", "", "ti", "tir" },
439         { "Tonga", "", "to", "ton" },
440         { "Tswana", "", "tn", "tsn" },
441         { "Tsonga", "", "ts", "tso" },
442         { "Turkmen", "", "tk", "tuk" },
443         { "Turkish", "", "tr", "tur" },
444         { "Twi", "", "tw", "twi" },
445         { "Uighur", "", "ug", "uig" },
446         { "Ukrainian", "", "uk", "ukr" },
447         { "Urdu", "", "ur", "urd" },
448         { "Uzbek", "", "uz", "uzb" },
449         { "Venda", "", "ve", "ven" },
450         { "Vietnamese", "", "vi", "vie" },
451         { "Volapük", "", "vo", "vol" },
452         { "Welsh", "", "cy", "cym", "wel" },
453         { "Walloon", "", "wa", "wln" },
454         { "Wolof", "", "wo", "wol" },
455         { "Xhosa", "", "xh", "xho" },
456         { "Yiddish", "", "yi", "yid" },
457         { "Yoruba", "", "yo", "yor" },
458         { "Zhuang", "", "za", "zha" },
459         { "Zulu", "", "zu", "zul" },
460         {NULL, NULL, NULL, NULL}
461 };
462 #define LANG_TABLE_SIZE (sizeof(ghb_language_table)/ sizeof(iso639_lang_t)-1)
463
464 static void audio_bitrate_opts_set(GtkBuilder *builder, const gchar *name);
465
466 static void
467 del_tree(const gchar *name, gboolean del_top)
468 {
469         const gchar *file;
470
471         if (g_file_test(name, G_FILE_TEST_IS_DIR))
472         {
473                 GDir *gdir = g_dir_open(name, 0, NULL);
474                 file = g_dir_read_name(gdir);
475                 while (file)
476                 {
477                         gchar *path;
478                         path = g_strdup_printf("%s/%s", name, file);
479                         del_tree(path, TRUE);
480                         g_free(path);
481                         file = g_dir_read_name(gdir);
482                 }
483                 if (del_top)
484                         g_rmdir(name);
485                 g_dir_close(gdir);
486         }
487         else
488         {
489                 g_unlink(name);
490         }
491 }
492
493 const gchar*
494 ghb_version()
495 {
496         return HB_VERSION;
497 }
498
499 void
500 ghb_vquality_range(
501         signal_user_data_t *ud, 
502         gdouble *min, 
503         gdouble *max,
504         gdouble *step,
505         gdouble *page,
506         gint *digits)
507 {
508         if (ghb_settings_get_boolean(ud->settings, "directqp"))
509         {
510                 gint vcodec = ghb_settings_combo_int(ud->settings, "VideoEncoder");
511                 // Only x264 and ffmpeg currently support direct qp/crf entry
512                 *step = 1;
513                 *page = 10;
514                 *digits = 0;
515                 if (vcodec == HB_VCODEC_X264)
516                 {
517                         *min = 0;
518                         *max = 51;
519                 }
520                 else if (vcodec == HB_VCODEC_FFMPEG)
521                 {
522                         *min = 0;
523                         *max = 31;
524                 }
525                 else
526                 {
527                         *min = 0;
528                         *max = 1.0;
529                         *step = 0.001;
530                         *page = 0.1;
531                         *digits = 3;
532                 }
533         }
534         else
535         {
536                 *min = 0;
537                 *max = 1.0;
538                 *step = 0.001;
539                 *page = 0.1;
540                 *digits = 3;
541         }
542 }
543
544 static gint
545 lookup_generic_int(combo_opts_t *opts, const GValue *gval)
546 {
547         gint ii;
548         gchar *str;
549         gint result = -1;
550
551         str = ghb_value_string(gval);
552         for (ii = 0; ii < opts->count; ii++)
553         {
554                 if (strcmp(opts->map[ii].shortOpt, str) == 0)
555                 {
556                         result = opts->map[ii].ivalue;
557                         break;
558                 }
559         }
560         g_free(str);
561         return result;
562 }
563
564 static const gchar*
565 lookup_generic_option(combo_opts_t *opts, const GValue *gval)
566 {
567         gint ii;
568         gchar *str;
569         const gchar *result = "";
570
571         str = ghb_value_string(gval);
572         for (ii = 0; ii < opts->count; ii++)
573         {
574                 if (strcmp(opts->map[ii].shortOpt, str) == 0)
575                 {
576                         result = opts->map[ii].option;
577                         break;
578                 }
579         }
580         g_free(str);
581         return result;
582 }
583
584 static gint
585 lookup_mix_int(const GValue *mix)
586 {
587         gint ii;
588         gchar *str;
589         gint result = 0;
590
591
592         str = ghb_value_string(mix);
593         for (ii = 0; ii < hb_audio_mixdowns_count; ii++)
594         {
595                 if (strcmp(hb_audio_mixdowns[ii].short_name, str) == 0)
596                 {
597                         result = hb_audio_mixdowns[ii].amixdown;
598                         break;
599                 }
600         }
601         g_free(str);
602         return result;
603 }
604
605 static const gchar*
606 lookup_mix_option(const GValue *mix)
607 {
608         gint ii;
609         gchar *str;
610         gchar *result = "None";
611
612
613         str = ghb_value_string(mix);
614         for (ii = 0; ii < hb_audio_mixdowns_count; ii++)
615         {
616                 if (strcmp(hb_audio_mixdowns[ii].short_name, str) == 0)
617                 {
618                         result = hb_audio_mixdowns[ii].human_readable_name;
619                         break;
620                 }
621         }
622         g_free(str);
623         return result;
624 }
625
626 static gint
627 lookup_video_rate_int(const GValue *vrate)
628 {
629         gint ii;
630         gchar *str;
631         gint result = 0;
632
633         str = ghb_value_string(vrate);
634         for (ii = 0; ii < hb_video_rates_count; ii++)
635         {
636                 if (strcmp(hb_video_rates[ii].string, str) == 0)
637                 {
638                         result = hb_video_rates[ii].rate;
639                         break;
640                 }
641         }
642         g_free(str);
643         // Default to "same as source"
644         return result;
645 }
646
647 static const gchar*
648 lookup_video_rate_option(const GValue *vrate)
649 {
650         gint ii;
651         gchar *str;
652         const gchar *result = "Same as source";
653
654         str = ghb_value_string(vrate);
655         for (ii = 0; ii < hb_video_rates_count; ii++)
656         {
657                 if (strcmp(hb_video_rates[ii].string, str) == 0)
658                 {
659                         result = hb_video_rates[ii].string;
660                         break;
661                 }
662         }
663         g_free(str);
664         // Default to "same as source"
665         return result;
666 }
667
668 static gint
669 lookup_audio_rate_int(const GValue *rate)
670 {
671         gint ii;
672         gchar *str;
673         gint result = 0;
674
675         // Coincidentally, the string "source" will return 0
676         // which is our flag to use "same as source"
677         str = ghb_value_string(rate);
678         for (ii = 0; ii < hb_audio_rates_count; ii++)
679         {
680                 if (strcmp(hb_audio_rates[ii].string, str) == 0)
681                 {
682                         result = hb_audio_rates[ii].rate;
683                         break;
684                 }
685         }
686         g_free(str);
687         return result;
688 }
689
690 static const gchar*
691 lookup_audio_rate_option(const GValue *rate)
692 {
693         gint ii;
694         gchar *str;
695         const gchar *result = "Same as source";
696
697         // Coincidentally, the string "source" will return 0
698         // which is our flag to use "same as source"
699         str = ghb_value_string(rate);
700         for (ii = 0; ii < hb_audio_rates_count; ii++)
701         {
702                 if (strcmp(hb_audio_rates[ii].string, str) == 0)
703                 {
704                         result = hb_audio_rates[ii].string;
705                         break;
706                 }
707         }
708         g_free(str);
709         return result;
710 }
711
712 static gint
713 lookup_audio_bitrate_int(const GValue *rate)
714 {
715         gint ii;
716         gchar *str;
717         gint result = 0;
718
719         // Coincidentally, the string "source" will return 0
720         // which is our flag to use "same as source"
721         str = ghb_value_string(rate);
722         for (ii = 0; ii < hb_audio_bitrates_count; ii++)
723         {
724                 if (strcmp(hb_audio_bitrates[ii].string, str) == 0)
725                 {
726                         result = hb_audio_bitrates[ii].rate;
727                         break;
728                 }
729         }
730         g_free(str);
731         return result;
732 }
733
734 static const gchar*
735 lookup_audio_bitrate_option(const GValue *rate)
736 {
737         gint ii;
738         gchar *str;
739         const gchar *result = "Same as source";
740
741         // Coincidentally, the string "source" will return 0
742         // which is our flag to use "same as source"
743         str = ghb_value_string(rate);
744         for (ii = 0; ii < hb_audio_bitrates_count; ii++)
745         {
746                 if (strcmp(hb_audio_bitrates[ii].string, str) == 0)
747                 {
748                         result = hb_audio_bitrates[ii].string;
749                         break;
750                 }
751         }
752         g_free(str);
753         return result;
754 }
755
756 static gint
757 lookup_audio_lang_int(const GValue *rate)
758 {
759         gint ii;
760         gchar *str;
761         gint result = 0;
762
763         // Coincidentally, the string "source" will return 0
764         // which is our flag to use "same as source"
765         str = ghb_value_string(rate);
766         for (ii = 0; ii < LANG_TABLE_SIZE; ii++)
767         {
768                 if (strcmp(ghb_language_table[ii].iso639_2, str) == 0)
769                 {
770                         result = ii;
771                         break;
772                 }
773         }
774         g_free(str);
775         return result;
776 }
777
778 static const gchar*
779 lookup_audio_lang_option(const GValue *rate)
780 {
781         gint ii;
782         gchar *str;
783         const gchar *result = "Same as source";
784
785         // Coincidentally, the string "source" will return 0
786         // which is our flag to use "same as source"
787         str = ghb_value_string(rate);
788         for (ii = 0; ii < LANG_TABLE_SIZE; ii++)
789         {
790                 if (strcmp(ghb_language_table[ii].iso639_2, str) == 0)
791                 {
792                         result = ghb_language_table[ii].eng_name;
793                         break;
794                 }
795         }
796         g_free(str);
797         return result;
798 }
799
800 static GValue*
801 get_acodec_value(gint val)
802 {
803         GValue *value = NULL;
804         gint ii;
805
806         for (ii = 0; ii < acodec_opts.count; ii++)
807         {
808                 if (acodec_opts.map[ii].ivalue == val)
809                 {
810                         value = ghb_string_value_new(acodec_opts.map[ii].shortOpt);
811                         break;
812                 }
813         }
814         return value;
815 }
816
817 #if 0
818 static GValue*
819 get_abitrate_value(gint val)
820 {
821         GValue *value = NULL;
822         gint ii;
823
824         for (ii = 0; ii < hb_audio_bitrates_count; ii++)
825         {
826                 if (hb_audio_bitrates[ii].rate == val)
827                 {
828                         value = ghb_string_value_new(hb_audio_bitrates[ii].string);
829                         break;
830                 }
831         }
832         return value;
833 }
834 #endif
835
836 static GValue*
837 get_amix_value(gint val)
838 {
839         GValue *value = NULL;
840         gint ii;
841
842         for (ii = 0; ii < hb_audio_mixdowns_count; ii++)
843         {
844                 if (hb_audio_mixdowns[ii].amixdown == val)
845                 {
846                         value = ghb_string_value_new(hb_audio_mixdowns[ii].short_name);
847                         break;
848                 }
849         }
850         return value;
851 }
852
853 // Handle for libhb.  Gets set by ghb_backend_init()
854 static hb_handle_t * h_scan = NULL;
855 static hb_handle_t * h_queue = NULL;
856
857 extern void hb_get_tempory_directory(hb_handle_t *h, char path[512]);
858
859 gchar*
860 ghb_get_tmp_dir()
861 {
862         char dir[512];
863
864         hb_get_tempory_directory(h_scan, dir);
865         return g_strdup(dir);
866 }
867
868 void
869 ghb_hb_cleanup(gboolean partial)
870 {
871         char dir[512];
872
873         hb_get_tempory_directory(h_scan, dir);
874         del_tree(dir, !partial);
875 }
876
877 gint
878 ghb_get_title_number(gint titleindex)
879 {
880         hb_list_t  * list;
881         hb_title_t * title;
882         
883         if (h_scan == NULL) return 1;
884         list = hb_get_titles( h_scan );
885         if( !hb_list_count( list ) )
886         {
887                 /* No valid title, stop right there */
888                 return 1;
889         }
890         title = hb_list_item( list, titleindex );
891         if (title == NULL) return 1;    // Bad titleindex
892         return title->index;
893 }
894
895 static hb_audio_config_t*
896 get_hb_audio(gint titleindex, gint track)
897 {
898         hb_list_t  * list;
899         hb_title_t * title;
900     hb_audio_config_t *audio = NULL;
901         
902     if (h_scan == NULL) return NULL;
903         list = hb_get_titles( h_scan );
904         if( !hb_list_count( list ) )
905         {
906                 /* No valid title, stop right there */
907                 return NULL;
908         }
909     title = hb_list_item( list, titleindex );
910         if (title == NULL) return NULL; // Bad titleindex
911         if (!hb_list_count(title->list_audio))
912         {
913                 return NULL;
914         }
915     audio = (hb_audio_config_t *)hb_list_audio_config_item(title->list_audio, track);
916         return audio;
917 }
918
919 static gint
920 search_rates(hb_rate_t *rates, gint rate, gint count)
921 {
922         gint ii;
923         for (ii = 0; ii < count; ii++)
924         {
925                 if (rates[ii].rate == rate)
926                         return ii;
927         }
928         return -1;
929 }
930
931 static gboolean find_combo_item_by_int(GtkTreeModel *store, gint value, GtkTreeIter *iter);
932
933 static GtkListStore*
934 get_combo_box_store(GtkBuilder *builder, const gchar *name)
935 {
936         GtkComboBox *combo;
937         GtkListStore *store;
938
939         g_debug("get_combo_box_store() %s\n", name);
940         // First modify the combobox model to allow greying out of options
941         combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name));
942         store = GTK_LIST_STORE(gtk_combo_box_get_model (combo));
943         return store;
944 }
945
946 static void
947 grey_combo_box_item(GtkBuilder *builder, const gchar *name, gint value, gboolean grey)
948 {
949         GtkListStore *store;
950         GtkTreeIter iter;
951         
952         store = get_combo_box_store(builder, name);
953         if (find_combo_item_by_int(GTK_TREE_MODEL(store), value, &iter))
954         {
955                 gtk_list_store_set(store, &iter, 
956                                                    1, !grey, 
957                                                    -1);
958         }
959 }
960
961 void
962 ghb_grey_combo_options(GtkBuilder *builder)
963 {
964         GtkWidget *widget;
965         gint container, track, titleindex, acodec;
966     hb_audio_config_t *audio = NULL;
967         GValue *gval;
968         
969         widget = GHB_WIDGET (builder, "title");
970         gval = ghb_widget_value(widget);
971         titleindex = ghb_lookup_combo_int("title", gval);
972         ghb_value_free(gval);
973         widget = GHB_WIDGET (builder, "AudioTrack");
974         gval = ghb_widget_value(widget);
975         track = ghb_lookup_combo_int("AudioTrack", gval);
976         ghb_value_free(gval);
977         audio = get_hb_audio(titleindex, track);
978         widget = GHB_WIDGET (builder, "FileFormat");
979         gval = ghb_widget_value(widget);
980         container = ghb_lookup_combo_int("FileFormat", gval);
981         ghb_value_free(gval);
982
983         grey_combo_box_item(builder, "x264_analyse", 3, TRUE);
984         grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_FAAC, FALSE);
985         grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_LAME, FALSE);
986         grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_VORBIS, FALSE);
987
988         gboolean allow_ac3 = TRUE;
989         allow_ac3 = (container != HB_MUX_OGM);
990
991         if (allow_ac3)
992         {
993                 grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_AC3, FALSE);
994                 grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_DCA, FALSE);
995         }
996         else
997         {
998                 grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_AC3, TRUE);
999                 grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_DCA, TRUE);
1000         }
1001         if (audio && audio->in.codec != HB_ACODEC_AC3)
1002         {
1003                 grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_AC3, TRUE);
1004         }
1005         if (audio && audio->in.codec != HB_ACODEC_DCA)
1006         {
1007                 grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_DCA, TRUE);
1008         }
1009         grey_combo_box_item(builder, "VideoEncoder", HB_VCODEC_THEORA, FALSE);
1010
1011         widget = GHB_WIDGET (builder, "AudioEncoder");
1012         gval = ghb_widget_value(widget);
1013         acodec = ghb_lookup_combo_int("AudioEncoder", gval);
1014         ghb_value_free(gval);
1015         if (acodec != HB_ACODEC_AC3)
1016         {
1017                 grey_combo_box_item(builder, "AudioMixdown", 0, TRUE);
1018         }
1019         if (container == HB_MUX_MP4)
1020         {
1021                 grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_LAME, TRUE);
1022                 grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_VORBIS, TRUE);
1023                 grey_combo_box_item(builder, "VideoEncoder", HB_VCODEC_THEORA, TRUE);
1024         }
1025         else if (container == HB_MUX_AVI)
1026         {
1027                 grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_FAAC, TRUE);
1028                 grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_VORBIS, TRUE);
1029                 grey_combo_box_item(builder, "VideoEncoder", HB_VCODEC_THEORA, TRUE);
1030         }
1031         else if (container == HB_MUX_OGM)
1032         {
1033                 grey_combo_box_item(builder, "AudioEncoder", HB_ACODEC_FAAC, TRUE);
1034         }
1035
1036         gboolean allow_mono = TRUE;
1037         gboolean allow_stereo = TRUE;
1038         gboolean allow_dolby = TRUE;
1039         gboolean allow_dpl2 = TRUE;
1040         gboolean allow_6ch = TRUE;
1041         if (audio)
1042         {
1043                 allow_mono =
1044                         (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
1045                         (acodec != HB_ACODEC_LAME);
1046                 gint layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
1047                 allow_stereo =
1048                         ((layout == HB_INPUT_CH_LAYOUT_MONO && !allow_mono) || layout >= HB_INPUT_CH_LAYOUT_STEREO);
1049                 allow_dolby =
1050                         (layout == HB_INPUT_CH_LAYOUT_3F1R) || 
1051                         (layout == HB_INPUT_CH_LAYOUT_3F2R) || 
1052                         (layout == HB_INPUT_CH_LAYOUT_DOLBY);
1053                 allow_dpl2 = (layout == HB_INPUT_CH_LAYOUT_3F2R);
1054                 allow_6ch =
1055                         (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
1056                         (acodec != HB_ACODEC_LAME) &&
1057                         (layout == HB_INPUT_CH_LAYOUT_3F2R) && 
1058                         (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE);
1059         }
1060         grey_combo_box_item(builder, "AudioMixdown", HB_AMIXDOWN_MONO, !allow_mono);
1061         grey_combo_box_item(builder, "AudioMixdown", HB_AMIXDOWN_STEREO, !allow_stereo);
1062         grey_combo_box_item(builder, "AudioMixdown", HB_AMIXDOWN_DOLBY, !allow_dolby);
1063         grey_combo_box_item(builder, "AudioMixdown", HB_AMIXDOWN_DOLBYPLII, !allow_dpl2);
1064         grey_combo_box_item(builder, "AudioMixdown", HB_AMIXDOWN_6CH, !allow_6ch);
1065 }
1066
1067 gint
1068 ghb_get_best_mix(gint titleindex, gint track, gint acodec, gint mix)
1069 {
1070     hb_audio_config_t *audio = NULL;
1071         gboolean allow_mono = TRUE;
1072         gboolean allow_stereo = TRUE;
1073         gboolean allow_dolby = TRUE;
1074         gboolean allow_dpl2 = TRUE;
1075         gboolean allow_6ch = TRUE;
1076         
1077         if (acodec & (HB_ACODEC_AC3 | HB_ACODEC_DCA))
1078         {
1079                 // Audio codec pass-thru.  No mixdown
1080                 return 0;
1081         }
1082         audio = get_hb_audio(titleindex, track);
1083         if (audio)
1084         {
1085                 allow_mono =
1086                         (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
1087                         (acodec != HB_ACODEC_LAME);
1088                 gint layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
1089                 allow_stereo =
1090                         ((layout == HB_INPUT_CH_LAYOUT_MONO && !allow_mono) || layout >= HB_INPUT_CH_LAYOUT_STEREO);
1091                 allow_dolby =
1092                         (layout == HB_INPUT_CH_LAYOUT_3F1R) || 
1093                         (layout == HB_INPUT_CH_LAYOUT_3F2R) || 
1094                         (layout == HB_INPUT_CH_LAYOUT_DOLBY);
1095                 allow_dpl2 = (layout == HB_INPUT_CH_LAYOUT_3F2R);
1096                 allow_6ch =
1097                         (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
1098                         (acodec != HB_ACODEC_LAME) &&
1099                         (layout == HB_INPUT_CH_LAYOUT_3F2R) && 
1100                         (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE);
1101         }
1102         gboolean greater = FALSE;
1103         if (mix == 0) 
1104         {
1105                 // If no mix is specified, select the best available.
1106                 mix = HB_AMIXDOWN_6CH;
1107         }
1108         if (mix == HB_AMIXDOWN_6CH)
1109         {
1110                 greater = TRUE;
1111                 if (allow_6ch) return HB_AMIXDOWN_6CH;
1112         }
1113         if (mix == HB_AMIXDOWN_DOLBYPLII || greater)
1114         {
1115                 greater = TRUE;
1116                 if (allow_dpl2) return HB_AMIXDOWN_DOLBYPLII;
1117         }
1118         if (mix == HB_AMIXDOWN_DOLBY || greater)
1119         {
1120                 greater = TRUE;
1121                 if (allow_dolby) return HB_AMIXDOWN_DOLBY;
1122         }
1123         if (mix == HB_AMIXDOWN_STEREO || greater)
1124         {
1125                 greater = TRUE;
1126                 if (allow_stereo) return HB_AMIXDOWN_STEREO;
1127         }
1128         if (mix == HB_AMIXDOWN_MONO || greater)
1129         {
1130                 greater = TRUE;
1131                 if (allow_mono) return HB_AMIXDOWN_MONO;
1132         }
1133         if (allow_stereo) return HB_AMIXDOWN_STEREO;
1134         if (allow_dolby) return HB_AMIXDOWN_DOLBY;
1135         if (allow_dpl2) return HB_AMIXDOWN_DOLBYPLII;
1136         if (allow_6ch) return HB_AMIXDOWN_6CH;
1137         return 0;
1138 }
1139
1140 // Set up the model for the combo box
1141 static void
1142 init_combo_box(GtkBuilder *builder, const gchar *name)
1143 {
1144         GtkComboBox *combo;
1145         GtkListStore *store;
1146         GtkCellRenderer *cell;
1147
1148         g_debug("init_combo_box() %s\n", name);
1149         // First modify the combobox model to allow greying out of options
1150         combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name));
1151         // Store contains:
1152         // 1 - String to display
1153         // 2 - bool indicating whether the entry is selectable (grey or not)
1154         // 3 - String that is used for presets
1155         // 4 - Int value determined by backend
1156         // 5 - String value determined by backend
1157         store = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_BOOLEAN, 
1158                                                            G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
1159         gtk_combo_box_set_model(combo, GTK_TREE_MODEL(store));
1160
1161         if (GTK_WIDGET_TYPE(combo) == GTK_TYPE_COMBO_BOX)
1162         {
1163                 gtk_cell_layout_clear(GTK_CELL_LAYOUT(combo));
1164         cell = GTK_CELL_RENDERER(gtk_cell_renderer_text_new());
1165         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
1166         gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell,
1167                 "text", 0, "sensitive", 1, NULL);
1168         }
1169         else
1170         { // Combo box entry
1171                 gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(combo), 0);
1172         }
1173 }       
1174
1175 static void
1176 audio_samplerate_opts_set(GtkBuilder *builder, const gchar *name, hb_rate_t *rates, gint count)
1177 {
1178         GtkTreeIter iter;
1179         GtkListStore *store;
1180         gint ii;
1181         
1182         g_debug("audio_samplerate_opts_set ()\n");
1183         store = get_combo_box_store(builder, name);
1184         gtk_list_store_clear(store);
1185         // Add an item for "Same As Source"
1186         gtk_list_store_append(store, &iter);
1187         gtk_list_store_set(store, &iter, 
1188                                            0, "Same as source", 
1189                                            1, TRUE, 
1190                                            2, "source", 
1191                                            3, 0, 
1192                                            4, "source", 
1193                                            -1);
1194         for (ii = 0; ii < count; ii++)
1195         {
1196                 gtk_list_store_append(store, &iter);
1197                 gtk_list_store_set(store, &iter, 
1198                                                    0, rates[ii].string, 
1199                                                    1, TRUE, 
1200                                                    2, rates[ii].string, 
1201                                                    3, rates[ii].rate, 
1202                                                    4, rates[ii].string, 
1203                                                    -1);
1204         }
1205 }
1206
1207 static void
1208 video_rate_opts_set(GtkBuilder *builder, const gchar *name, hb_rate_t *rates, gint count)
1209 {
1210         GtkTreeIter iter;
1211         GtkListStore *store;
1212         gint ii;
1213         
1214         g_debug("video_rate_opts_set ()\n");
1215         store = get_combo_box_store(builder, name);
1216         gtk_list_store_clear(store);
1217         // Add an item for "Same As Source"
1218         gtk_list_store_append(store, &iter);
1219         gtk_list_store_set(store, &iter, 
1220                                            0, "Same as source", 
1221                                            1, TRUE, 
1222                                            2, "source", 
1223                                            3, 0, 
1224                                            4, "source", 
1225                                            -1);
1226         for (ii = 0; ii < count; ii++)
1227         {
1228                 gchar *desc = "";
1229                 gchar *option;
1230                 if (strcmp(rates[ii].string, "23.976") == 0)
1231                 {
1232                         desc = "(NTSC Film)";
1233                 }
1234                 else if (strcmp(rates[ii].string, "25") == 0)
1235                 {
1236                         desc = "(PAL Film/Video)";
1237                 }
1238                 else if (strcmp(rates[ii].string, "29.97") == 0)
1239                 {
1240                         desc = "(NTSC Video)";
1241                 }
1242                 option = g_strdup_printf ("%s %s", rates[ii].string, desc);
1243                 gtk_list_store_append(store, &iter);
1244                 gtk_list_store_set(store, &iter, 
1245                                                    0, option, 
1246                                                    1, TRUE, 
1247                                                    2, rates[ii].string, 
1248                                                    3, rates[ii].rate, 
1249                                                    4, rates[ii].string, 
1250                                                    -1);
1251                 g_free(option);
1252         }
1253 }
1254
1255 static void
1256 mix_opts_set(GtkBuilder *builder, const gchar *name)
1257 {
1258         GtkTreeIter iter;
1259         GtkListStore *store;
1260         gint ii;
1261         
1262         g_debug("mix_opts_set ()\n");
1263         store = get_combo_box_store(builder, name);
1264         gtk_list_store_clear(store);
1265         gtk_list_store_append(store, &iter);
1266         gtk_list_store_set(store, &iter, 
1267                                            0, "None", 
1268                                            1, TRUE, 
1269                                            2, "none", 
1270                                            3, 0, 
1271                                            4, "none", 
1272                                            -1);
1273         for (ii = 0; ii < hb_audio_mixdowns_count; ii++)
1274         {
1275                 gtk_list_store_append(store, &iter);
1276                 gtk_list_store_set(store, &iter, 
1277                                                    0, hb_audio_mixdowns[ii].human_readable_name, 
1278                                                    1, TRUE, 
1279                                                    2, hb_audio_mixdowns[ii].short_name, 
1280                                                    3, hb_audio_mixdowns[ii].amixdown, 
1281                                                    4, hb_audio_mixdowns[ii].internal_name, 
1282                                                    -1);
1283         }
1284 }
1285
1286 static void
1287 language_opts_set(GtkBuilder *builder, const gchar *name)
1288 {
1289         GtkTreeIter iter;
1290         GtkListStore *store;
1291         gint ii;
1292         
1293         g_debug("language_opts_set ()\n");
1294         store = get_combo_box_store(builder, name);
1295         gtk_list_store_clear(store);
1296         for (ii = 0; ii < LANG_TABLE_SIZE; ii++)
1297         {
1298                 gtk_list_store_append(store, &iter);
1299                 gtk_list_store_set(store, &iter, 
1300                                                    0, ghb_language_table[ii].eng_name, 
1301                                                    1, TRUE, 
1302                                                    2, ghb_language_table[ii].iso639_2, 
1303                                                    3, ii, 
1304                                                    4, ghb_language_table[ii].iso639_1, 
1305                                                    -1);
1306         }
1307 }
1308
1309 static gchar **titles = NULL;
1310
1311 void
1312 title_opts_set(GtkBuilder *builder, const gchar *name)
1313 {
1314         GtkTreeIter iter;
1315         GtkListStore *store;
1316         hb_list_t  * list = NULL;
1317         hb_title_t * title = NULL;
1318         gint ii;
1319         gint count = 0;
1320         
1321         g_debug("title_opts_set ()\n");
1322         store = get_combo_box_store(builder, name);
1323         gtk_list_store_clear(store);
1324         if (h_scan != NULL)
1325         {
1326                 list = hb_get_titles( h_scan );
1327                 count = hb_list_count( list );
1328                 if (count > 100) count = 100;
1329         }
1330         if (titles) g_strfreev(titles);
1331         if (title_opts.map) g_free(title_opts.map);
1332         if (count > 0)
1333         {
1334                 title_opts.count = count;
1335                 title_opts.map = g_malloc(count*sizeof(options_map_t));
1336                 titles = g_malloc((count+1) * sizeof(gchar*));
1337         }
1338         else
1339         {
1340                 title_opts.count = 1;
1341                 title_opts.map = g_malloc(sizeof(options_map_t));
1342                 titles = NULL;
1343         }
1344         if( count <= 0 )
1345         {
1346                 // No titles.  Fill in a default.
1347                 gtk_list_store_append(store, &iter);
1348                 gtk_list_store_set(store, &iter, 
1349                                                    0, "No Titles", 
1350                                                    1, TRUE, 
1351                                                    2, "none", 
1352                                                    3, -1, 
1353                                                    4, "none", 
1354                                                    -1);
1355                 title_opts.map[0].option = "No Titles";
1356                 title_opts.map[0].shortOpt = "none";
1357                 title_opts.map[0].ivalue = -1;
1358                 title_opts.map[0].svalue = "none";
1359                 return;
1360         }
1361         for (ii = 0; ii < count; ii++)
1362         {
1363                 title = (hb_title_t*)hb_list_item(list, ii);
1364                 if (title->duration != 0)
1365                 {
1366                         titles[ii]  = g_strdup_printf ("%d - %02dh%02dm%02ds",
1367                                 title->index, title->hours, title->minutes, title->seconds);
1368                 }
1369                 else
1370                 {
1371                         titles[ii]  = g_strdup_printf ("%d - Unknown Length", title->index);
1372                 }
1373                 gtk_list_store_append(store, &iter);
1374                 gtk_list_store_set(store, &iter, 
1375                                                    0, titles[ii], 
1376                                                    1, TRUE, 
1377                                                    2, titles[ii], 
1378                                                    3, ii, 
1379                                                    4, titles[ii], 
1380                                                    -1);
1381                 title_opts.map[ii].option = titles[ii];
1382                 title_opts.map[ii].shortOpt = titles[ii];
1383                 title_opts.map[ii].ivalue = ii;
1384                 title_opts.map[ii].svalue = titles[ii];
1385         }
1386         titles[ii] = NULL;
1387 }
1388
1389 static gboolean
1390 find_combo_item_by_int(GtkTreeModel *store, gint value, GtkTreeIter *iter)
1391 {
1392         gint ivalue;
1393         gboolean foundit = FALSE;
1394         
1395         if (gtk_tree_model_get_iter_first (store, iter))
1396         {
1397                 do
1398                 {
1399                         gtk_tree_model_get(store, iter, 3, &ivalue, -1);
1400                         if (value == ivalue)
1401                         {
1402                                 foundit = TRUE;
1403                                 break;
1404                         }
1405                 } while (gtk_tree_model_iter_next (store, iter));
1406         }
1407         return foundit;
1408 }
1409
1410 void
1411 audio_track_opts_set(GtkBuilder *builder, const gchar *name, gint titleindex)
1412 {
1413         GtkTreeIter iter;
1414         GtkListStore *store;
1415         hb_list_t  * list = NULL;
1416         hb_title_t * title = NULL;
1417     hb_audio_config_t * audio;
1418         gint ii;
1419         gint count = 0;
1420         
1421         g_debug("audio_track_opts_set ()\n");
1422         store = get_combo_box_store(builder, name);
1423         gtk_list_store_clear(store);
1424         if (h_scan != NULL)
1425         {
1426                 list = hb_get_titles( h_scan );
1427             title = (hb_title_t*)hb_list_item( list, titleindex );
1428                 if (title != NULL)
1429                 {
1430                         count = hb_list_count( title->list_audio );
1431                 }
1432         }
1433         if (count > 10) count = 10;
1434         if (audio_track_opts.map) g_free(audio_track_opts.map);
1435         if (count > 0)
1436         {
1437                 audio_track_opts.count = count;
1438                 audio_track_opts.map = g_malloc(count*sizeof(options_map_t));
1439         }
1440         else
1441         {
1442                 audio_track_opts.count = 1;
1443                 audio_track_opts.map = g_malloc(sizeof(options_map_t));
1444         }
1445         if( count <= 0 )
1446         {
1447                 // No audio. set some default
1448                 gtk_list_store_append(store, &iter);
1449                 gtk_list_store_set(store, &iter, 
1450                                                    0, "No Audio", 
1451                                                    1, TRUE, 
1452                                                    2, "none", 
1453                                                    3, -1, 
1454                                                    4, "none", 
1455                                                    -1);
1456                 audio_track_opts.map[0].option = "No Audio";
1457                 audio_track_opts.map[0].shortOpt = "none";
1458                 audio_track_opts.map[0].ivalue = -1;
1459                 audio_track_opts.map[0].svalue = "none";
1460                 return;
1461         }
1462         for (ii = 0; ii < count; ii++)
1463         {
1464         audio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, ii );
1465                 gtk_list_store_append(store, &iter);
1466                 gtk_list_store_set(store, &iter, 
1467                                                    0, audio->lang.description, 
1468                                                    1, TRUE, 
1469                                                    2, index_str[ii], 
1470                                                    3, ii, 
1471                                                    4, index_str[ii], 
1472                                                    -1);
1473                 audio_track_opts.map[ii].option = audio->lang.description,
1474                 audio_track_opts.map[ii].shortOpt = index_str[ii];
1475                 audio_track_opts.map[ii].ivalue = ii;
1476                 audio_track_opts.map[ii].svalue = index_str[ii];
1477         }
1478 }
1479
1480
1481 void
1482 subtitle_opts_set(GtkBuilder *builder, const gchar *name, gint titleindex)
1483 {
1484         GtkTreeIter iter;
1485         GtkListStore *store;
1486         hb_list_t  * list = NULL;
1487         hb_title_t * title = NULL;
1488     hb_subtitle_t * subtitle;
1489         gint ii;
1490         gint count = 0;
1491         
1492         g_debug("subtitle_opts_set () %s\n", name);
1493         store = get_combo_box_store(builder, name);
1494         gtk_list_store_clear(store);
1495         if (h_scan != NULL)
1496         {
1497                 list = hb_get_titles( h_scan );
1498             title = (hb_title_t*)hb_list_item( list, titleindex );
1499                 if (title != NULL)
1500                 {
1501                         count = hb_list_count( title->list_subtitle );
1502                 }
1503         }
1504         if (count > 10) count = 10;
1505         if (subtitle_opts.map) g_free(subtitle_opts.map);
1506         if (count > 0)
1507         {
1508                 subtitle_opts.count = count+2;
1509                 subtitle_opts.map = g_malloc((count+2)*sizeof(options_map_t));
1510         }
1511         else
1512         {
1513                 subtitle_opts.count = LANG_TABLE_SIZE+2;
1514                 subtitle_opts.map = g_malloc((LANG_TABLE_SIZE+2)*sizeof(options_map_t));
1515         }
1516         // Add options for "none" and "autoselect"
1517         gtk_list_store_append(store, &iter);
1518         gtk_list_store_set(store, &iter, 
1519                                            0, "None", 
1520                                            1, TRUE, 
1521                                            2, "none", 
1522                                            3, -2, 
1523                                            4, "none", 
1524                                            -1);
1525         subtitle_opts.map[0].option = "None";
1526         subtitle_opts.map[0].shortOpt = "none";
1527         subtitle_opts.map[0].ivalue = -2;
1528         subtitle_opts.map[0].svalue = "none";
1529         gtk_list_store_append(store, &iter);
1530         gtk_list_store_set(store, &iter, 
1531                                            0, "Autoselect", 
1532                                            1, TRUE, 
1533                                            2, "auto", 
1534                                            3, -1, 
1535                                            4, "auto", 
1536                                            -1);
1537         subtitle_opts.map[0].option = "Same as audio";
1538         subtitle_opts.map[0].shortOpt = "auto";
1539         subtitle_opts.map[0].ivalue = -1;
1540         subtitle_opts.map[0].svalue = "auto";
1541         if (count >0)
1542         {
1543                 for (ii = 0; ii < count; ii++)
1544                 {
1545                 subtitle = (hb_subtitle_t *)hb_list_item(title->list_subtitle, ii);
1546                         gtk_list_store_append(store, &iter);
1547                         gtk_list_store_set(store, &iter, 
1548                                                 0, subtitle->lang, 
1549                                                 1, TRUE, 
1550                                                 2, subtitle->iso639_2, 
1551                                                 3, ii, 
1552                                                 4, subtitle->iso639_2, 
1553                                                 -1);
1554                         subtitle_opts.map[ii+2].option = subtitle->lang;
1555                         subtitle_opts.map[ii+2].shortOpt = subtitle->iso639_2;
1556                         subtitle_opts.map[ii+2].ivalue = ii;
1557                         subtitle_opts.map[ii+2].svalue = subtitle->iso639_2;
1558                 }
1559         }
1560         else
1561         {
1562                 for (ii = 0; ii < LANG_TABLE_SIZE; ii++)
1563                 {
1564                         gtk_list_store_append(store, &iter);
1565                         gtk_list_store_set(store, &iter, 
1566                                 0, ghb_language_table[ii].eng_name, 
1567                                 1, TRUE, 
1568                                 2, ghb_language_table[ii].iso639_2, 
1569                                 3, ii, 
1570                                 4, ghb_language_table[ii].iso639_2, 
1571                                 -1);
1572                         subtitle_opts.map[ii+2].option = ghb_language_table[ii].eng_name;
1573                         subtitle_opts.map[ii+2].shortOpt = ghb_language_table[ii].iso639_2;
1574                         subtitle_opts.map[ii+2].ivalue = ii;
1575                         subtitle_opts.map[ii+2].svalue = ghb_language_table[ii].iso639_2;
1576                 }
1577         }
1578 }
1579
1580 gint
1581 ghb_longest_title()
1582 {
1583         hb_list_t  * list;
1584         hb_title_t * title;
1585         gint ii;
1586         gint count = 0;
1587         guint64 longest = 0;
1588         gint titleindex = 0;
1589         
1590         g_debug("ghb_longest_title ()\n");
1591         if (h_scan == NULL) return 0;
1592         list = hb_get_titles( h_scan );
1593         count = hb_list_count( list );
1594         if (count > 100) count = 100;
1595         for (ii = 0; ii < count; ii++)
1596         {
1597                 title = (hb_title_t*)hb_list_item(list, ii);
1598                 if (title->duration > longest)
1599                 {
1600                         titleindex = ii;
1601                         longest = title->duration;
1602                 }
1603         }
1604         return titleindex;
1605 }
1606
1607 gint
1608 ghb_find_audio_track(
1609         gint titleindex, 
1610         const gchar *lang, 
1611         gint acodec,
1612         GHashTable *track_indices)
1613 {
1614         hb_list_t  * list;
1615         hb_title_t * title;
1616     hb_audio_config_t * audio;
1617         gint ii;
1618         gint count = 0;
1619         gint track = -1;
1620         gint max_chan = 0;
1621         gboolean *used;
1622         
1623         g_debug("find_audio_track ()\n");
1624         if (h_scan == NULL) return -1;
1625         list = hb_get_titles( h_scan );
1626     title = (hb_title_t*)hb_list_item( list, titleindex );
1627         if (title != NULL)
1628         {
1629                 count = hb_list_count( title->list_audio );
1630         }
1631         if (count > 10) count = 10;
1632         used = g_hash_table_lookup(track_indices, &acodec);
1633         if (used == NULL)
1634         {
1635                 used = g_malloc0(count * sizeof(gboolean));
1636                 g_hash_table_insert(track_indices, &acodec, used);
1637         }
1638         // Try to fine an item that matches the preferred language and
1639         // the passthru codec type
1640         if (acodec == HB_ACODEC_AC3 || acodec == HB_ACODEC_DCA)
1641         {
1642                 for (ii = 0; ii < count; ii++)
1643                 {
1644                         gint channels;
1645
1646                         if (used[ii])
1647                                 continue;
1648
1649                 audio = (hb_audio_config_t*)hb_list_audio_config_item( 
1650                                                                                                         title->list_audio, ii );
1651                         channels = HB_INPUT_CH_LAYOUT_GET_DISCRETE_COUNT(
1652                                                                                                         audio->in.channel_layout);
1653                         // Find a track that is not visually impaired or dirctor's
1654                         // commentary, and has the highest channel count.
1655                         if ((audio->in.codec == acodec) &&
1656                                 (audio->lang.type < 2) &&
1657                                 ((strcmp(lang, audio->lang.iso639_2) == 0) ||
1658                                 (strcmp(lang, "und") == 0)))
1659                         {
1660                                 if (channels > max_chan)
1661                                 {
1662                                         track = ii;
1663                                         max_chan = channels;
1664                                 }
1665                         }
1666                 }
1667         }
1668         if (track > -1)
1669         {
1670                 used[track] = TRUE;
1671                 return track;
1672         }
1673         // Try to fine an item that matches the preferred language
1674         for (ii = 0; ii < count; ii++)
1675         {
1676                 if (used[ii])
1677                         continue;
1678         audio = (hb_audio_config_t*)hb_list_audio_config_item( 
1679                                                                                                         title->list_audio, ii );
1680                 // Find a track that is not visually impaired or dirctor's commentary
1681                 if ((audio->lang.type < 2) &&
1682                         ((strcmp(lang, audio->lang.iso639_2) == 0) ||
1683                         (strcmp(lang, "und") == 0)))
1684                 {
1685                         track = ii;
1686                         break;
1687                 }
1688         }
1689         if (track > -1)
1690         {
1691                 used[track] = TRUE;
1692                 return track;
1693         }
1694         // Try to fine an item that does not match the preferred language and
1695         // matches the passthru codec type
1696         if (acodec == HB_ACODEC_AC3 || acodec == HB_ACODEC_DCA)
1697         {
1698                 for (ii = 0; ii < count; ii++)
1699                 {
1700                         gint channels;
1701
1702                         if (used[ii])
1703                                 continue;
1704
1705                 audio = (hb_audio_config_t*)hb_list_audio_config_item( 
1706                                                                                                         title->list_audio, ii );
1707                         channels = HB_INPUT_CH_LAYOUT_GET_DISCRETE_COUNT(
1708                                                                                                         audio->in.channel_layout);
1709                         // Find a track that is not visually impaired or dirctor's
1710                         // commentary, and has the highest channel count.
1711                         if ((audio->in.codec == acodec) &&
1712                                 (audio->lang.type < 2))
1713                         {
1714                                 if (channels > max_chan)
1715                                 {
1716                                         track = ii;
1717                                         max_chan = channels;
1718                                 }
1719                         }
1720                 }
1721         }
1722         if (track > -1)
1723         {
1724                 used[track] = TRUE;
1725                 return track;
1726         }
1727         // Try to fine an item that does not match the preferred language
1728         for (ii = 0; ii < count; ii++)
1729         {
1730                 if (used[ii])
1731                         continue;
1732         audio = (hb_audio_config_t*)hb_list_audio_config_item( 
1733                                                                                                         title->list_audio, ii );
1734                 // Find a track that is not visually impaired or dirctor's commentary
1735                 if (audio->lang.type < 2)
1736                 {
1737                         track = ii;
1738                         break;
1739                 }
1740         }
1741         if (track > -1)
1742         {
1743                 used[track] = TRUE;
1744                 return track;
1745         }
1746         // Last ditch, anything goes
1747         for (ii = 0; ii < count; ii++)
1748         {
1749         audio = (hb_audio_config_t*)hb_list_audio_config_item( 
1750                                                                                                         title->list_audio, ii );
1751                 if (!used[ii])
1752                 {
1753                         track = ii;
1754                         break;
1755                 }
1756         }
1757         if (track > -1)
1758         {
1759                 used[track] = TRUE;
1760         }
1761         return track;
1762 }
1763
1764 static void
1765 generic_opts_set(GtkBuilder *builder, const gchar *name, combo_opts_t *opts)
1766 {
1767         GtkTreeIter iter;
1768         GtkListStore *store;
1769         gint ii;
1770         
1771         g_debug("generic_opts_set ()\n");
1772         if (name == NULL || opts == NULL) return;
1773         store = get_combo_box_store(builder, name);
1774         gtk_list_store_clear(store);
1775         for (ii = 0; ii < opts->count; ii++)
1776         {
1777                 gtk_list_store_append(store, &iter);
1778                 gtk_list_store_set(store, &iter, 
1779                                                    0, opts->map[ii].option, 
1780                                                    1, TRUE, 
1781                                                    2, opts->map[ii].shortOpt, 
1782                                                    3, opts->map[ii].ivalue, 
1783                                                    4, opts->map[ii].svalue, 
1784                                                    -1);
1785         }
1786 }
1787
1788 combo_opts_t*
1789 find_combo_table(const gchar *name)
1790 {
1791         gint ii;
1792
1793         for (ii = 0; combo_name_map[ii].name != NULL; ii++)
1794         {
1795                 if (strcmp(name, combo_name_map[ii].name) == 0)
1796                 {
1797                         return combo_name_map[ii].opts;
1798                 }
1799         }
1800         return NULL;
1801 }
1802
1803 gint
1804 ghb_lookup_combo_int(const gchar *name, const GValue *gval)
1805 {
1806         if (strcmp(name, "AudioBitrate") == 0)
1807                 return lookup_audio_bitrate_int(gval);
1808         else if (strcmp(name, "AudioSamplerate") == 0)
1809                 return lookup_audio_rate_int(gval);
1810         else if (strcmp(name, "VideoFramerate") == 0)
1811                 return lookup_video_rate_int(gval);
1812         else if (strcmp(name, "AudioMixdown") == 0)
1813                 return lookup_mix_int(gval);
1814         else if (strcmp(name, "SourceAudioLang") == 0)
1815                 return lookup_audio_lang_int(gval);
1816         else
1817         {
1818                 return lookup_generic_int(find_combo_table(name), gval);
1819         }
1820         g_warning("ghb_lookup_combo_int() couldn't find %s", name);
1821         return 0;
1822 }
1823
1824 const gchar*
1825 ghb_lookup_combo_option(const gchar *name, const GValue *gval)
1826 {
1827         if (strcmp(name, "AudioBitrate") == 0)
1828                 return lookup_audio_bitrate_option(gval);
1829         else if (strcmp(name, "AudioSamplerate") == 0)
1830                 return lookup_audio_rate_option(gval);
1831         else if (strcmp(name, "VideoFramerate") == 0)
1832                 return lookup_video_rate_option(gval);
1833         else if (strcmp(name, "AudioMixdown") == 0)
1834                 return lookup_mix_option(gval);
1835         else if (strcmp(name, "SourceAudioLang") == 0)
1836                 return lookup_audio_lang_option(gval);
1837         else
1838         {
1839                 return lookup_generic_option(find_combo_table(name), gval);
1840         }
1841         g_warning("ghb_lookup_combo_int() couldn't find %s", name);
1842         return 0;
1843 }
1844
1845 void
1846 ghb_update_ui_combo_box(GtkBuilder *builder, const gchar *name, gint user_data, gboolean all)
1847 {
1848         GtkComboBox *combo = NULL;
1849         gint signal_id;
1850         gint handler_id = 0;
1851
1852         g_debug("ghb_update_ui_combo_box() %s\n", name);
1853         if (name != NULL)
1854         {               
1855                 // Clearing a combo box causes a rash of "changed" events, even when
1856                 // the active item is -1 (inactive).  To control things, I'm disabling
1857                 // the event till things are settled down.
1858                 combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name));
1859                 signal_id = g_signal_lookup("changed", GTK_TYPE_COMBO_BOX);
1860                 if (signal_id > 0)
1861                 {
1862                         // Valid signal id found.  This should always succeed.
1863                         handler_id = g_signal_handler_find ((gpointer)combo, G_SIGNAL_MATCH_ID, 
1864                                                                                                 signal_id, 0, 0, 0, 0);
1865                         if (handler_id > 0)
1866                         {
1867                                 // This should also always succeed
1868                                 g_signal_handler_block ((gpointer)combo, handler_id);
1869                         }
1870                 }
1871         }       
1872         if (all)
1873         {
1874                 audio_bitrate_opts_set(builder, "AudioBitrate");
1875                 audio_samplerate_opts_set(builder, "AudioSamplerate", hb_audio_rates, hb_audio_rates_count);
1876                 video_rate_opts_set(builder, "VideoFramerate", hb_video_rates, hb_video_rates_count);
1877                 mix_opts_set(builder, "AudioMixdown");
1878                 language_opts_set(builder, "SourceAudioLang");
1879                 subtitle_opts_set(builder, "Subtitles", user_data);
1880                 title_opts_set(builder, "title");
1881                 audio_track_opts_set(builder, "AudioTrack", user_data);
1882                 generic_opts_set(builder, "LoggingLevel", &logging_opts);
1883                 generic_opts_set(builder, "FileFormat", &container_opts);
1884                 generic_opts_set(builder, "PictureDeinterlace", &deint_opts);
1885                 generic_opts_set(builder, "tweak_PictureDeinterlace", &deint_opts);
1886                 generic_opts_set(builder, "PictureDenoise", &denoise_opts);
1887                 generic_opts_set(builder, "tweak_PictureDenoise", &denoise_opts);
1888                 generic_opts_set(builder, "VideoEncoder", &vcodec_opts);
1889                 generic_opts_set(builder, "AudioEncoder", &acodec_opts);
1890                 generic_opts_set(builder, "x264_direct", &direct_opts);
1891                 generic_opts_set(builder, "x264_me", &me_opts);
1892                 generic_opts_set(builder, "x264_subme", &subme_opts);
1893                 generic_opts_set(builder, "x264_analyse", &analyse_opts);
1894                 generic_opts_set(builder, "x264_trellis", &trellis_opts);
1895         }
1896         else
1897         {
1898                 if (strcmp(name, "AudioBitrate") == 0)
1899                         audio_bitrate_opts_set(builder, "AudioBitrate");
1900                 else if (strcmp(name, "AudioSamplerate") == 0)
1901                         audio_samplerate_opts_set(builder, "AudioSamplerate", hb_audio_rates, hb_audio_rates_count);
1902                 else if (strcmp(name, "VideoFramerate") == 0)
1903                         video_rate_opts_set(builder, "VideoFramerate", hb_video_rates, hb_video_rates_count);
1904                 else if (strcmp(name, "AudioMixdown") == 0)
1905                         mix_opts_set(builder, "AudioMixdown");
1906                 else if (strcmp(name, "SourceAudioLang") == 0)
1907                         language_opts_set(builder, "SourceAudioLang");
1908                 else if (strcmp(name, "Subtitles") == 0)
1909                         subtitle_opts_set(builder, "Subtitles", user_data);
1910                 else if (strcmp(name, "title") == 0)
1911                         title_opts_set(builder, "title");
1912                 else if (strcmp(name, "AudioTrack") == 0)
1913                         audio_track_opts_set(builder, "AudioTrack", user_data);
1914                 else
1915                         generic_opts_set(builder, name, find_combo_table(name));
1916         }
1917         if (handler_id > 0)
1918         {
1919                 g_signal_handler_unblock ((gpointer)combo, handler_id);
1920         }
1921 }
1922         
1923 static void
1924 init_ui_combo_boxes(GtkBuilder *builder)
1925 {
1926         gint ii;
1927
1928         init_combo_box(builder, "AudioBitrate");
1929         init_combo_box(builder, "AudioSamplerate");
1930         init_combo_box(builder, "VideoFramerate");
1931         init_combo_box(builder, "AudioMixdown");
1932         init_combo_box(builder, "SourceAudioLang");
1933         init_combo_box(builder, "Subtitles");
1934         init_combo_box(builder, "title");
1935         init_combo_box(builder, "AudioTrack");
1936         for (ii = 0; combo_name_map[ii].name != NULL; ii++)
1937         {
1938                 init_combo_box(builder, combo_name_map[ii].name);
1939         }
1940 }
1941         
1942 static const char * turbo_opts = 
1943         "ref=1:subme=1:me=dia:analyse=none:trellis=0:"
1944         "no-fast-pskip=0:8x8dct=0:weightb=0";
1945
1946 // Construct the x264 options string
1947 // The result is allocated, so someone must free it at some point.
1948 gchar*
1949 ghb_build_x264opts_string(GValue *settings)
1950 {
1951         gchar *result;
1952         gchar *opts = ghb_settings_get_string(settings, "x264Option");
1953         if (opts != NULL)
1954         {
1955                 result = opts;
1956         }
1957         else
1958         {
1959                 result = g_strdup("");
1960         }
1961         return result;
1962 }
1963
1964 GValue*
1965 ghb_get_chapters(gint titleindex)
1966 {
1967         hb_list_t  * list;
1968         hb_title_t * title;
1969     hb_chapter_t * chapter;
1970         gint count, ii;
1971         GValue *chapters = NULL;
1972         
1973         g_debug("ghb_get_chapters (title = %d)\n", titleindex);
1974         if (h_scan == NULL) return NULL;
1975         list = hb_get_titles( h_scan );
1976     title = (hb_title_t*)hb_list_item( list, titleindex );
1977         if (title == NULL) return NULL;
1978         count = hb_list_count( title->list_chapter );
1979         chapters = ghb_array_value_new(count);
1980         for (ii = 0; ii < count; ii++)
1981         {
1982                 chapter = hb_list_item(title->list_chapter, ii);
1983                 if (chapter == NULL) break;
1984                 if (chapter->title == NULL || chapter->title[0] == 0)
1985                 {
1986                         gchar *str;
1987                         str = g_strdup_printf ("Chapter %2d", ii+1);
1988                         ghb_array_append(chapters, ghb_string_value_new(str));
1989                         g_free(str);
1990                 }
1991                 else 
1992                 {
1993                         ghb_array_append(chapters, ghb_string_value_new(chapter->title));
1994                 }
1995         }
1996         return chapters;
1997 }
1998
1999 gboolean
2000 ghb_ac3_in_audio_list(const GValue *audio_list)
2001 {
2002         gint count, ii;
2003
2004         count = ghb_array_len(audio_list);
2005         for (ii = 0; ii < count; ii++)
2006         {
2007                 GValue *asettings;
2008                 gint acodec;
2009
2010                 asettings = ghb_array_get_nth(audio_list, ii);
2011                 acodec = ghb_settings_combo_int(asettings, "AudioEncoder");
2012                 if (acodec == HB_ACODEC_AC3)
2013                         return TRUE;
2014         }
2015         return FALSE;
2016 }
2017
2018 static void
2019 audio_bitrate_opts_add(GtkBuilder *builder, const gchar *name, gint rate)
2020 {
2021         GtkTreeIter iter;
2022         GtkListStore *store;
2023         gchar *str;
2024         
2025         g_debug("audio_rate_opts_add ()\n");
2026         store = get_combo_box_store(builder, name);
2027         if (!find_combo_item_by_int(GTK_TREE_MODEL(store), rate, &iter))
2028         {
2029                 str = g_strdup_printf ("%d", rate);
2030                 gtk_list_store_append(store, &iter);
2031                 gtk_list_store_set(store, &iter, 
2032                                                    0, str, 
2033                                                    1, TRUE, 
2034                                                    2, str, 
2035                                                    3, rate, 
2036                                                    4, str, 
2037                                                    -1);
2038                 g_free(str);
2039         }
2040 }
2041
2042 static void
2043 audio_bitrate_opts_clean(GtkBuilder *builder, const gchar *name, gint last_rate)
2044 {
2045         GtkTreeIter iter;
2046         GtkListStore *store;
2047         gint ivalue;
2048         gboolean done = FALSE;
2049         gint ii = 0;
2050         guint last = (guint)last_rate;
2051         
2052         g_debug("audio_bitrate_opts_clean ()\n");
2053         store = get_combo_box_store(builder, name);
2054         if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store), &iter))
2055         {
2056                 do
2057                 {
2058                         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 3, &ivalue, -1);
2059                         if (search_rates(
2060                                 hb_audio_bitrates, ivalue, hb_audio_bitrates_count) < 0)
2061                         {
2062                                 done = !gtk_list_store_remove(store, &iter);
2063                         }
2064                         else if (ivalue > last)
2065                         {
2066                                 ii++;
2067                                 gtk_list_store_set(store, &iter, 1, FALSE, -1);
2068                                 done = !gtk_tree_model_iter_next (GTK_TREE_MODEL(store), &iter);
2069                         }
2070                         else
2071                         {
2072                                 ii++;
2073                                 gtk_list_store_set(store, &iter, 1, TRUE, -1);
2074                                 done = !gtk_tree_model_iter_next (GTK_TREE_MODEL(store), &iter);
2075                         }
2076                 } while (!done);
2077         }
2078 }
2079
2080 static void
2081 audio_bitrate_opts_set(GtkBuilder *builder, const gchar *name)
2082 {
2083         GtkTreeIter iter;
2084         GtkListStore *store;
2085         gint ii;
2086         
2087         g_debug("audio_bitrate_opts_set ()\n");
2088         store = get_combo_box_store(builder, name);
2089         gtk_list_store_clear(store);
2090         for (ii = 0; ii < hb_audio_bitrates_count; ii++)
2091         {
2092                 gtk_list_store_append(store, &iter);
2093                 gtk_list_store_set(store, &iter, 
2094                                                    0, hb_audio_bitrates[ii].string, 
2095                                                    1, TRUE, 
2096                                                    2, hb_audio_bitrates[ii].string, 
2097                                                    3, hb_audio_bitrates[ii].rate, 
2098                                                    4, hb_audio_bitrates[ii].string, 
2099                                                    -1);
2100         }
2101 }
2102
2103 void
2104 ghb_set_passthru_bitrate_opts(GtkBuilder *builder, gint bitrate)
2105 {
2106         audio_bitrate_opts_add(builder, "AudioBitrate", bitrate);
2107 }
2108
2109 void
2110 ghb_set_default_bitrate_opts(GtkBuilder *builder, gint last_rate)
2111 {
2112         audio_bitrate_opts_clean(builder, "AudioBitrate", last_rate);
2113 }
2114
2115 static ghb_status_t hb_status;
2116
2117 void
2118 ghb_combo_init(GtkBuilder *builder)
2119 {
2120         // Set up the list model for the combos
2121         init_ui_combo_boxes(builder);
2122         // Populate all the combos
2123         ghb_update_ui_combo_box(builder, NULL, 0, TRUE);
2124 }
2125
2126 void
2127 ghb_backend_init(gint debug)
2128 {
2129     /* Init libhb */
2130     h_scan = hb_init( debug, 0 );
2131     h_queue = hb_init( debug, 0 );
2132 }
2133
2134 void
2135 ghb_backend_close()
2136 {
2137         hb_close(&h_queue);
2138         hb_close(&h_scan);
2139 }
2140
2141 void
2142 ghb_backend_scan(const gchar *path, gint titleindex, gint preview_count)
2143 {
2144     hb_scan( h_scan, path, titleindex, preview_count, 1 );
2145         hb_status.scan.state |= GHB_STATE_SCANNING;
2146         // initialize count and cur to something that won't cause FPE
2147         // when computing progress
2148         hb_status.scan.title_count = 1;
2149         hb_status.scan.title_cur = 0;
2150 }
2151
2152 void
2153 ghb_backend_queue_scan(const gchar *path, gint titlenum)
2154 {
2155         g_debug("ghb_backend_queue_scan()");
2156         hb_scan( h_queue, path, titlenum, 10, 0 );
2157         hb_status.queue.state |= GHB_STATE_SCANNING;
2158 }
2159
2160 gint
2161 ghb_get_scan_state()
2162 {
2163         return hb_status.scan.state;
2164 }
2165
2166 gint
2167 ghb_get_queue_state()
2168 {
2169         return hb_status.queue.state;
2170 }
2171
2172 void
2173 ghb_clear_scan_state(gint state)
2174 {
2175         hb_status.scan.state &= ~state;
2176 }
2177
2178 void
2179 ghb_clear_queue_state(gint state)
2180 {
2181         hb_status.queue.state &= ~state;
2182 }
2183
2184 void
2185 ghb_set_scan_state(gint state)
2186 {
2187         hb_status.scan.state |= state;
2188 }
2189
2190 void
2191 ghb_set_queue_state(gint state)
2192 {
2193         hb_status.queue.state |= state;
2194 }
2195
2196 void
2197 ghb_get_status(ghb_status_t *status)
2198 {
2199         memcpy(status, &hb_status, sizeof(ghb_status_t));
2200 }
2201
2202 void 
2203 ghb_track_status()
2204 {
2205     hb_state_t s_scan;
2206     hb_state_t s_queue;
2207
2208         if (h_scan == NULL) return;
2209     hb_get_state( h_scan, &s_scan );
2210         switch( s_scan.state )
2211     {
2212 #define p s_scan.param.scanning
2213         case HB_STATE_SCANNING:
2214                 {
2215                         hb_status.scan.state |= GHB_STATE_SCANNING;
2216                         hb_status.scan.title_count = p.title_count;
2217                         hb_status.scan.title_cur = p.title_cur;
2218                 } break;
2219 #undef p
2220
2221         case HB_STATE_SCANDONE:
2222         {
2223                         hb_status.scan.state &= ~GHB_STATE_SCANNING;
2224                         hb_status.scan.state |= GHB_STATE_SCANDONE;
2225         } break;
2226
2227 #define p s_scan.param.working
2228         case HB_STATE_WORKING:
2229                         hb_status.scan.state |= GHB_STATE_WORKING;
2230                         hb_status.scan.state &= ~GHB_STATE_PAUSED;
2231                         hb_status.scan.job_cur = p.job_cur;
2232                         hb_status.scan.job_count = p.job_count;
2233                         hb_status.scan.progress = p.progress;
2234                         hb_status.scan.rate_cur = p.rate_cur;
2235                         hb_status.scan.rate_avg = p.rate_avg;
2236                         hb_status.scan.hours = p.hours;
2237                         hb_status.scan.minutes = p.minutes;
2238                         hb_status.scan.seconds = p.seconds;
2239                         hb_status.scan.unique_id = p.sequence_id & 0xFFFFFF;
2240             break;
2241 #undef p
2242
2243         case HB_STATE_PAUSED:
2244                         hb_status.scan.state |= GHB_STATE_PAUSED;
2245             break;
2246                                 
2247         case HB_STATE_MUXING:
2248         {
2249                         hb_status.scan.state |= GHB_STATE_MUXING;
2250         } break;
2251
2252 #define p s_scan.param.workdone
2253         case HB_STATE_WORKDONE:
2254                 {
2255             hb_job_t *job;
2256
2257                         hb_status.scan.state |= GHB_STATE_WORKDONE;
2258                         hb_status.scan.state &= ~GHB_STATE_MUXING;
2259                         hb_status.scan.state &= ~GHB_STATE_PAUSED;
2260                         hb_status.scan.state &= ~GHB_STATE_WORKING;
2261                         switch (p.error)
2262                         {
2263                         case HB_ERROR_NONE:
2264                                 hb_status.scan.error = GHB_ERROR_NONE;
2265                                 break;
2266                         case HB_ERROR_CANCELED:
2267                                 hb_status.scan.error = GHB_ERROR_CANCELED;
2268                                 break;
2269                         default:
2270                                 hb_status.scan.error = GHB_ERROR_FAIL;
2271                                 break;
2272                         }
2273                         // Delete all remaining jobs of this encode.
2274                         // An encode can be composed of multiple associated jobs.
2275                         // When a job is stopped, libhb removes it from the job list,
2276                         // but does not remove other jobs that may be associated with it.
2277                         // Associated jobs are taged in the sequence id.
2278             while ((job = hb_job(h_scan, 0)) != NULL) 
2279                 hb_rem( h_scan, job );
2280                 } break;
2281 #undef p
2282     }
2283     hb_get_state( h_queue, &s_queue );
2284         switch( s_queue.state )
2285     {
2286 #define p s_queue.param.scanning
2287         case HB_STATE_SCANNING:
2288                 {
2289                         hb_status.queue.state |= GHB_STATE_SCANNING;
2290                         hb_status.queue.title_count = p.title_count;
2291                         hb_status.queue.title_cur = p.title_cur;
2292                 } break;
2293 #undef p
2294
2295         case HB_STATE_SCANDONE:
2296         {
2297                         hb_status.queue.state &= ~GHB_STATE_SCANNING;
2298                         hb_status.queue.state |= GHB_STATE_SCANDONE;
2299         } break;
2300
2301 #define p s_queue.param.working
2302         case HB_STATE_WORKING:
2303                         hb_status.queue.state |= GHB_STATE_WORKING;
2304                         hb_status.queue.state &= ~GHB_STATE_PAUSED;
2305                         hb_status.queue.job_cur = p.job_cur;
2306                         hb_status.queue.job_count = p.job_count;
2307                         hb_status.queue.progress = p.progress;
2308                         hb_status.queue.rate_cur = p.rate_cur;
2309                         hb_status.queue.rate_avg = p.rate_avg;
2310                         hb_status.queue.hours = p.hours;
2311                         hb_status.queue.minutes = p.minutes;
2312                         hb_status.queue.seconds = p.seconds;
2313                         hb_status.queue.unique_id = p.sequence_id & 0xFFFFFF;
2314             break;
2315 #undef p
2316
2317         case HB_STATE_PAUSED:
2318                         hb_status.queue.state |= GHB_STATE_PAUSED;
2319             break;
2320                                 
2321         case HB_STATE_MUXING:
2322         {
2323                         hb_status.queue.state |= GHB_STATE_MUXING;
2324         } break;
2325
2326 #define p s_queue.param.workdone
2327         case HB_STATE_WORKDONE:
2328                 {
2329             hb_job_t *job;
2330
2331                         hb_status.queue.state |= GHB_STATE_WORKDONE;
2332                         hb_status.queue.state &= ~GHB_STATE_MUXING;
2333                         hb_status.queue.state &= ~GHB_STATE_PAUSED;
2334                         hb_status.queue.state &= ~GHB_STATE_WORKING;
2335                         switch (p.error)
2336                         {
2337                         case HB_ERROR_NONE:
2338                                 hb_status.queue.error = GHB_ERROR_NONE;
2339                                 break;
2340                         case HB_ERROR_CANCELED:
2341                                 hb_status.queue.error = GHB_ERROR_CANCELED;
2342                                 break;
2343                         default:
2344                                 hb_status.queue.error = GHB_ERROR_FAIL;
2345                                 break;
2346                         }
2347                         // Delete all remaining jobs of this encode.
2348                         // An encode can be composed of multiple associated jobs.
2349                         // When a job is stopped, libhb removes it from the job list,
2350                         // but does not remove other jobs that may be associated with it.
2351                         // Associated jobs are taged in the sequence id.
2352             while ((job = hb_job(h_queue, 0)) != NULL) 
2353                 hb_rem( h_queue, job );
2354                 } break;
2355 #undef p
2356     }
2357 }
2358
2359 gboolean
2360 ghb_get_title_info(ghb_title_info_t *tinfo, gint titleindex)
2361 {
2362         hb_list_t  * list;
2363         hb_title_t * title;
2364         
2365     if (h_scan == NULL) return FALSE;
2366         list = hb_get_titles( h_scan );
2367         if( !hb_list_count( list ) )
2368         {
2369                 /* No valid title, stop right there */
2370                 return FALSE;
2371         }
2372
2373     title = hb_list_item( list, titleindex );
2374         if (title == NULL) return FALSE;        // Bad titleindex
2375         tinfo->width = title->width;
2376         tinfo->height = title->height;
2377         memcpy(tinfo->crop, title->crop, 4 * sizeof(int));
2378         // Don't allow crop to 0
2379         if (title->crop[0] + title->crop[1] >= title->height)
2380                 title->crop[0] = title->crop[1] = 0;
2381         if (title->crop[2] + title->crop[3] >= title->width)
2382                 title->crop[2] = title->crop[3] = 0;
2383         tinfo->num_chapters = hb_list_count(title->list_chapter);
2384         tinfo->rate_base = title->rate_base;
2385         tinfo->rate = title->rate;
2386         hb_reduce(&(tinfo->aspect_n), &(tinfo->aspect_d), 
2387                                 title->width * title->pixel_aspect_width, 
2388                                 title->height * title->pixel_aspect_height);
2389         tinfo->hours = title->hours;
2390         tinfo->minutes = title->minutes;
2391         tinfo->seconds = title->seconds;
2392         tinfo->duration = title->duration;
2393         return TRUE;
2394 }
2395
2396 gboolean
2397 ghb_get_audio_info(ghb_audio_info_t *ainfo, gint titleindex, gint audioindex)
2398 {
2399     hb_audio_config_t *audio;
2400         
2401         audio = get_hb_audio(titleindex, audioindex);
2402         if (audio == NULL) return FALSE; // Bad audioindex
2403         ainfo->codec = audio->in.codec;
2404         ainfo->bitrate = audio->in.bitrate;
2405         ainfo->samplerate = audio->in.samplerate;
2406         return TRUE;
2407 }
2408
2409 gboolean
2410 ghb_audio_is_passthru(gint acodec)
2411 {
2412         g_debug("ghb_audio_is_passthru () \n");
2413         return (acodec == HB_ACODEC_AC3) || (acodec == HB_ACODEC_DCA);
2414 }
2415
2416 gint
2417 ghb_get_default_acodec()
2418 {
2419         return HB_ACODEC_FAAC;
2420 }
2421
2422 void
2423 ghb_set_scale(signal_user_data_t *ud, gint mode)
2424 {
2425         hb_list_t  * list;
2426         hb_title_t * title;
2427         hb_job_t   * job;
2428         gboolean keep_aspect, round_dims, anamorphic;
2429         gboolean autocrop, autoscale, noscale;
2430         gint crop[4], width, height, par_width, par_height;
2431         gint crop_width, crop_height;
2432         gint aspect_n, aspect_d;
2433         gboolean keep_width = (mode == GHB_SCALE_KEEP_WIDTH);
2434         gboolean keep_height = (mode == GHB_SCALE_KEEP_HEIGHT);
2435         gint step;
2436         GtkWidget *widget;
2437         gint modshift;
2438         gint modround;
2439         gint max_width = 0;
2440         gint max_height = 0;
2441         
2442         g_debug("ghb_set_scale ()\n");
2443
2444         if (h_scan == NULL) return;
2445         list = hb_get_titles( h_scan );
2446         if( !hb_list_count( list ) )
2447         {
2448                 /* No valid title, stop right there */
2449                 return;
2450         }
2451         gint titleindex;
2452
2453         titleindex = ghb_settings_combo_int(ud->settings, "title");
2454     title = hb_list_item( list, titleindex );
2455         if (title == NULL) return;
2456         job   = title->job;
2457         if (job == NULL) return;
2458         
2459         // First configure widgets
2460         round_dims = ghb_settings_get_boolean(ud->settings, "ModDimensions");
2461         anamorphic = ghb_settings_get_boolean(ud->settings, "anamorphic");
2462         keep_aspect = ghb_settings_get_boolean(ud->settings, "PictureKeepRatio");
2463         autocrop = ghb_settings_get_boolean(ud->settings, "PictureAutoCrop");
2464         autoscale = ghb_settings_get_boolean(ud->settings, "autoscale");
2465         // "Noscale" is a flag that says we prefer to crop extra to satisfy
2466         // alignment constraints rather than scaling to satisfy them.
2467         noscale = ghb_settings_get_boolean(ud->settings, "noscale");
2468         // Align dimensions to either 16 or 2 pixels
2469         // The scaler crashes if the dimensions are not divisible by 2
2470         // x264 also will not accept dims that are not multiple of 2
2471         modshift = round_dims ? 4 : 1;
2472         modround = round_dims ? 8 : 1;
2473         if (autoscale)
2474         {
2475                 keep_width = FALSE;
2476                 keep_height = FALSE;
2477         }
2478         if (keep_aspect)
2479         {
2480                 keep_height = FALSE;
2481         }
2482         // Step needs to be at least 2 because odd widths cause scaler crash
2483         step = round_dims ? 16 : 2;
2484         widget = GHB_WIDGET (ud->builder, "scale_width");
2485         gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), step, 16);
2486         widget = GHB_WIDGET (ud->builder, "scale_height");
2487         gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), step, 16);
2488         if (autocrop)
2489         {
2490                 ghb_title_info_t tinfo;
2491
2492                 if (ghb_get_title_info (&tinfo, titleindex))
2493                 {
2494                         crop[0] = tinfo.crop[0];
2495                         crop[1] = tinfo.crop[1];
2496                         crop[2] = tinfo.crop[2];
2497                         crop[3] = tinfo.crop[3];
2498                         if (noscale)
2499                         {
2500                                 gint need1, need2;
2501
2502                                 // Adjust the cropping to accomplish the desired width and height
2503                                 crop_width = tinfo.width - crop[2] - crop[3];
2504                                 crop_height = tinfo.height - crop[0] - crop[1];
2505                                 width = (crop_width >> modshift) << modshift;
2506                                 height = (crop_height >> modshift) << modshift;
2507                                 need1 = (crop_height - height) / 2;
2508                                 need2 = crop_height - height - need1;
2509                                 crop[0] += need1;
2510                                 crop[1] += need2;
2511                                 need1 = (crop_width - width) / 2;
2512                                 need2 = crop_width - width - need1;
2513                                 crop[2] += need1;
2514                                 crop[3] += need2;
2515                         }
2516                         ghb_ui_update(ud, "PictureTopCrop", ghb_int64_value(crop[0]));
2517                         ghb_ui_update(ud, "PictureBottomCrop", ghb_int64_value(crop[1]));
2518                         ghb_ui_update(ud, "PictureLeftCrop", ghb_int64_value(crop[2]));
2519                         ghb_ui_update(ud, "PictureRightCrop", ghb_int64_value(crop[3]));
2520                 }
2521         }
2522         crop[0] = ghb_settings_get_int(ud->settings, "PictureTopCrop");
2523         crop[1] = ghb_settings_get_int(ud->settings, "PictureBottomCrop");
2524         crop[2] = ghb_settings_get_int(ud->settings, "PictureLeftCrop");
2525         crop[3] = ghb_settings_get_int(ud->settings, "PictureRightCrop");
2526         hb_reduce(&aspect_n, &aspect_d, 
2527                                 title->width * title->pixel_aspect_width, 
2528                                 title->height * title->pixel_aspect_height);
2529         crop_width = title->width - crop[2] - crop[3];
2530         crop_height = title->height - crop[0] - crop[1];
2531         if (autoscale)
2532         {
2533                 width = crop_width;
2534                 height = crop_height;
2535                 max_width = 0;
2536                 max_height = 0;
2537         }
2538         else
2539         {
2540                 width = ghb_settings_get_int(ud->settings, "scale_width");
2541                 height = ghb_settings_get_int(ud->settings, "scale_height");
2542                 max_width = ghb_settings_get_int(ud->settings, "PictureWidth");
2543                 max_height = ghb_settings_get_int(ud->settings, "PictureHeight");
2544                 // Align max dims 
2545                 max_width = (max_width >> modshift) << modshift;
2546                 max_height = (max_height >> modshift) << modshift;
2547                 // Adjust dims according to max values
2548                 if (!max_height)
2549                 {
2550                         max_height = crop_height;
2551                 }
2552                 if (!max_width)
2553                 {
2554                         max_width = crop_width;
2555                 }
2556                 height = MIN(height, max_height);
2557                 width = MIN(width, max_width);
2558                 g_debug("max_width %d, max_height %d\n", max_width, max_height);
2559         }
2560         if (width < 16)
2561                 width = title->width - crop[2] - crop[3];
2562         if (height < 16)
2563                 height = title->height - crop[0] - crop[1];
2564
2565         if (anamorphic)
2566         {
2567                 job->anamorphic.mode = autoscale ? 2 : 3;
2568                 // The scaler crashes if the dimensions are not divisible by 2
2569                 // Align mod 2.  And so does something in x264_encoder_headers()
2570                 job->anamorphic.modulus = round_dims ? 16 : 2;
2571                 job->width = width;
2572                 job->height = height;
2573                 if (max_height) 
2574                         job->maxHeight = max_height;
2575                 job->crop[0] = crop[0]; job->crop[1] = crop[1];
2576                 job->crop[2] = crop[2]; job->crop[3] = crop[3];
2577                 hb_set_anamorphic_size( job, &width, &height, 
2578                                                                 &par_width, &par_height );
2579         }
2580         else 
2581         {
2582                 job->anamorphic.mode = 0;
2583                 if (keep_aspect)
2584                 {
2585                         gdouble par;
2586                         gint new_width, new_height;
2587                         
2588                         g_debug("kw %s kh %s\n", keep_width ? "y":"n", keep_height ? "y":"n");
2589                         g_debug("w %d h %d\n", width, height);
2590                         // Compute pixel aspect ration.  
2591                         par = (gdouble)(title->height * aspect_n) / (title->width * aspect_d);
2592                         // Must scale so that par becomes 1:1
2593                         // Try to keep largest dimension
2594                         new_height = (crop_height * ((gdouble)width/crop_width) / par);
2595                         new_width = (crop_width * ((gdouble)height/crop_height) * par);
2596                         // Height and width are always multiples of 2, so do the rounding
2597                         new_height = ((new_height + 1) >> 1) << 1;
2598                         new_width = ((new_width + 1) >> 1) << 1;
2599                         if ((max_width && new_width > max_width) || 
2600                                 new_width > title->width)
2601                         {
2602                                 height = new_height;
2603                         }
2604                         else if ((max_height && new_height > max_height) || 
2605                                                 new_height > title->height)
2606                         {
2607                                 width = new_width;
2608                         }
2609                         else if (keep_width)
2610                         {
2611                                 height = new_height;
2612                         }
2613                         else if (keep_height)
2614                         {
2615                                 width = new_width;
2616                         }
2617                         else if (width > new_width)
2618                         {
2619                                 height = new_height;
2620                         }
2621                         else
2622                         {
2623                                 width = new_width;
2624                         }
2625                         g_debug("new w %d h %d\n", width, height);
2626                 }
2627                 width = ((width + modround) >> modshift) << modshift;
2628                 height = ((height + modround) >> modshift) << modshift;
2629         }
2630         ghb_ui_update(ud, "scale_width", ghb_int64_value(width));
2631         ghb_ui_update(ud, "scale_height", ghb_int64_value(height));
2632 }
2633
2634 static void
2635 set_preview_job_settings(hb_job_t *job, GValue *settings)
2636 {
2637         job->crop[0] = ghb_settings_get_int(settings, "PictureTopCrop");
2638         job->crop[1] = ghb_settings_get_int(settings, "PictureBottomCrop");
2639         job->crop[2] = ghb_settings_get_int(settings, "PictureLeftCrop");
2640         job->crop[3] = ghb_settings_get_int(settings, "PictureRightCrop");
2641
2642         gboolean anamorphic, round_dimensions, autoscale;
2643         autoscale = ghb_settings_get_boolean(settings, "autoscale");
2644         anamorphic = ghb_settings_get_boolean(settings, "anamorphic");
2645         round_dimensions = ghb_settings_get_boolean(settings, "ModDimensions");
2646         if (anamorphic)
2647         {
2648                 job->anamorphic.modulus = round_dimensions ? 16 : 2;
2649                 job->anamorphic.mode = autoscale ? 2 : 3;
2650         }
2651         else
2652         {
2653                 job->anamorphic.modulus = 2;
2654                 job->anamorphic.mode = 0;
2655         }
2656         job->width = ghb_settings_get_int(settings, "scale_width");
2657         job->height = ghb_settings_get_int(settings, "scale_height");
2658         gint deint = ghb_settings_combo_int(settings, "PictureDeinterlace");
2659         gboolean decomb = ghb_settings_get_boolean(settings, "PictureDecomb");
2660         job->deinterlace = (!decomb && deint == 0) ? 0 : 1;
2661 }
2662
2663 gint
2664 ghb_calculate_target_bitrate(GValue *settings, gint titleindex)
2665 {
2666         hb_list_t  * list;
2667         hb_title_t * title;
2668         hb_job_t   * job;
2669         gint size;
2670
2671         if (h_scan == NULL) return 1500;
2672         list = hb_get_titles( h_scan );
2673     title = hb_list_item( list, titleindex );
2674         if (title == NULL) return 1500;
2675         job   = title->job;
2676         if (job == NULL) return 1500;
2677         size = ghb_settings_get_int(settings, "VideoTargetSize");
2678         return hb_calc_bitrate( job, size );
2679 }
2680
2681 gboolean
2682 ghb_validate_filter_string(const gchar *str, gint max_fields)
2683 {
2684         gint fields = 0;
2685         gchar *end;
2686         gdouble val;
2687
2688         if (str == NULL || *str == 0) return TRUE;
2689         while (*str)
2690         {
2691                 val = g_strtod(str, &end);
2692                 if (str != end)
2693                 { // Found a numeric value
2694                         fields++;
2695                         // negative max_fields means infinate
2696                         if (max_fields >= 0 && fields > max_fields) return FALSE;
2697                         if (*end == 0)
2698                                 return TRUE;
2699                         if (*end != ':')
2700                                 return FALSE;
2701                         str = end + 1;
2702                 }
2703                 else
2704                         return FALSE;
2705         }
2706         return FALSE;
2707 }
2708
2709 gboolean
2710 ghb_validate_filters(signal_user_data_t *ud)
2711 {
2712         gboolean tweaks;
2713         gchar *str;
2714         gint index;
2715         gchar *message;
2716         gboolean enabled;
2717
2718         tweaks = ghb_settings_get_boolean(ud->settings, "allow_tweaks");
2719         if (tweaks)
2720         {
2721                 // detele 6
2722                 str = ghb_settings_get_string(ud->settings, "tweak_PictureDetelecine");
2723                 enabled = ghb_settings_get_boolean(ud->settings, "PictureDetelecine");
2724                 if (enabled && !ghb_validate_filter_string(str, 6))
2725                 {
2726                         message = g_strdup_printf(
2727                                                 "Invalid Detelecine Settings:\n\n%s\n",
2728                                                 str);
2729                         ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
2730                         g_free(str);
2731                         g_free(message);
2732                         return FALSE;
2733                 }
2734                 g_free(str);
2735                 // decomb 7
2736                 str = ghb_settings_get_string(ud->settings, "tweak_PictureDecomb");
2737                 enabled = ghb_settings_get_boolean(ud->settings, "PictureDecomb");
2738                 if (enabled && !ghb_validate_filter_string(str, 7))
2739                 {
2740                         message = g_strdup_printf(
2741                                                 "Invalid Decomb Settings:\n\n%s\n",
2742                                                 str);
2743                         ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
2744                         g_free(str);
2745                         g_free(message);
2746                         return FALSE;
2747                 }
2748                 g_free(str);
2749                 // deinte 4
2750                 index = ghb_lookup_combo_int("tweak_PictureDeinterlace", 
2751                         ghb_settings_get_value(ud->settings, "tweak_PictureDeinterlace"));
2752                 if (index < 0)
2753                 {
2754                         str = ghb_settings_get_string(ud->settings, "tweak_PictureDeinterlace");
2755                         if (!ghb_validate_filter_string(str, 4))
2756                         {
2757                                 message = g_strdup_printf(
2758                                                         "Invalid Deinterlace Settings:\n\n%s\n",
2759                                                         str);
2760                                 ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
2761                                 g_free(message);
2762                                 g_free(str);
2763                                 return FALSE;
2764                         }
2765                         g_free(str);
2766                 }
2767                 // denois 4
2768                 index = ghb_lookup_combo_int("tweak_PictureDenoise", 
2769                                 ghb_settings_get_value(ud->settings, "tweak_PictureDenoise"));
2770                 if (index < 0)
2771                 {
2772                         str = ghb_settings_get_string(ud->settings, "tweak_PictureDenoise");
2773                         if (!ghb_validate_filter_string(str, 4))
2774                         {
2775                                 message = g_strdup_printf(
2776                                                         "Invalid Denoise Settings:\n\n%s\n",
2777                                                         str);
2778                                 ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
2779                                 g_free(str);
2780                                 g_free(message);
2781                                 return FALSE;
2782                         }
2783                         g_free(str);
2784                 }
2785         }
2786         return TRUE;
2787 }
2788
2789 gboolean
2790 ghb_validate_video(signal_user_data_t *ud)
2791 {
2792         gint vcodec, mux;
2793         gchar *message;
2794
2795         mux = ghb_settings_combo_int(ud->settings, "FileFormat");
2796         vcodec = ghb_settings_combo_int(ud->settings, "VideoEncoder");
2797         if ((mux == HB_MUX_MP4 || mux == HB_MUX_AVI) && 
2798                 (vcodec == HB_VCODEC_THEORA))
2799         {
2800                 // mp4|avi/theora combination is not supported.
2801                 message = g_strdup_printf(
2802                                         "Theora is not supported in the MP4 and AVI containers.\n\n"
2803                                         "You should choose a different video codec or container.\n"
2804                                         "If you continue, XviD will be chosen for you.");
2805                 if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Cancel", "Continue"))
2806                 {
2807                         g_free(message);
2808                         return FALSE;
2809                 }
2810                 g_free(message);
2811                 vcodec = HB_VCODEC_XVID;
2812                 ghb_ui_update(ud, "VideoEncoder", ghb_int64_value(vcodec));
2813         }
2814         return TRUE;
2815 }
2816
2817 gboolean
2818 ghb_validate_audio(signal_user_data_t *ud)
2819 {
2820         hb_list_t  * list;
2821         hb_title_t * title;
2822         gchar *message;
2823         GValue *value;
2824
2825         if (h_scan == NULL) return FALSE;
2826         list = hb_get_titles( h_scan );
2827         if( !hb_list_count( list ) )
2828         {
2829                 /* No valid title, stop right there */
2830                 g_message("No title found.\n");
2831                 return FALSE;
2832         }
2833
2834         gint titleindex;
2835
2836         titleindex = ghb_settings_combo_int(ud->settings, "title");
2837     title = hb_list_item( list, titleindex );
2838         if (title == NULL) return FALSE;
2839         gint mux = ghb_settings_combo_int(ud->settings, "FileFormat");
2840
2841         const GValue *audio_list;
2842         gint count, ii;
2843
2844         audio_list = ghb_settings_get_value(ud->settings, "audio_list");
2845         count = ghb_array_len(audio_list);
2846         for (ii = 0; ii < count; ii++)
2847         {
2848                 GValue *asettings;
2849             hb_audio_config_t *taudio;
2850
2851                 asettings = ghb_array_get_nth(audio_list, ii);
2852                 gint track = ghb_settings_combo_int(asettings, "AudioTrack");
2853                 gint codec = ghb_settings_combo_int(asettings, "AudioEncoder");
2854         taudio = (hb_audio_config_t *) hb_list_audio_config_item(
2855                                                                                         title->list_audio, track );
2856                 if ((taudio->in.codec != HB_ACODEC_AC3 && codec == HB_ACODEC_AC3) ||
2857                     (taudio->in.codec != HB_ACODEC_DCA && codec == HB_ACODEC_DCA))
2858                 {
2859                         // Not supported.  AC3 is passthrough only, so input must be AC3
2860                         char *str;
2861                         str = (codec == HB_ACODEC_AC3) ? "AC-3" : "DTS";
2862                         message = g_strdup_printf(
2863                                                 "The source does not support %s Pass-Thru.\n\n"
2864                                                 "You should choose a different audio codec.\n"
2865                                                 "If you continue, one will be chosen for you.", str);
2866                         if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Cancel", "Continue"))
2867                         {
2868                                 g_free(message);
2869                                 return FALSE;
2870                         }
2871                         g_free(message);
2872                         if (mux == HB_MUX_AVI)
2873                         {
2874                                 codec = HB_ACODEC_LAME;
2875                         }
2876                         else
2877                         {
2878                                 codec = HB_ACODEC_FAAC;
2879                         }
2880                         value = get_acodec_value(codec);
2881                         ghb_settings_take_value(asettings, "AudioEncoder", value);
2882                 }
2883                 gchar *a_unsup = NULL;
2884                 gchar *mux_s = NULL;
2885                 if (mux == HB_MUX_MP4)
2886                 { 
2887                         mux_s = "MP4";
2888                         // mp4/mp3|vorbis combination is not supported.
2889                         if (codec == HB_ACODEC_LAME)
2890                         {
2891                                 a_unsup = "MP3";
2892                                 codec = HB_ACODEC_FAAC;
2893                         }
2894                         if (codec == HB_ACODEC_VORBIS)
2895                         {
2896                                 a_unsup = "Vorbis";
2897                                 codec = HB_ACODEC_FAAC;
2898                         }
2899                 }
2900                 else if (mux == HB_MUX_AVI)
2901                 {
2902                         mux_s = "AVI";
2903                         // avi/faac|vorbis combination is not supported.
2904                         if (codec == HB_ACODEC_FAAC)
2905                         {
2906                                 a_unsup = "FAAC";
2907                                 codec = HB_ACODEC_LAME;
2908                         }
2909                         if (codec == HB_ACODEC_VORBIS)
2910                         {
2911                                 a_unsup = "Vorbis";
2912                                 codec = HB_ACODEC_LAME;
2913                         }
2914                 }
2915                 else if (mux == HB_MUX_OGM)
2916                 {
2917                         mux_s = "OGM";
2918                         // avi/faac|vorbis combination is not supported.
2919                         if (codec == HB_ACODEC_FAAC)
2920                         {
2921                                 a_unsup = "FAAC";
2922                                 codec = HB_ACODEC_VORBIS;
2923                         }
2924                         if (codec == HB_ACODEC_AC3)
2925                         {
2926                                 a_unsup = "AC-3";
2927                                 codec = HB_ACODEC_VORBIS;
2928                         }
2929                         if (codec == HB_ACODEC_DCA)
2930                         {
2931                                 a_unsup = "DTS";
2932                                 codec = HB_ACODEC_VORBIS;
2933                         }
2934                 }
2935                 if (a_unsup)
2936                 {
2937                         message = g_strdup_printf(
2938                                                 "%s is not supported in the %s container.\n\n"
2939                                                 "You should choose a different audio codec.\n"
2940                                                 "If you continue, one will be chosen for you.", a_unsup, mux_s);
2941                         if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Cancel", "Continue"))
2942                         {
2943                                 g_free(message);
2944                                 return FALSE;
2945                         }
2946                         g_free(message);
2947                         value = get_acodec_value(codec);
2948                         ghb_settings_take_value(asettings, "AudioEncoder", value);
2949                 }
2950                 gint mix = ghb_settings_combo_int (asettings, "AudioMixdown");
2951                 gboolean allow_mono = TRUE;
2952                 gboolean allow_stereo = TRUE;
2953                 gboolean allow_dolby = TRUE;
2954                 gboolean allow_dpl2 = TRUE;
2955                 gboolean allow_6ch = TRUE;
2956                 allow_mono =
2957                         (taudio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
2958                         (codec != HB_ACODEC_LAME);
2959                 gint layout = taudio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
2960                 allow_stereo =
2961                         ((layout == HB_INPUT_CH_LAYOUT_MONO && !allow_mono) || layout >= HB_INPUT_CH_LAYOUT_STEREO);
2962                 allow_dolby =
2963                         (layout == HB_INPUT_CH_LAYOUT_3F1R) || 
2964                         (layout == HB_INPUT_CH_LAYOUT_3F2R) || 
2965                         (layout == HB_INPUT_CH_LAYOUT_DOLBY);
2966                 allow_dpl2 = (layout == HB_INPUT_CH_LAYOUT_3F2R);
2967                 allow_6ch =
2968                         (taudio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
2969                         (codec != HB_ACODEC_LAME) &&
2970                         (layout == HB_INPUT_CH_LAYOUT_3F2R) && 
2971                         (taudio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE);
2972
2973                 gchar *mix_unsup = NULL;
2974                 if (mix == HB_AMIXDOWN_MONO && !allow_mono)
2975                 {
2976                         mix_unsup = "mono";
2977                 }
2978                 if (mix == HB_AMIXDOWN_STEREO && !allow_stereo)
2979                 {
2980                         mix_unsup = "stereo";
2981                 }
2982                 if (mix == HB_AMIXDOWN_DOLBY && !allow_dolby)
2983                 {
2984                         mix_unsup = "Dolby";
2985                 }
2986                 if (mix == HB_AMIXDOWN_DOLBYPLII && !allow_dpl2)
2987                 {
2988                         mix_unsup = "Dolby Pro Logic II";
2989                 }
2990                 if (mix == HB_AMIXDOWN_6CH && !allow_6ch)
2991                 {
2992                         mix_unsup = "6 Channel";
2993                 }
2994                 if (mix_unsup)
2995                 {
2996                         message = g_strdup_printf(
2997                                                 "The source audio does not support %s mixdown.\n\n"
2998                                                 "You should choose a different mixdown.\n"
2999                                                 "If you continue, one will be chosen for you.", mix_unsup);
3000                         if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Cancel", "Continue"))
3001                         {
3002                                 g_free(message);
3003                                 return FALSE;
3004                         }
3005                         g_free(message);
3006                         mix = ghb_get_best_mix(titleindex, track, codec, mix);
3007                         value = get_amix_value(mix);
3008                         ghb_settings_take_value(asettings, "AudioMixdown", value);
3009                 }
3010         }
3011         return TRUE;
3012 }
3013
3014 gboolean
3015 ghb_validate_vquality(GValue *settings)
3016 {
3017         gint vcodec;
3018         gchar *message;
3019         gint min, max;
3020
3021         if (ghb_settings_get_boolean(settings, "nocheckvquality")) return TRUE;
3022         vcodec = ghb_settings_combo_int(settings, "VideoEncoder");
3023         gdouble vquality;
3024         vquality = ghb_settings_get_double(settings, "VideoQualitySlider");
3025         if (ghb_settings_get_boolean(settings, "vquality_type_constant"))
3026         {
3027                 if (!ghb_settings_get_boolean(settings, "directqp"))
3028                 {
3029                         vquality *= 100.0;
3030                         if (vcodec != HB_VCODEC_X264)
3031                         {
3032                                 min = 68;
3033                                 max = 97;
3034                         }
3035                         else
3036                         {
3037                                 min = 40;
3038                                 max = 70;
3039                         }
3040                 }
3041                 else
3042                 {
3043                         if (vcodec == HB_VCODEC_X264)
3044                         {
3045                                 min = 16;
3046                                 max = 30;
3047                         }
3048                         else if (vcodec == HB_VCODEC_FFMPEG)
3049                         {
3050                                 min = 1;
3051                                 max = 8;
3052                         }
3053                         else
3054                         {
3055                                 min = 68;
3056                                 max = 97;
3057                                 vquality *= 100.0;
3058                         }
3059                 }
3060                 if (vquality < min || vquality > max)
3061                 {
3062                         message = g_strdup_printf(
3063                                                 "Interesting video quality choise: %d\n\n"
3064                                                 "Typical values range from %d to %d.\n"
3065                                                 "Are you sure you wish to use this setting?",
3066                                                 (gint)vquality, min, max);
3067                         if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, 
3068                                                                         "Cancel", "Continue"))
3069                         {
3070                                 g_free(message);
3071                                 return FALSE;
3072                         }
3073                         g_free(message);
3074                 }
3075         }
3076         return TRUE;
3077 }
3078
3079 static void
3080 add_job(hb_handle_t *h, GValue *js, gint unique_id, gint titleindex)
3081 {
3082         hb_list_t  * list;
3083         hb_title_t * title;
3084         hb_job_t   * job;
3085         static gchar *x264opts;
3086         gint sub_id = 0;
3087         gboolean tweaks = FALSE;
3088         gchar *detel_str = NULL;
3089         gchar *decomb_str = NULL;
3090         gchar *deint_str = NULL;
3091         gchar *deblock_str = NULL;
3092         gchar *denoise_str = NULL;
3093         gchar *dest_str = NULL;
3094
3095         g_debug("add_job()\n");
3096         if (h == NULL) return;
3097         list = hb_get_titles( h );
3098         if( !hb_list_count( list ) )
3099         {
3100                 /* No valid title, stop right there */
3101                 return;
3102         }
3103
3104     title = hb_list_item( list, titleindex );
3105         if (title == NULL) return;
3106
3107         /* Set job settings */
3108         job   = title->job;
3109         if (job == NULL) return;
3110
3111         job->start_at_preview = ghb_settings_get_int(js, "start_frame") + 1;
3112         if (job->start_at_preview)
3113         {
3114                 job->seek_points = ghb_settings_get_int(js, "preview_count");
3115                 job->pts_to_stop = ghb_settings_get_int(js, "live_duration") * 90000LL;
3116         }
3117
3118         tweaks = ghb_settings_get_boolean(js, "allow_tweaks");
3119         job->mux = ghb_settings_combo_int(js, "FileFormat");
3120         if (job->mux == HB_MUX_MP4)
3121         {
3122                 job->largeFileSize = ghb_settings_get_boolean(js, "Mp4LargeFile");
3123                 job->mp4_optimize = ghb_settings_get_boolean(js, "Mp4HttpOptimize");
3124         }
3125         else
3126         {
3127                 job->largeFileSize = FALSE;
3128                 job->mp4_optimize = FALSE;
3129         }
3130         if (!job->start_at_preview)
3131         {
3132                 gint chapter_start, chapter_end;
3133                 chapter_start = ghb_settings_get_int(js, "start_chapter");
3134                 chapter_end = ghb_settings_get_int(js, "end_chapter");
3135                 gint num_chapters = hb_list_count(title->list_chapter);
3136                 job->chapter_start = MIN( num_chapters, chapter_start );
3137                 job->chapter_end   = MAX( job->chapter_start, chapter_end );
3138
3139                 job->chapter_markers = ghb_settings_get_boolean(js, "ChapterMarkers");
3140                 if ( job->chapter_markers )
3141                 {
3142                         GValue *chapters;
3143                         GValue *chapter;
3144                         gint chap;
3145                         gint count;
3146                 
3147                         chapters = ghb_settings_get_value(js, "chapter_list");
3148                         count = ghb_array_len(chapters);
3149                         for(chap = chapter_start; chap <= chapter_end; chap++)
3150                         {
3151                                 hb_chapter_t * chapter_s;
3152                                 gchar *name;
3153                                 
3154                                 name = NULL;
3155                                 if (chap-1 < count)
3156                                 {
3157                                         chapter = ghb_array_get_nth(chapters, chap-1);
3158                                         name = ghb_value_string(chapter); 
3159                                 }
3160                                 if (name == NULL)
3161                                 {
3162                                         name = g_strdup_printf ("Chapter %2d", chap);
3163                                 }
3164                                 chapter_s = hb_list_item( job->title->list_chapter, chap - 1);
3165                                 strncpy(chapter_s->title, name, 1023);
3166                                 chapter_s->title[1023] = '\0';
3167                                 g_free(name);
3168                         }
3169                 }
3170         }
3171         job->crop[0] = ghb_settings_get_int(js, "PictureTopCrop");
3172         job->crop[1] = ghb_settings_get_int(js, "PictureBottomCrop");
3173         job->crop[2] = ghb_settings_get_int(js, "PictureLeftCrop");
3174         job->crop[3] = ghb_settings_get_int(js, "PictureRightCrop");
3175
3176         
3177         gboolean decomb = ghb_settings_get_boolean(js, "PictureDecomb");
3178         gint deint = ghb_settings_combo_int(js, 
3179                                         tweaks ? "tweak_PictureDeinterlace":"PictureDeinterlace");
3180         if (!decomb)
3181                 job->deinterlace = (deint != 0) ? 1 : 0;
3182         else
3183                 job->deinterlace = 0;
3184     job->grayscale   = ghb_settings_get_boolean(js, "VideoGrayScale");
3185
3186         gboolean autoscale = ghb_settings_get_boolean(js, "autoscale");
3187         gboolean anamorphic = ghb_settings_get_boolean(js, "anamorphic");
3188         gboolean round_dimensions = ghb_settings_get_boolean(js, "ModDimensions");
3189         if (anamorphic)
3190         {
3191                 job->anamorphic.mode = autoscale ? 2 : 3;
3192                 // Also, x264 requires things to be divisible by 2.
3193                 job->anamorphic.modulus = round_dimensions ? 16 : 2;
3194         }
3195         else
3196         {
3197                 job->anamorphic.mode = 0;
3198                 job->anamorphic.modulus = 2;
3199         }
3200         /* Add selected filters */
3201         job->filters = hb_list_init();
3202         gint vrate = ghb_settings_combo_int(js, "VideoFramerate");
3203         if( vrate == 0 && ghb_settings_get_boolean(js, "PictureDetelecine" ) )
3204                 job->vfr = 1;
3205         else
3206                 job->vfr = 0;
3207
3208         if( ghb_settings_get_boolean(js, "PictureDetelecine" ) )
3209         {
3210                 hb_filter_detelecine.settings = NULL;
3211                 if (tweaks)
3212                 {
3213                         detel_str = ghb_settings_get_string(js, "tweak_PictureDetelecine");
3214                         if (detel_str && detel_str[0])
3215                         {
3216                                 hb_filter_detelecine.settings = detel_str;
3217                         }
3218                 }
3219                 hb_list_add( job->filters, &hb_filter_detelecine );
3220         }
3221         if( decomb )
3222         {
3223                 // Use default settings
3224                 hb_filter_decomb.settings = NULL;
3225                 if (tweaks)
3226                 {
3227                         decomb_str = ghb_settings_get_string(js, "tweak_PictureDecomb");
3228                         if (decomb_str && decomb_str[0])
3229                         {
3230                                 hb_filter_decomb.settings = (gchar*)decomb_str;
3231                         }
3232                 }
3233                 hb_list_add( job->filters, &hb_filter_decomb );
3234         }
3235         if( job->deinterlace )
3236         {
3237                 if (deint > 0)
3238                         deint_str = g_strdup(deint_opts.map[deint].svalue);
3239                 else
3240                         deint_str = ghb_settings_get_string(js, 
3241                                         tweaks ? "tweak_PictureDeinterlace" : "PictureDeinterlace");
3242                 hb_filter_deinterlace.settings = deint_str;
3243                 hb_list_add( job->filters, &hb_filter_deinterlace );
3244         }
3245         gint deblock = ghb_settings_get_int(js, "PictureDeblock");
3246         if( deblock >= 5 )
3247         {
3248                 deblock_str = g_strdup_printf("%d", deblock);
3249                 hb_filter_deblock.settings = deblock_str;
3250                 hb_list_add( job->filters, &hb_filter_deblock );
3251         }
3252         gint denoise = ghb_settings_combo_int(js, 
3253                                         tweaks ? "tweak_PictureDenoise" : "PictureDenoise");
3254         if( denoise != 0 )
3255         {
3256                 if (denoise > 0)
3257                         denoise_str = g_strdup(denoise_opts.map[denoise].svalue);
3258                 else
3259                         denoise_str = (gchar*)ghb_settings_get_string(
3260                                 js, tweaks ? "tweak_PictureDenoise" : "PictureDenoise");
3261                 hb_filter_denoise.settings = denoise_str;
3262                 hb_list_add( job->filters, &hb_filter_denoise );
3263         }
3264         job->width = ghb_settings_get_int(js, "scale_width");
3265         job->height = ghb_settings_get_int(js, "scale_height");
3266
3267         job->vcodec = ghb_settings_combo_int(js, "VideoEncoder");
3268         if ((job->mux == HB_MUX_MP4 || job->mux == HB_MUX_AVI) && 
3269                 (job->vcodec == HB_VCODEC_THEORA))
3270         {
3271                 // mp4|avi/theora combination is not supported.
3272                 job->vcodec = HB_VCODEC_XVID;
3273         }
3274         if ((job->vcodec == HB_VCODEC_X264) && (job->mux == HB_MUX_MP4))
3275         {
3276                 job->ipod_atom = ghb_settings_get_boolean(js, "Mp4iPodCompatible");
3277         }
3278         if (ghb_settings_get_boolean(js, "vquality_type_constant"))
3279         {
3280                 gdouble vquality;
3281                 vquality = ghb_settings_get_double(js, "VideoQualitySlider");
3282                 if (!ghb_settings_get_boolean(js, "directqp"))
3283                 {
3284                         if (vquality == 0.0) vquality = 0.01;
3285                         if (vquality == 1.0) vquality = 0.0;
3286                 }
3287                 job->vquality =  vquality;
3288                 job->vbitrate = 0;
3289         }
3290         else if (ghb_settings_get_boolean(js, "vquality_type_bitrate"))
3291         {
3292                 job->vquality = -1.0;
3293                 job->vbitrate = ghb_settings_get_int(js, "VideoAvgBitrate");
3294         }
3295         // AVI container does not support variable frame rate.
3296         if (job->mux == HB_MUX_AVI)
3297         {
3298                 job->vfr = FALSE;
3299                 job->cfr = 1;
3300         }
3301
3302         if( vrate == 0 )
3303         {
3304                 job->vrate = title->rate;
3305                 job->vrate_base = title->rate_base;
3306                 job->cfr = 0;
3307         }
3308         else
3309         {
3310                 job->vrate = 27000000;
3311                 job->vrate_base = vrate;
3312                 job->cfr = 1;
3313         }
3314         // First remove any audios that are already in the list
3315         // This happens if you are encoding the same title a second time.
3316         gint num_audio_tracks = hb_list_count(job->list_audio);
3317         gint ii;
3318     for(ii = 0; ii < num_audio_tracks; ii++)
3319     {
3320         hb_audio_t *audio = (hb_audio_t*)hb_list_item(job->list_audio, 0);
3321         hb_list_rem(job->list_audio, audio);
3322     }
3323
3324         const GValue *audio_list;
3325         gint count;
3326         gint tcount = 0;
3327         
3328         audio_list = ghb_settings_get_value(js, "audio_list");
3329         count = ghb_array_len(audio_list);
3330         for (ii = 0; ii < count; ii++)
3331         {
3332                 GValue *asettings;
3333             hb_audio_config_t audio;
3334             hb_audio_config_t *taudio;
3335
3336                 hb_audio_config_init(&audio);
3337                 asettings = ghb_array_get_nth(audio_list, ii);
3338                 audio.in.track = ghb_settings_get_int(asettings, "AudioTrack");
3339                 audio.out.track = tcount;
3340                 audio.out.codec = ghb_settings_combo_int(asettings, "AudioEncoder");
3341         taudio = (hb_audio_config_t *) hb_list_audio_config_item(
3342                                                                         title->list_audio, audio.in.track );
3343                 if ((taudio->in.codec != HB_ACODEC_AC3 && 
3344              audio.out.codec == HB_ACODEC_AC3) ||
3345                     (taudio->in.codec != HB_ACODEC_DCA && 
3346                          audio.out.codec == HB_ACODEC_DCA))
3347                 {
3348                         // Not supported.  AC3 is passthrough only, so input must be AC3
3349                         if (job->mux == HB_MUX_AVI)
3350                         {
3351                                 audio.out.codec = HB_ACODEC_LAME;
3352                         }
3353                         else
3354                         {
3355 printf("switching to faac\n");
3356                                 audio.out.codec = HB_ACODEC_FAAC;
3357                         }
3358                 }
3359                 if ((job->mux == HB_MUX_MP4) && 
3360                         ((audio.out.codec == HB_ACODEC_LAME) ||
3361                         (audio.out.codec == HB_ACODEC_VORBIS)))
3362                 {
3363                         // mp4/mp3|vorbis combination is not supported.
3364                         audio.out.codec = HB_ACODEC_FAAC;
3365                 }
3366                 if ((job->mux == HB_MUX_AVI) && 
3367                         ((audio.out.codec == HB_ACODEC_FAAC) ||
3368                         (audio.out.codec == HB_ACODEC_VORBIS)))
3369                 {
3370                         // avi/faac|vorbis combination is not supported.
3371                         audio.out.codec = HB_ACODEC_LAME;
3372                 }
3373                 if ((job->mux == HB_MUX_OGM) && 
3374                         ((audio.out.codec == HB_ACODEC_FAAC) ||
3375                         (audio.out.codec == HB_ACODEC_AC3) ||
3376                         (audio.out.codec == HB_ACODEC_DCA)))
3377                 {
3378                         // ogm/faac|ac3 combination is not supported.
3379                         audio.out.codec = HB_ACODEC_VORBIS;
3380                 }
3381         audio.out.dynamic_range_compression = 
3382                         ghb_settings_get_double(asettings, "AudioTrackDRCSlider");
3383                 // It would be better if this were done in libhb for us, but its not yet.
3384                 if (audio.out.codec == HB_ACODEC_AC3 || audio.out.codec == HB_ACODEC_DCA)
3385                 {
3386                         audio.out.mixdown = 0;
3387                 }
3388                 else
3389                 {
3390                         audio.out.mixdown = ghb_settings_combo_int(asettings, "AudioMixdown");
3391                         // Make sure the mixdown is valid and pick a new one if not.
3392                         audio.out.mixdown = ghb_get_best_mix(titleindex, 
3393                                 audio.in.track, audio.out.codec, audio.out.mixdown);
3394                         audio.out.bitrate = 
3395                                 ghb_settings_combo_int(asettings, "AudioBitrate");
3396                         gint srate = ghb_settings_combo_int(asettings, "AudioSamplerate");
3397                         if (srate == 0) // 0 is same as source
3398                                 audio.out.samplerate = taudio->in.samplerate;
3399                         else
3400                                 audio.out.samplerate = srate;
3401                 }
3402
3403                 // Add it to the jobs audio list
3404         hb_audio_add( job, &audio );
3405                 tcount++;
3406         }
3407         // I was tempted to move this up with the reset of the video quality
3408         // settings, but I suspect the settings above need to be made
3409         // first in order for hb_calc_bitrate to be accurate.
3410         if (ghb_settings_get_boolean(js, "vquality_type_target"))
3411         {
3412                 gint size;
3413                 
3414                 size = ghb_settings_get_int(js, "VideoTargetSize");
3415         job->vbitrate = hb_calc_bitrate( job, size );
3416                 job->vquality = -1.0;
3417         }
3418
3419         dest_str = ghb_settings_get_string(js, "destination");
3420         job->file = dest_str;
3421         job->crf = ghb_settings_get_boolean(js, "constant_rate_factor");
3422         // TODO: libhb holds onto a reference to the x264opts and is not
3423         // finished with it until encoding the job is done.  But I can't
3424         // find a way to get at the job before it is removed in order to
3425         // free up the memory I am allocating here.
3426         // The short story is THIS LEAKS.
3427         x264opts = ghb_build_x264opts_string(js);
3428         
3429         if( x264opts != NULL && *x264opts != '\0' )
3430         {
3431                 job->x264opts = x264opts;
3432         }
3433         else /*avoids a bus error crash when options aren't specified*/
3434         {
3435                 job->x264opts =  NULL;
3436         }
3437         gint subtitle;
3438         gchar *slang = ghb_settings_get_string(js, "Subtitles");
3439         subtitle = -2; // default to none
3440         if (strcmp(slang, "auto") == 0)
3441         {
3442                 subtitle = -1;
3443         }
3444         else
3445         {
3446                 gint scount;
3447         hb_subtitle_t * subt;
3448
3449                 scount = hb_list_count(title->list_subtitle);
3450                 for (ii = 0; ii < scount; ii++)
3451                 {
3452                 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, ii);
3453                         if (strcmp(slang, subt->iso639_2) == 0)
3454                         {
3455                                 subtitle = ii;
3456                                 break;
3457                         }
3458                 }
3459         }
3460         gboolean forced_subtitles = ghb_settings_get_boolean(js, "SubtitlesForced");
3461         job->subtitle_force = forced_subtitles;
3462         if (subtitle >= 0)
3463                 job->subtitle = subtitle;
3464         else
3465                 job->subtitle = -1;
3466         if (subtitle == -1)
3467         {
3468                 // Subtitle scan. Look for subtitle matching audio language
3469                 char *x264opts_tmp;
3470
3471                 /*
3472                  * When subtitle scan is enabled do a fast pre-scan job
3473                  * which will determine which subtitles to enable, if any.
3474                  */
3475                 job->pass = -1;
3476                 job->indepth_scan = 1;
3477
3478                 x264opts_tmp = job->x264opts;
3479                 job->x264opts = NULL;
3480
3481                 job->select_subtitle = malloc(sizeof(hb_subtitle_t*));
3482                 *(job->select_subtitle) = NULL;
3483
3484                 /*
3485                  * Add the pre-scan job
3486                  */
3487                 job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24);
3488                 hb_add( h, job );
3489                 //if (job->x264opts != NULL)
3490                 //      g_free(job->x264opts);
3491
3492                 job->x264opts = x264opts_tmp;
3493         }
3494         else
3495         {
3496                 job->select_subtitle = NULL;
3497         }
3498         if( ghb_settings_get_boolean(js, "VideoTwoPass") &&
3499                 !ghb_settings_get_boolean(js, "vquality_type_constant"))
3500         {
3501                 /*
3502                  * If subtitle_scan is enabled then only turn it on
3503                  * for the second pass and then off again for the
3504                  * second.
3505                  */
3506                 hb_subtitle_t **subtitle_tmp = job->select_subtitle;
3507                 job->select_subtitle = NULL;
3508                 job->pass = 1;
3509                 job->indepth_scan = 0;
3510                 gchar *x264opts2 = NULL;
3511                 if (x264opts)
3512                 {
3513                         x264opts2 = g_strdup(x264opts);
3514                 }
3515                 /*
3516                  * If turbo options have been selected then append them
3517                  * to the x264opts now (size includes one ':' and the '\0')
3518                  */
3519                 if( ghb_settings_get_boolean(js, "VideoTurboTwoPass") )
3520                 {
3521                         char *tmp_x264opts;
3522
3523                         if ( x264opts )
3524                         {
3525                                 tmp_x264opts = g_strdup_printf("%s:%s", x264opts, turbo_opts);
3526                                 g_free(x264opts);
3527                         } 
3528                         else 
3529                         {
3530                                 /*
3531                                  * No x264opts to modify, but apply the turbo options
3532                                  * anyway as they may be modifying defaults
3533                                  */
3534                                 tmp_x264opts = g_strdup_printf("%s", turbo_opts);
3535                         }
3536                         x264opts = tmp_x264opts;
3537
3538                         job->x264opts = x264opts;
3539                 }
3540                 job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24);
3541                 hb_add( h, job );
3542                 //if (job->x264opts != NULL)
3543                 //      g_free(job->x264opts);
3544
3545                 job->select_subtitle = subtitle_tmp;
3546                 job->pass = 2;
3547                 /*
3548                  * On the second pass we turn off subtitle scan so that we
3549                  * can actually encode using any subtitles that were auto
3550                  * selected in the first pass (using the whacky select-subtitle
3551                  * attribute of the job).
3552                  */
3553                 job->indepth_scan = 0;
3554                 job->x264opts = x264opts2;
3555                 job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24);
3556                 hb_add( h, job );
3557                 //if (job->x264opts != NULL)
3558                 //      g_free(job->x264opts);
3559         }
3560         else
3561         {
3562                 job->indepth_scan = 0;
3563                 job->pass = 0;
3564                 job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24);
3565                 hb_add( h, job );
3566                 //if (job->x264opts != NULL)
3567                 //      g_free(job->x264opts);
3568         }
3569         if (detel_str) g_free(detel_str);
3570         if (decomb_str) g_free(decomb_str);
3571         if (deint_str) g_free(deint_str);
3572         if (deblock_str) g_free(deblock_str);
3573         if (denoise_str) g_free(denoise_str);
3574         if (dest_str) g_free(dest_str);
3575 }
3576
3577 void
3578 ghb_add_job(GValue *js, gint unique_id)
3579 {
3580         // Since I'm doing a scan of the single title I want just prior 
3581         // to adding the job, there is only the one title to choose from.
3582         add_job(h_queue, js, unique_id, 0);
3583 }
3584
3585 void
3586 ghb_add_live_job(GValue *js, gint unique_id)
3587 {
3588         // Since I'm doing a scan of the single title I want just prior 
3589         // to adding the job, there is only the one title to choose from.
3590         gint titleindex = ghb_settings_combo_int(js, "title");
3591         add_job(h_scan, js, unique_id, titleindex);
3592 }
3593
3594 void
3595 ghb_remove_job(gint unique_id)
3596 {
3597     hb_job_t * job;
3598     gint ii;
3599         
3600         // Multiples passes all get the same id
3601         // remove them all.
3602         // Go backwards through list, so reordering doesn't screw me.
3603         ii = hb_count(h_queue) - 1;
3604     while ((job = hb_job(h_queue, ii--)) != NULL)
3605     {
3606         if ((job->sequence_id & 0xFFFFFF) == unique_id)
3607                         hb_rem(h_queue, job);
3608     }
3609 }
3610
3611 void
3612 ghb_start_queue()
3613 {
3614         hb_start( h_queue );
3615 }
3616
3617 void
3618 ghb_stop_queue()
3619 {
3620         hb_stop( h_queue );
3621 }
3622
3623 void
3624 ghb_start_live_encode()
3625 {
3626         hb_start( h_scan );
3627 }
3628
3629 void
3630 ghb_stop_live_encode()
3631 {
3632         hb_stop( h_scan );
3633 }
3634
3635 void
3636 ghb_pause_queue()
3637 {
3638     hb_state_t s;
3639     hb_get_state2( h_queue, &s );
3640
3641     if( s.state == HB_STATE_PAUSED )
3642     {
3643         hb_resume( h_queue );
3644     }
3645     else
3646     {
3647         hb_pause( h_queue );
3648     }
3649 }
3650
3651 #define RED_HEIGHT      720.0
3652 #define RED_WIDTH       1280.0
3653
3654 GdkPixbuf*
3655 ghb_get_preview_image(
3656         gint titleindex, 
3657         gint index, 
3658         signal_user_data_t *ud,
3659         gboolean borders,
3660         gint *out_width,
3661         gint *out_height)
3662 {
3663         GValue *settings;
3664         hb_title_t *title;
3665         hb_list_t  *list;
3666         
3667         settings = ud->settings;
3668         list = hb_get_titles( h_scan );
3669         if( !hb_list_count( list ) )
3670         {
3671                 /* No valid title, stop right there */
3672                 return NULL;
3673         }
3674     title = hb_list_item( list, titleindex );
3675         if (title == NULL) return NULL;
3676         if (title->job == NULL) return NULL;
3677         set_preview_job_settings(title->job, settings);
3678
3679         // hb_get_preview can't handle sizes that are larger than the original title
3680         // dimensions
3681         if (title->job->width > title->width)
3682                 title->job->width = title->width;
3683         
3684         if (title->job->height > title->height)
3685                 title->job->height = title->height;
3686         // And also creates artifacts if the width is not a multiple of 8
3687         //title->job->width = ((title->job->width + 4) >> 3) << 3;
3688         // And the height must be a multiple of 2
3689         //title->job->height = ((title->job->height + 1) >> 1) << 1;
3690         
3691         // Make sure we have a big enough buffer to receive the image from libhb. libhb
3692         // creates images with a one-pixel border around the original content. Hence we
3693         // add 2 pixels horizontally and vertically to the buffer size.
3694         gint srcWidth = title->width + 2;
3695         gint srcHeight= title->height + 2;
3696         gint dstWidth = title->width;
3697         gint dstHeight= title->height;
3698         gint borderTop = 1;
3699         gint borderLeft = 1;
3700     if (borders)
3701     {
3702         //     |<---------- title->width ----------->|
3703         //     |   |<---- title->job->width ---->|   |
3704         //     |   |                             |   |
3705         //     .......................................
3706         //     ....+-----------------------------+....
3707         //     ....|                             |....<-- gray border
3708         //     ....|                             |....
3709         //     ....|                             |....
3710         //     ....|                             |<------- image
3711         //     ....|                             |....
3712         //     ....|                             |....
3713         //     ....|                             |....
3714         //     ....|                             |....
3715         //     ....|                             |....
3716         //     ....+-----------------------------+....
3717         //     .......................................
3718                 dstWidth = title->job->width;
3719         dstHeight = title->job->height;
3720                 borderTop = (srcHeight - dstHeight) / 2;
3721                 borderLeft = (srcWidth - dstWidth) / 2;
3722                 g_debug("boarders removed\n");
3723         }
3724
3725         g_debug("src %d x %d\n", srcWidth, srcHeight);
3726         g_debug("dst %d x %d\n", dstWidth, dstHeight);
3727         g_debug("job dim %d x %d\n", title->job->width, title->job->height);
3728         g_debug("title crop %d:%d:%d:%d\n", 
3729                         title->crop[0],
3730                         title->crop[1],
3731                         title->crop[2],
3732                         title->crop[3]);
3733         g_debug("job crop %d:%d:%d:%d\n", 
3734                         title->job->crop[0],
3735                         title->job->crop[1],
3736                         title->job->crop[2],
3737                         title->job->crop[3]);
3738         static guint8 *buffer = NULL;
3739         static gint bufferSize = 0;
3740
3741         gint newSize;
3742         newSize = srcWidth * srcHeight * 4;
3743         if( bufferSize < newSize )
3744         {
3745                 bufferSize = newSize;
3746                 buffer     = (guint8*) g_realloc( buffer, bufferSize );
3747         }
3748         hb_get_preview( h_scan, title, index, buffer );
3749
3750         // Create an GdkPixbuf and copy the libhb image into it, converting it from
3751         // libhb's format something suitable. Along the way, we'll strip off the
3752         // border around libhb's image.
3753         
3754         // The image data returned by hb_get_preview is 4 bytes per pixel, BGRA format.
3755         // Alpha is ignored.
3756
3757         GdkPixbuf *preview = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, dstWidth, dstHeight);
3758         guint8 *pixels = gdk_pixbuf_get_pixels (preview);
3759         
3760         guint32 *src = (guint32*)buffer;
3761         guint8 *dst = pixels;
3762         src += borderTop * srcWidth;    // skip top rows in src to get to first row of dst
3763         src += borderLeft;              // skip left pixels in src to get to first pixel of dst
3764         gint ii, jj;
3765         gint channels = gdk_pixbuf_get_n_channels (preview);
3766         gint stride = gdk_pixbuf_get_rowstride (preview);
3767         guint8 *tmp;
3768         for (ii = 0; ii < dstHeight; ii++)
3769         {
3770                 tmp = dst;
3771                 for (jj = 0; jj < dstWidth; jj++)
3772                 {
3773                         tmp[0] = src[0] >> 16;
3774                         tmp[1] = src[0] >> 8;
3775                         tmp[2] = src[0] >> 0;
3776                         tmp += channels;
3777                         src++;
3778                 }
3779                 dst += stride;
3780                 src += (srcWidth - dstWidth);   // skip to next row in src
3781         }
3782         // Got it, but hb_get_preview doesn't compensate for anamorphic, so lets
3783         // scale
3784         gint width, height, par_width, par_height;
3785         gboolean anamorphic = ghb_settings_get_boolean(settings, "anamorphic");
3786         if (anamorphic)
3787         {
3788                 hb_set_anamorphic_size( title->job, &width, &height, &par_width, &par_height );
3789                 ghb_par_scale(ud, &dstWidth, &dstHeight, par_width, par_height);
3790         }
3791         else
3792         {
3793                 ghb_par_scale(ud, &dstWidth, &dstHeight, 1, 1);
3794         }
3795         *out_width = dstWidth;
3796         *out_height = dstHeight;
3797         if (ghb_settings_get_boolean(settings, "reduce_hd_preview"))
3798         {
3799                 GdkScreen *ss;
3800                 gint s_w, s_h;
3801                 gint num, den;
3802
3803                 ss = gdk_screen_get_default();
3804                 s_w = gdk_screen_get_width(ss);
3805                 s_h = gdk_screen_get_height(ss);
3806                 num = dstWidth * par_width;
3807                 den = dstHeight * par_height;
3808
3809                 if (dstWidth > s_w * 80 / 100)
3810                 {
3811                         dstWidth = s_w * 80 / 100;
3812                         dstHeight = dstWidth * den / num;
3813                 }
3814                 if (dstHeight > s_h * 80 / 100)
3815                 {
3816                         dstHeight = s_h * 80 / 100;
3817                         dstWidth = dstHeight * num / den;
3818                 }
3819         }
3820         g_debug("scaled %d x %d\n", dstWidth, dstHeight);
3821         GdkPixbuf *scaled_preview;
3822         scaled_preview = gdk_pixbuf_scale_simple(preview, dstWidth, dstHeight, GDK_INTERP_HYPER);
3823         g_object_unref (preview);
3824         return scaled_preview;
3825 }
3826
3827 static void
3828 sanitize_volname(gchar *name)
3829 {
3830         gchar *a, *b;
3831
3832         a = b = name;
3833         while (*b)
3834         {
3835                 switch(*b)
3836                 {
3837                 case '<':
3838                         b++;
3839                         break;
3840                 case '>':
3841                         b++;
3842                         break;
3843                 default:
3844                         *a = *b;
3845                         a++; b++;
3846                         break;
3847                 }
3848         }
3849         *a = 0;
3850 }
3851
3852 gchar*
3853 ghb_dvd_volname(const gchar *device)
3854 {
3855         gchar *name;
3856         name = hb_dvd_name((gchar*)device);
3857         if (name != NULL)
3858         {
3859                 sanitize_volname(name);
3860                 return g_strdup(name);
3861         }
3862         return name;
3863 }