summaryrefslogtreecommitdiffstats
path: root/gtk/src/hb-backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'gtk/src/hb-backend.c')
-rw-r--r--gtk/src/hb-backend.c2955
1 files changed, 2955 insertions, 0 deletions
diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c
new file mode 100644
index 000000000..01f841999
--- /dev/null
+++ b/gtk/src/hb-backend.c
@@ -0,0 +1,2955 @@
+/***************************************************************************
+ * hb-backend.c
+ *
+ * Fri Mar 28 10:38:44 2008
+ * Copyright 2008 John Stebbins
+ * <john at stebbins dot name>
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
+ */
+#define _GNU_SOURCE
+#include <limits.h>
+#include <math.h>
+#include "hb.h"
+#include "hbversion.h"
+#include <gtk/gtk.h>
+#include "hb-backend.h"
+#include "settings.h"
+#include "callbacks.h"
+
+typedef struct
+{
+ const gchar *option;
+ const gchar *shortOpt;
+ gint ivalue;
+ gdouble dvalue;
+ const gchar *svalue;
+ gboolean sensitive;
+} options_map_t;
+
+typedef struct
+{
+ gint count;
+ options_map_t *map;
+ gint *grey_options;
+} combo_opts_t;
+
+static options_map_t d_container_opts[] =
+{
+ {"MKV", "mkv", HB_MUX_MKV, 0.0, "mkv"},
+ {"MP4", "mp4", HB_MUX_MP4, 0.0, "mp4"},
+ {"M4V", "m4v", HB_MUX_MP4, 0.0, "m4v"},
+ {"AVI", "avi", HB_MUX_AVI, 0.0, "avi"},
+ {"OGM", "ogm", HB_MUX_OGM, 0.0, "ogm"},
+};
+combo_opts_t container_opts =
+{
+ sizeof(d_container_opts)/sizeof(options_map_t),
+ d_container_opts
+};
+
+static options_map_t d_deint_opts[] =
+{
+ {"None", "none", 0, 0.0, ""},
+ {"Fast", "fast", 1, 0.0, "-1:-1:-1:0:1"},
+ {"Slow", "slow", 2, 0.0, "2:-1:-1:0:1"},
+ {"Slower", "slower", 3, 0.0, "0:-1:-1:0:1"},
+};
+combo_opts_t deint_opts =
+{
+ sizeof(d_deint_opts)/sizeof(options_map_t),
+ d_deint_opts
+};
+
+static options_map_t d_denoise_opts[] =
+{
+ {"None", "none", 0, 0.0, ""},
+ {"Weak", "weak", 1, 0.0, "2:1:2:3"},
+ {"Medium", "medium", 2, 0.0, "3:2:2:3"},
+ {"Strong", "strong", 3, 0.0, "7:7:5:5"},
+};
+combo_opts_t denoise_opts =
+{
+ sizeof(d_denoise_opts)/sizeof(options_map_t),
+ d_denoise_opts
+};
+
+static options_map_t d_vcodec_opts[] =
+{
+ {"H.264 (x264)", "x264", HB_VCODEC_X264, 0.0, ""},
+ {"MPEG-4 (XviD)", "xvid", HB_VCODEC_XVID, 0.0, ""},
+ {"MPEG-4 (FFMPEG)", "ffmpeg", HB_VCODEC_FFMPEG, 0.0, ""},
+ {"Theora", "theora", HB_VCODEC_THEORA, 0.0, ""},
+};
+combo_opts_t vcodec_opts =
+{
+ sizeof(d_vcodec_opts)/sizeof(options_map_t),
+ d_vcodec_opts
+};
+
+static options_map_t d_acodec_opts[] =
+{
+ {"AAC (faac)", "faac", HB_ACODEC_FAAC, 0.0, ""},
+ {"MP3 (lame)", "lame", HB_ACODEC_LAME, 0.0, ""},
+ {"Vorbis", "vorbis", HB_ACODEC_VORBIS, 0.0, ""},
+ {"AC3 (pass-thru)", "ac3", HB_ACODEC_AC3, 0.0, ""},
+};
+combo_opts_t acodec_opts =
+{
+ sizeof(d_acodec_opts)/sizeof(options_map_t),
+ d_acodec_opts
+};
+
+static options_map_t d_pref_acodec_opts[] =
+{
+ {"None", "none", 0, 0.0, ""},
+ {"AAC (faac)", "faac", HB_ACODEC_FAAC, 0.0, ""},
+ {"MP3 (lame)", "lame", HB_ACODEC_LAME, 0.0, ""},
+ {"Vorbis", "vorbis", HB_ACODEC_VORBIS, 0.0, ""},
+ {"AC3 (pass-thru)", "ac3", HB_ACODEC_AC3, 0.0, ""},
+};
+combo_opts_t pref_acodec_opts =
+{
+ sizeof(d_pref_acodec_opts)/sizeof(options_map_t),
+ d_pref_acodec_opts
+};
+
+static options_map_t d_source_acodec_opts[] =
+{
+ {"AC3", "ac3", HB_ACODEC_AC3, 0.0, ""},
+ {"DTS", "dca", HB_ACODEC_DCA, 0.0, ""},
+ {"MP2", "mpga", HB_ACODEC_MPGA, 0.0, ""},
+ {"LPCM", "lpcm", HB_ACODEC_LPCM, 0.0, ""},
+};
+combo_opts_t source_acodec_opts =
+{
+ sizeof(d_source_acodec_opts)/sizeof(options_map_t),
+ d_source_acodec_opts
+};
+
+static options_map_t d_direct_opts[] =
+{
+ {"None", "none", 0, 0.0, "none"},
+ {"Spatial", "spatial", 1, 0.0, "spatial"},
+ {"Temporal", "temporal", 2, 0.0, "temporal"},
+ {"Automatic", "auto", 3, 0.0, "auto"},
+};
+combo_opts_t direct_opts =
+{
+ sizeof(d_direct_opts)/sizeof(options_map_t),
+ d_direct_opts
+};
+
+static options_map_t d_me_opts[] =
+{
+ {"Diamond", "dia", 0, 0.0, "dia"},
+ {"Hexagon", "hex", 1, 0.0, "hex"},
+ {"Uneven Multi-Hexagon", "umh", 2, 0.0, "umh"},
+ {"Exhaustive", "esa", 3, 0.0, "esa"},
+};
+combo_opts_t me_opts =
+{
+ sizeof(d_me_opts)/sizeof(options_map_t),
+ d_me_opts
+};
+
+static options_map_t d_subme_opts[] =
+{
+ {"1", "1", 1, 0.0, "1"},
+ {"2", "2", 2, 0.0, "2"},
+ {"3", "3", 3, 0.0, "3"},
+ {"4", "4", 4, 0.0, "4"},
+ {"5", "5", 5, 0.0, "5"},
+ {"6", "6", 6, 0.0, "6"},
+ {"7", "7", 7, 0.0, "7"},
+};
+combo_opts_t subme_opts =
+{
+ sizeof(d_subme_opts)/sizeof(options_map_t),
+ d_subme_opts
+};
+
+static options_map_t d_analyse_opts[] =
+{
+ {"Some", "some", 0, 0.0, "some"},
+ {"None", "none", 1, 0.0, "none"},
+ {"All", "all", 2, 0.0, "all:8x8dct=1"},
+};
+combo_opts_t analyse_opts =
+{
+ sizeof(d_analyse_opts)/sizeof(options_map_t),
+ d_analyse_opts
+};
+
+static options_map_t d_trellis_opts[] =
+{
+ {"Disabled", "off", 0, 0.0, ""},
+ {"Final Macro Block", "fmb", 1, 0.0, ""},
+ {"Always", "always", 2, 0.0, ""},
+};
+combo_opts_t trellis_opts =
+{
+ sizeof(d_trellis_opts)/sizeof(options_map_t),
+ d_trellis_opts
+};
+
+typedef struct iso639_lang_t
+{
+ char * eng_name; /* Description in English */
+ char * native_name; /* Description in native language */
+ char * iso639_1; /* ISO-639-1 (2 characters) code */
+ char * iso639_2; /* ISO-639-2/t (3 character) code */
+ char * iso639_2b; /* ISO-639-2/b code (if different from above) */
+} iso639_lang_t;
+
+static const iso639_lang_t language_table[] =
+{
+ { "Any", "", "zz", "und" },
+ { "Afar", "", "aa", "aar" },
+ { "Abkhazian", "", "ab", "abk" },
+ { "Afrikaans", "", "af", "afr" },
+ { "Akan", "", "ak", "aka" },
+ { "Albanian", "", "sq", "sqi", "alb" },
+ { "Amharic", "", "am", "amh" },
+ { "Arabic", "", "ar", "ara" },
+ { "Aragonese", "", "an", "arg" },
+ { "Armenian", "", "hy", "hye", "arm" },
+ { "Assamese", "", "as", "asm" },
+ { "Avaric", "", "av", "ava" },
+ { "Avestan", "", "ae", "ave" },
+ { "Aymara", "", "ay", "aym" },
+ { "Azerbaijani", "", "az", "aze" },
+ { "Bashkir", "", "ba", "bak" },
+ { "Bambara", "", "bm", "bam" },
+ { "Basque", "", "eu", "eus", "baq" },
+ { "Belarusian", "", "be", "bel" },
+ { "Bengali", "", "bn", "ben" },
+ { "Bihari", "", "bh", "bih" },
+ { "Bislama", "", "bi", "bis" },
+ { "Bosnian", "", "bs", "bos" },
+ { "Breton", "", "br", "bre" },
+ { "Bulgarian", "", "bg", "bul" },
+ { "Burmese", "", "my", "mya", "bur" },
+ { "Catalan", "", "ca", "cat" },
+ { "Chamorro", "", "ch", "cha" },
+ { "Chechen", "", "ce", "che" },
+ { "Chinese", "", "zh", "zho", "chi" },
+ { "Church Slavic", "", "cu", "chu" },
+ { "Chuvash", "", "cv", "chv" },
+ { "Cornish", "", "kw", "cor" },
+ { "Corsican", "", "co", "cos" },
+ { "Cree", "", "cr", "cre" },
+ { "Czech", "", "cs", "ces", "cze" },
+ { "Danish", "Dansk", "da", "dan" },
+ { "Divehi", "", "dv", "div" },
+ { "Dutch", "Nederlands", "nl", "nld", "dut" },
+ { "Dzongkha", "", "dz", "dzo" },
+ { "English", "English", "en", "eng" },
+ { "Esperanto", "", "eo", "epo" },
+ { "Estonian", "", "et", "est" },
+ { "Ewe", "", "ee", "ewe" },
+ { "Faroese", "", "fo", "fao" },
+ { "Fijian", "", "fj", "fij" },
+ { "Finnish", "Suomi", "fi", "fin" },
+ { "French", "Francais", "fr", "fra", "fre" },
+ { "Western Frisian", "", "fy", "fry" },
+ { "Fulah", "", "ff", "ful" },
+ { "Georgian", "", "ka", "kat", "geo" },
+ { "German", "Deutsch", "de", "deu", "ger" },
+ { "Gaelic (Scots)", "", "gd", "gla" },
+ { "Irish", "", "ga", "gle" },
+ { "Galician", "", "gl", "glg" },
+ { "Manx", "", "gv", "glv" },
+ { "Greek, Modern", "", "el", "ell", "gre" },
+ { "Guarani", "", "gn", "grn" },
+ { "Gujarati", "", "gu", "guj" },
+ { "Haitian", "", "ht", "hat" },
+ { "Hausa", "", "ha", "hau" },
+ { "Hebrew", "", "he", "heb" },
+ { "Herero", "", "hz", "her" },
+ { "Hindi", "", "hi", "hin" },
+ { "Hiri Motu", "", "ho", "hmo" },
+ { "Hungarian", "Magyar", "hu", "hun" },
+ { "Igbo", "", "ig", "ibo" },
+ { "Icelandic", "Islenska", "is", "isl", "ice" },
+ { "Ido", "", "io", "ido" },
+ { "Sichuan Yi", "", "ii", "iii" },
+ { "Inuktitut", "", "iu", "iku" },
+ { "Interlingue", "", "ie", "ile" },
+ { "Interlingua", "", "ia", "ina" },
+ { "Indonesian", "", "id", "ind" },
+ { "Inupiaq", "", "ik", "ipk" },
+ { "Italian", "Italiano", "it", "ita" },
+ { "Javanese", "", "jv", "jav" },
+ { "Japanese", "", "ja", "jpn" },
+ { "Kalaallisut", "", "kl", "kal" },
+ { "Kannada", "", "kn", "kan" },
+ { "Kashmiri", "", "ks", "kas" },
+ { "Kanuri", "", "kr", "kau" },
+ { "Kazakh", "", "kk", "kaz" },
+ { "Central Khmer", "", "km", "khm" },
+ { "Kikuyu", "", "ki", "kik" },
+ { "Kinyarwanda", "", "rw", "kin" },
+ { "Kirghiz", "", "ky", "kir" },
+ { "Komi", "", "kv", "kom" },
+ { "Kongo", "", "kg", "kon" },
+ { "Korean", "", "ko", "kor" },
+ { "Kuanyama", "", "kj", "kua" },
+ { "Kurdish", "", "ku", "kur" },
+ { "Lao", "", "lo", "lao" },
+ { "Latin", "", "la", "lat" },
+ { "Latvian", "", "lv", "lav" },
+ { "Limburgan", "", "li", "lim" },
+ { "Lingala", "", "ln", "lin" },
+ { "Lithuanian", "", "lt", "lit" },
+ { "Luxembourgish", "", "lb", "ltz" },
+ { "Luba-Katanga", "", "lu", "lub" },
+ { "Ganda", "", "lg", "lug" },
+ { "Macedonian", "", "mk", "mkd", "mac" },
+ { "Marshallese", "", "mh", "mah" },
+ { "Malayalam", "", "ml", "mal" },
+ { "Maori", "", "mi", "mri", "mao" },
+ { "Marathi", "", "mr", "mar" },
+ { "Malay", "", "ms", "msa", "msa" },
+ { "Malagasy", "", "mg", "mlg" },
+ { "Maltese", "", "mt", "mlt" },
+ { "Moldavian", "", "mo", "mol" },
+ { "Mongolian", "", "mn", "mon" },
+ { "Nauru", "", "na", "nau" },
+ { "Navajo", "", "nv", "nav" },
+ { "Ndebele, South", "", "nr", "nbl" },
+ { "Ndebele, North", "", "nd", "nde" },
+ { "Ndonga", "", "ng", "ndo" },
+ { "Nepali", "", "ne", "nep" },
+ { "Norwegian Nynorsk", "", "nn", "nno" },
+ { "Norwegian Bokmål", "", "nb", "nob" },
+ { "Norwegian", "Norsk", "no", "nor" },
+ { "Chichewa; Nyanja", "", "ny", "nya" },
+ { "Occitan", "", "oc", "oci" },
+ { "Ojibwa", "", "oj", "oji" },
+ { "Oriya", "", "or", "ori" },
+ { "Oromo", "", "om", "orm" },
+ { "Ossetian", "", "os", "oss" },
+ { "Panjabi", "", "pa", "pan" },
+ { "Persian", "", "fa", "fas", "per" },
+ { "Pali", "", "pi", "pli" },
+ { "Polish", "", "pl", "pol" },
+ { "Portuguese", "Portugues", "pt", "por" },
+ { "Pushto", "", "ps", "pus" },
+ { "Quechua", "", "qu", "que" },
+ { "Romansh", "", "rm", "roh" },
+ { "Romanian", "", "ro", "ron", "rum" },
+ { "Rundi", "", "rn", "run" },
+ { "Russian", "", "ru", "rus" },
+ { "Sango", "", "sg", "sag" },
+ { "Sanskrit", "", "sa", "san" },
+ { "Serbian", "", "sr", "srp", "scc" },
+ { "Croatian", "Hrvatski", "hr", "hrv", "scr" },
+ { "Sinhala", "", "si", "sin" },
+ { "Slovak", "", "sk", "slk", "slo" },
+ { "Slovenian", "", "sl", "slv" },
+ { "Northern Sami", "", "se", "sme" },
+ { "Samoan", "", "sm", "smo" },
+ { "Shona", "", "sn", "sna" },
+ { "Sindhi", "", "sd", "snd" },
+ { "Somali", "", "so", "som" },
+ { "Sotho, Southern", "", "st", "sot" },
+ { "Spanish", "Espanol", "es", "spa" },
+ { "Sardinian", "", "sc", "srd" },
+ { "Swati", "", "ss", "ssw" },
+ { "Sundanese", "", "su", "sun" },
+ { "Swahili", "", "sw", "swa" },
+ { "Swedish", "Svenska", "sv", "swe" },
+ { "Tahitian", "", "ty", "tah" },
+ { "Tamil", "", "ta", "tam" },
+ { "Tatar", "", "tt", "tat" },
+ { "Telugu", "", "te", "tel" },
+ { "Tajik", "", "tg", "tgk" },
+ { "Tagalog", "", "tl", "tgl" },
+ { "Thai", "", "th", "tha" },
+ { "Tibetan", "", "bo", "bod", "tib" },
+ { "Tigrinya", "", "ti", "tir" },
+ { "Tonga", "", "to", "ton" },
+ { "Tswana", "", "tn", "tsn" },
+ { "Tsonga", "", "ts", "tso" },
+ { "Turkmen", "", "tk", "tuk" },
+ { "Turkish", "", "tr", "tur" },
+ { "Twi", "", "tw", "twi" },
+ { "Uighur", "", "ug", "uig" },
+ { "Ukrainian", "", "uk", "ukr" },
+ { "Urdu", "", "ur", "urd" },
+ { "Uzbek", "", "uz", "uzb" },
+ { "Venda", "", "ve", "ven" },
+ { "Vietnamese", "", "vi", "vie" },
+ { "Volapük", "", "vo", "vol" },
+ { "Welsh", "", "cy", "cym", "wel" },
+ { "Walloon", "", "wa", "wln" },
+ { "Wolof", "", "wo", "wol" },
+ { "Xhosa", "", "xh", "xho" },
+ { "Yiddish", "", "yi", "yid" },
+ { "Yoruba", "", "yo", "yor" },
+ { "Zhuang", "", "za", "zha" },
+ { "Zulu", "", "zu", "zul" },
+};
+#define LANG_TABLE_SIZE (sizeof(language_table)/ sizeof(iso639_lang_t))
+
+const gchar*
+ghb_version()
+{
+ return HB_VERSION;
+}
+
+static setting_value_t*
+get_acodec_value(gint val)
+{
+ setting_value_t *value = NULL;
+ gint ii;
+
+ for (ii = 0; ii < acodec_opts.count; ii++)
+ {
+ if (acodec_opts.map[ii].ivalue == val)
+ {
+ value = g_malloc(sizeof(setting_value_t));
+ value->option = g_strdup(acodec_opts.map[ii].option);
+ value->shortOpt = g_strdup(acodec_opts.map[ii].shortOpt);
+ value->svalue = g_strdup(acodec_opts.map[ii].svalue);
+ value->index = ii;
+ value->ivalue = acodec_opts.map[ii].ivalue;
+ value->dvalue = acodec_opts.map[ii].dvalue;
+ break;
+ }
+ }
+ return value;
+}
+
+static setting_value_t*
+get_amix_value(gint val)
+{
+ setting_value_t *value = NULL;
+ gint ii;
+
+ for (ii = 0; ii < hb_audio_mixdowns_count; ii++)
+ {
+ if (hb_audio_mixdowns[ii].amixdown == val)
+ {
+ value = g_malloc(sizeof(setting_value_t));
+ value->option = g_strdup(hb_audio_mixdowns[ii].human_readable_name);
+ value->shortOpt = g_strdup(hb_audio_mixdowns[ii].short_name);
+ value->svalue = g_strdup(hb_audio_mixdowns[ii].internal_name);
+ value->index = ii;
+ value->ivalue = hb_audio_mixdowns[ii].amixdown;
+ value->dvalue = hb_audio_mixdowns[ii].amixdown;
+ break;
+ }
+ }
+ return value;
+}
+
+// Handle for libhb. Gets set by ghb_backend_init()
+static hb_handle_t * h = NULL;
+
+static hb_audio_config_t*
+get_hb_audio(gint titleindex, gint track)
+{
+ hb_list_t * list;
+ hb_title_t * title;
+ hb_audio_config_t *audio = NULL;
+
+ if (h == NULL) return NULL;
+ list = hb_get_titles( h );
+ if( !hb_list_count( list ) )
+ {
+ /* No valid title, stop right there */
+ return NULL;
+ }
+ title = hb_list_item( list, titleindex );
+ if (title == NULL) return NULL; // Bad titleindex
+ if (!hb_list_count(title->list_audio))
+ {
+ return NULL;
+ }
+ audio = (hb_audio_config_t *)hb_list_audio_config_item(title->list_audio, track);
+ return audio;
+}
+
+static gint
+search_rates(hb_rate_t *rates, gint rate, gint count)
+{
+ gint ii;
+ for (ii = 0; ii < count; ii++)
+ {
+ if (rates[ii].rate == rate)
+ return ii;
+ }
+ return -1;
+}
+
+#if 0
+const gchar*
+ghb_get_rate_string(gint rate, gint type)
+{
+ gint index;
+
+ switch (type)
+ {
+ case GHB_FRAMERATE:
+ {
+ index = search_rates(hb_video_rates, rate, hb_video_rates_count);
+ if (index >= 0) return hb_video_rates[index].string;
+ } break;
+ case GHB_AUDIO_BITRATE:
+ {
+ rate /= 1000;
+ index = search_rates(hb_audio_bitrates, rate, hb_audio_bitrates_count);
+ if (index >= 0) return hb_audio_bitrates[index].string;
+ } break;
+ case GHB_AUDIO_SAMPLERATE:
+ {
+ index = search_rates(hb_audio_rates, rate, hb_audio_rates_count);
+ if (index >= 0) return hb_audio_rates[index].string;
+ } break;
+ }
+ return NULL;
+}
+#endif
+
+static gboolean find_combo_item_by_int(GtkTreeModel *store, gint value, GtkTreeIter *iter);
+
+static GtkListStore*
+get_combo_box_store(GtkBuilder *builder, const gchar *name)
+{
+ GtkComboBox *combo;
+ GtkListStore *store;
+
+ g_debug("get_combo_box_store() %s\n", name);
+ // First modify the combobox model to allow greying out of options
+ combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name));
+ store = GTK_LIST_STORE(gtk_combo_box_get_model (combo));
+ return store;
+}
+
+static void
+grey_combo_box_item(GtkBuilder *builder, const gchar *name, gint value, gboolean grey)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ store = get_combo_box_store(builder, name);
+ if (find_combo_item_by_int(GTK_TREE_MODEL(store), value, &iter))
+ {
+ gtk_list_store_set(store, &iter,
+ 1, !grey,
+ -1);
+ }
+}
+
+void
+ghb_grey_combo_options(GtkBuilder *builder)
+{
+ GtkWidget *widget;
+ gint container, track, titleindex, acodec;
+ hb_audio_config_t *audio = NULL;
+
+ widget = GHB_WIDGET (builder, "title");
+ titleindex = ghb_widget_int(widget);
+ widget = GHB_WIDGET (builder, "audio_track");
+ track = ghb_widget_int(widget);
+ audio = get_hb_audio(titleindex, track);
+
+ grey_combo_box_item(builder, "audio_codec", HB_ACODEC_FAAC, FALSE);
+ grey_combo_box_item(builder, "pref_audio_codec1", HB_ACODEC_FAAC, FALSE);
+ grey_combo_box_item(builder, "pref_audio_codec2", HB_ACODEC_FAAC, FALSE);
+ grey_combo_box_item(builder, "audio_codec", HB_ACODEC_LAME, FALSE);
+ grey_combo_box_item(builder, "pref_audio_codec1", HB_ACODEC_LAME, FALSE);
+ grey_combo_box_item(builder, "pref_audio_codec2", HB_ACODEC_LAME, FALSE);
+ grey_combo_box_item(builder, "audio_codec", HB_ACODEC_VORBIS, FALSE);
+ grey_combo_box_item(builder, "pref_audio_codec1", HB_ACODEC_VORBIS, FALSE);
+ grey_combo_box_item(builder, "pref_audio_codec2", HB_ACODEC_VORBIS, FALSE);
+ if (audio && audio->in.codec != HB_ACODEC_AC3)
+ {
+ grey_combo_box_item(builder, "audio_codec", HB_ACODEC_AC3, TRUE);
+ }
+ else
+ {
+ grey_combo_box_item(builder, "audio_codec", HB_ACODEC_AC3, FALSE);
+ }
+ grey_combo_box_item(builder, "pref_audio_codec1", HB_ACODEC_AC3, FALSE);
+ grey_combo_box_item(builder, "pref_audio_codec2", HB_ACODEC_AC3, FALSE);
+ grey_combo_box_item(builder, "video_codec", HB_VCODEC_THEORA, FALSE);
+
+ widget = GHB_WIDGET (builder, "audio_codec");
+ acodec = ghb_widget_int(widget);
+ if (acodec != HB_ACODEC_AC3)
+ {
+ grey_combo_box_item(builder, "audio_mix", 0, TRUE);
+ }
+ widget = GHB_WIDGET (builder, "container");
+ container = ghb_widget_int(widget);
+ if (container == HB_MUX_MP4)
+ {
+ grey_combo_box_item(builder, "audio_codec", HB_ACODEC_LAME, TRUE);
+ grey_combo_box_item(builder, "pref_audio_codec1", HB_ACODEC_LAME, TRUE);
+ grey_combo_box_item(builder, "pref_audio_codec2", HB_ACODEC_LAME, TRUE);
+ grey_combo_box_item(builder, "audio_codec", HB_ACODEC_VORBIS, TRUE);
+ grey_combo_box_item(builder, "pref_audio_codec1", HB_ACODEC_VORBIS, TRUE);
+ grey_combo_box_item(builder, "pref_audio_codec2", HB_ACODEC_VORBIS, TRUE);
+ grey_combo_box_item(builder, "video_codec", HB_VCODEC_THEORA, TRUE);
+ }
+ else if (container == HB_MUX_AVI)
+ {
+ grey_combo_box_item(builder, "audio_codec", HB_ACODEC_FAAC, TRUE);
+ grey_combo_box_item(builder, "pref_audio_codec1", HB_ACODEC_FAAC, TRUE);
+ grey_combo_box_item(builder, "pref_audio_codec2", HB_ACODEC_FAAC, TRUE);
+ grey_combo_box_item(builder, "audio_codec", HB_ACODEC_VORBIS, TRUE);
+ grey_combo_box_item(builder, "pref_audio_codec1", HB_ACODEC_VORBIS, TRUE);
+ grey_combo_box_item(builder, "pref_audio_codec2", HB_ACODEC_VORBIS, TRUE);
+ grey_combo_box_item(builder, "video_codec", HB_VCODEC_THEORA, TRUE);
+ }
+ else if (container == HB_MUX_OGM)
+ {
+ grey_combo_box_item(builder, "audio_codec", HB_ACODEC_FAAC, TRUE);
+ grey_combo_box_item(builder, "pref_audio_codec1", HB_ACODEC_FAAC, TRUE);
+ grey_combo_box_item(builder, "pref_audio_codec2", HB_ACODEC_FAAC, TRUE);
+ grey_combo_box_item(builder, "audio_codec", HB_ACODEC_AC3, TRUE);
+ grey_combo_box_item(builder, "pref_audio_codec1", HB_ACODEC_AC3, TRUE);
+ grey_combo_box_item(builder, "pref_audio_codec2", HB_ACODEC_AC3, TRUE);
+ }
+
+ gboolean allow_mono = TRUE;
+ gboolean allow_stereo = TRUE;
+ gboolean allow_dolby = TRUE;
+ gboolean allow_dpl2 = TRUE;
+ gboolean allow_6ch = TRUE;
+ if (audio)
+ {
+ allow_mono =
+ (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
+ (acodec != HB_ACODEC_LAME);
+ gint layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
+ allow_stereo =
+ ((layout == HB_INPUT_CH_LAYOUT_MONO && !allow_mono) || layout >= HB_INPUT_CH_LAYOUT_STEREO);
+ allow_dolby =
+ (layout == HB_INPUT_CH_LAYOUT_3F1R) ||
+ (layout == HB_INPUT_CH_LAYOUT_3F2R) ||
+ (layout == HB_INPUT_CH_LAYOUT_DOLBY);
+ allow_dpl2 = (layout == HB_INPUT_CH_LAYOUT_3F2R);
+ allow_6ch =
+ (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
+ (acodec != HB_ACODEC_LAME) &&
+ (layout == HB_INPUT_CH_LAYOUT_3F2R) &&
+ (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE);
+ }
+ grey_combo_box_item(builder, "audio_mix", HB_AMIXDOWN_MONO, !allow_mono);
+ grey_combo_box_item(builder, "audio_mix", HB_AMIXDOWN_STEREO, !allow_stereo);
+ grey_combo_box_item(builder, "audio_mix", HB_AMIXDOWN_DOLBY, !allow_dolby);
+ grey_combo_box_item(builder, "audio_mix", HB_AMIXDOWN_DOLBYPLII, !allow_dpl2);
+ grey_combo_box_item(builder, "audio_mix", HB_AMIXDOWN_6CH, !allow_6ch);
+}
+
+gint
+ghb_get_best_mix(gint titleindex, gint track, gint acodec, gint mix)
+{
+ hb_audio_config_t *audio = NULL;
+ gboolean allow_mono = TRUE;
+ gboolean allow_stereo = TRUE;
+ gboolean allow_dolby = TRUE;
+ gboolean allow_dpl2 = TRUE;
+ gboolean allow_6ch = TRUE;
+
+ if (acodec & (HB_ACODEC_AC3 | HB_ACODEC_DCA))
+ {
+ // Audio codec pass-thru. No mixdown
+ return 0;
+ }
+ audio = get_hb_audio(titleindex, track);
+ if (audio)
+ {
+ allow_mono =
+ (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
+ (acodec != HB_ACODEC_LAME);
+ gint layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
+ allow_stereo =
+ ((layout == HB_INPUT_CH_LAYOUT_MONO && !allow_mono) || layout >= HB_INPUT_CH_LAYOUT_STEREO);
+ allow_dolby =
+ (layout == HB_INPUT_CH_LAYOUT_3F1R) ||
+ (layout == HB_INPUT_CH_LAYOUT_3F2R) ||
+ (layout == HB_INPUT_CH_LAYOUT_DOLBY);
+ allow_dpl2 = (layout == HB_INPUT_CH_LAYOUT_3F2R);
+ allow_6ch =
+ (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
+ (acodec != HB_ACODEC_LAME) &&
+ (layout == HB_INPUT_CH_LAYOUT_3F2R) &&
+ (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE);
+ }
+ gboolean greater = FALSE;
+ if (mix == 0)
+ {
+ // If no mix is specified, select the best available.
+ mix = HB_AMIXDOWN_6CH;
+ }
+ if (mix == HB_AMIXDOWN_6CH)
+ {
+ greater = TRUE;
+ if (allow_6ch) return HB_AMIXDOWN_6CH;
+ }
+ if (mix == HB_AMIXDOWN_DOLBYPLII || greater)
+ {
+ greater = TRUE;
+ if (allow_dpl2) return HB_AMIXDOWN_DOLBYPLII;
+ }
+ if (mix == HB_AMIXDOWN_DOLBY || greater)
+ {
+ greater = TRUE;
+ if (allow_dolby) return HB_AMIXDOWN_DOLBY;
+ }
+ if (mix == HB_AMIXDOWN_STEREO || greater)
+ {
+ greater = TRUE;
+ if (allow_stereo) return HB_AMIXDOWN_STEREO;
+ }
+ if (mix == HB_AMIXDOWN_MONO || greater)
+ {
+ greater = TRUE;
+ if (allow_mono) return HB_AMIXDOWN_MONO;
+ }
+ if (allow_stereo) return HB_AMIXDOWN_STEREO;
+ if (allow_dolby) return HB_AMIXDOWN_DOLBY;
+ if (allow_dpl2) return HB_AMIXDOWN_DOLBYPLII;
+ if (allow_6ch) return HB_AMIXDOWN_6CH;
+ return 0;
+}
+
+// Set up the model for the combo box
+static void
+init_combo_box(GtkBuilder *builder, const gchar *name)
+{
+ GtkComboBox *combo;
+ GtkListStore *store;
+ GtkCellRenderer *cell;
+
+ g_debug("init_combo_box() %s\n", name);
+ // First modify the combobox model to allow greying out of options
+ combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name));
+ // Store contains:
+ // 1 - String to display
+ // 2 - bool indicating whether the entry is selectable (grey or not)
+ // 3 - String that is used for presets
+ // 4 - Int value determined by backend
+ // 5 - String value determined by backend
+ store = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_BOOLEAN,
+ G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
+ gtk_combo_box_set_model(combo, GTK_TREE_MODEL(store));
+
+ gtk_cell_layout_clear(GTK_CELL_LAYOUT(combo));
+ cell = GTK_CELL_RENDERER(gtk_cell_renderer_text_new());
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
+ gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell,
+ "text", 0, "sensitive", 1, NULL);
+}
+
+static void
+audio_bitrate_opts_set(GtkBuilder *builder, const gchar *name, hb_rate_t *rates, gint count)
+{
+ GtkTreeIter iter;
+ GtkListStore *store;
+ gint ii;
+
+ g_debug("audio_bitrate_opts_set ()\n");
+ store = get_combo_box_store(builder, name);
+ gtk_list_store_clear(store);
+ for (ii = 0; ii < count; ii++)
+ {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, rates[ii].string,
+ 1, TRUE,
+ 2, rates[ii].string,
+ 3, rates[ii].rate * 1000,
+ 4, rates[ii].string,
+ -1);
+ }
+}
+
+static gboolean
+audio_bitrate_opts_clean(GtkBuilder *builder, const gchar *name, hb_rate_t *rates, gint count)
+{
+ GtkTreeIter iter;
+ GtkListStore *store;
+ gint ivalue;
+ gboolean done = FALSE;
+ gboolean changed = FALSE;
+
+ g_debug("audio_bitrate_opts_clean ()\n");
+ store = get_combo_box_store(builder, name);
+ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store), &iter))
+ {
+ do
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 3, &ivalue, -1);
+ if (search_rates(rates, ivalue/1000, count) < 0)
+ {
+ done = !gtk_list_store_remove(store, &iter);
+ changed = TRUE;
+ }
+ else
+ {
+ done = !gtk_tree_model_iter_next (GTK_TREE_MODEL(store), &iter);
+ }
+ } while (!done);
+ }
+ return changed;
+}
+
+static void
+audio_samplerate_opts_set(GtkBuilder *builder, const gchar *name, hb_rate_t *rates, gint count)
+{
+ GtkTreeIter iter;
+ GtkListStore *store;
+ gint ii;
+
+ g_debug("audio_samplerate_opts_set ()\n");
+ store = get_combo_box_store(builder, name);
+ gtk_list_store_clear(store);
+ // Add an item for "Same As Source"
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, "Same as source",
+ 1, TRUE,
+ 2, "source",
+ 3, 0,
+ 4, "source",
+ -1);
+ for (ii = 0; ii < count; ii++)
+ {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, rates[ii].string,
+ 1, TRUE,
+ 2, rates[ii].string,
+ 3, rates[ii].rate,
+ 4, rates[ii].string,
+ -1);
+ }
+}
+
+static void
+video_rate_opts_set(GtkBuilder *builder, const gchar *name, hb_rate_t *rates, gint count)
+{
+ GtkTreeIter iter;
+ GtkListStore *store;
+ gint ii;
+
+ g_debug("video_rate_opts_set ()\n");
+ store = get_combo_box_store(builder, name);
+ gtk_list_store_clear(store);
+ // Add an item for "Same As Source"
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, "Same as source",
+ 1, TRUE,
+ 2, "source",
+ 3, 0,
+ 4, "source",
+ -1);
+ for (ii = 0; ii < count; ii++)
+ {
+ gchar *desc = "";
+ gchar *option;
+ if (strcmp(rates[ii].string, "23.976") == 0)
+ {
+ desc = "(NTSC Film)";
+ }
+ else if (strcmp(rates[ii].string, "25") == 0)
+ {
+ desc = "(PAL Film/Video)";
+ }
+ else if (strcmp(rates[ii].string, "29.97") == 0)
+ {
+ desc = "(NTSC Video)";
+ }
+ option = g_strdup_printf ("%s %s", rates[ii].string, desc);
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, option,
+ 1, TRUE,
+ 2, rates[ii].string,
+ 3, rates[ii].rate,
+ 4, rates[ii].string,
+ -1);
+ g_free(option);
+ }
+}
+
+static void
+mix_opts_set(GtkBuilder *builder, const gchar *name)
+{
+ GtkTreeIter iter;
+ GtkListStore *store;
+ gint ii;
+
+ g_debug("audio_bitrate_opts_set ()\n");
+ store = get_combo_box_store(builder, name);
+ gtk_list_store_clear(store);
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, "None",
+ 1, TRUE,
+ 2, "none",
+ 3, 0,
+ 4, "none",
+ -1);
+ for (ii = 0; ii < hb_audio_mixdowns_count; ii++)
+ {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, hb_audio_mixdowns[ii].human_readable_name,
+ 1, TRUE,
+ 2, hb_audio_mixdowns[ii].short_name,
+ 3, hb_audio_mixdowns[ii].amixdown,
+ 4, hb_audio_mixdowns[ii].internal_name,
+ -1);
+ }
+}
+
+static void
+language_opts_set(GtkBuilder *builder, const gchar *name)
+{
+ GtkTreeIter iter;
+ GtkListStore *store;
+ gint ii;
+
+ g_debug("audio_bitrate_opts_set ()\n");
+ store = get_combo_box_store(builder, name);
+ gtk_list_store_clear(store);
+ for (ii = 0; ii < LANG_TABLE_SIZE; ii++)
+ {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, language_table[ii].eng_name,
+ 1, TRUE,
+ 2, language_table[ii].iso639_2,
+ 3, ii,
+ 4, language_table[ii].iso639_1,
+ -1);
+ }
+}
+
+void
+title_opts_set(GtkBuilder *builder, const gchar *name)
+{
+ GtkTreeIter iter;
+ GtkListStore *store;
+ hb_list_t * list;
+ hb_title_t * title;
+ gint ii;
+ gint count = 0;
+
+ g_debug("title_opts_set ()\n");
+ store = get_combo_box_store(builder, name);
+ gtk_list_store_clear(store);
+ if (h != NULL)
+ {
+ list = hb_get_titles( h );
+ count = hb_list_count( list );
+ if (count > 100) count = 100;
+ }
+ if( count <= 0 )
+ {
+ // No titles. Fill in a default.
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, "No Titles",
+ 1, TRUE,
+ 2, "none",
+ 3, -1,
+ 4, "none",
+ -1);
+ return;
+ }
+ for (ii = 0; ii < count; ii++)
+ {
+ gchar *option;
+
+ title = (hb_title_t*)hb_list_item(list, ii);
+ option = g_strdup_printf ("%d - %02dh%02dm%02ds",
+ title->index, title->hours, title->minutes, title->seconds);
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, option,
+ 1, TRUE,
+ 2, option,
+ 3, ii,
+ 4, option,
+ -1);
+ }
+}
+
+static gboolean
+find_combo_item_by_int(GtkTreeModel *store, gint value, GtkTreeIter *iter)
+{
+ gint ivalue;
+ gboolean foundit = FALSE;
+
+ if (gtk_tree_model_get_iter_first (store, iter))
+ {
+ do
+ {
+ gtk_tree_model_get(store, iter, 3, &ivalue, -1);
+ if (value == ivalue)
+ {
+ foundit = TRUE;
+ break;
+ }
+ } while (gtk_tree_model_iter_next (store, iter));
+ }
+ return foundit;
+}
+
+static gboolean
+audio_rate_opts_add(GtkBuilder *builder, const gchar *name, gint rate)
+{
+ GtkTreeIter iter;
+ GtkListStore *store;
+ gchar *str;
+
+ g_debug("audio_rate_opts_add ()\n");
+ store = get_combo_box_store(builder, name);
+ if (!find_combo_item_by_int(GTK_TREE_MODEL(store), rate, &iter))
+ {
+ str = g_strdup_printf ("%.8g", (gdouble)rate/1000.0);
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, TRUE,
+ 2, str,
+ 3, rate,
+ 4, str,
+ -1);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#if 0
+static gboolean
+audio_rate_opts_remove(GtkBuilder *builder, const gchar *name, gint rate)
+{
+ GtkTreeIter iter;
+ GtkListStore *store;
+
+ g_debug("audio_rate_opts_remove ()\n");
+ store = get_combo_box_store(builder, name);
+ if (find_combo_item_by_int(GTK_TREE_MODEL(store), rate, &iter))
+ {
+ gtk_list_store_remove (store, &iter);
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif
+
+void
+audio_track_opts_set(GtkBuilder *builder, const gchar *name, gint titleindex)
+{
+ GtkTreeIter iter;
+ GtkListStore *store;
+ hb_list_t * list;
+ hb_title_t * title;
+ hb_audio_config_t * audio;
+ gint ii;
+ gint count = 0;
+
+ g_debug("audio_track_opts_set ()\n");
+ store = get_combo_box_store(builder, name);
+ gtk_list_store_clear(store);
+ if (h != NULL)
+ {
+ list = hb_get_titles( h );
+ title = (hb_title_t*)hb_list_item( list, titleindex );
+ if (title != NULL)
+ {
+ count = hb_list_count( title->list_audio );
+ }
+ }
+ if (count > 10) count = 10;
+ if( count <= 0 )
+ {
+ // No audio. set some default
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, "No Audio",
+ 1, TRUE,
+ 2, "none",
+ 3, -1,
+ 4, "none",
+ -1);
+ return;
+ }
+ for (ii = 0; ii < count; ii++)
+ {
+ audio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, ii );
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, audio->lang.description,
+ 1, TRUE,
+ 2, audio->lang.description,
+ 3, ii,
+ 4, audio->lang.description,
+ -1);
+ }
+}
+
+void
+subtitle_opts_set(GtkBuilder *builder, const gchar *name, gint titleindex)
+{
+ GtkTreeIter iter;
+ GtkListStore *store;
+ hb_list_t * list;
+ hb_title_t * title;
+ hb_subtitle_t * subtitle;
+ gint ii;
+ gint count = 0;
+
+ g_debug("subtitle_opts_set ()\n");
+ store = get_combo_box_store(builder, name);
+ gtk_list_store_clear(store);
+ if (h != NULL)
+ {
+ list = hb_get_titles( h );
+ title = (hb_title_t*)hb_list_item( list, titleindex );
+ if (title != NULL)
+ {
+ count = hb_list_count( title->list_subtitle );
+ }
+ }
+ if (count > 10) count = 10;
+ // Add options for "none" and "autoselect"
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, "None",
+ 1, TRUE,
+ 2, "none",
+ 3, -2,
+ 4, "none",
+ -1);
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, "Same As Audio",
+ 1, TRUE,
+ 2, "auto",
+ 3, -1,
+ 4, "auto",
+ -1);
+ for (ii = 0; ii < count; ii++)
+ {
+ subtitle = (hb_subtitle_t *) hb_list_item( title->list_subtitle, ii );
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, subtitle->lang,
+ 1, TRUE,
+ 2, subtitle->iso639_2,
+ 3, ii,
+ 4, subtitle->iso639_2,
+ -1);
+ }
+ if (titleindex == -1)
+ {
+ for (ii = 0; ii < LANG_TABLE_SIZE; ii++)
+ {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, language_table[ii].eng_name,
+ 1, TRUE,
+ 2, language_table[ii].iso639_2,
+ 3, ii,
+ 4, language_table[ii].iso639_2,
+ -1);
+ }
+ }
+}
+
+gint
+ghb_longest_title()
+{
+ hb_list_t * list;
+ hb_title_t * title;
+ gint ii;
+ gint count = 0;
+ guint64 longest = 0;
+ gint titleindex = 0;
+
+ g_debug("ghb_longest_title ()\n");
+ if (h == NULL) return 0;
+ list = hb_get_titles( h );
+ count = hb_list_count( list );
+ if (count > 100) count = 100;
+ for (ii = 0; ii < count; ii++)
+ {
+ title = (hb_title_t*)hb_list_item(list, ii);
+ if (title->duration > longest)
+ {
+ titleindex = ii;
+ longest = title->duration;
+ }
+ }
+ return titleindex;
+}
+
+gint
+ghb_find_audio_track(gint titleindex, const gchar *lang, gint acodec)
+{
+ hb_list_t * list;
+ hb_title_t * title;
+ hb_audio_config_t * audio;
+ gint ii;
+ gint count = 0;
+ gint track = 0;
+
+ g_debug("find_audio_track ()\n");
+ if (h != NULL)
+ {
+ list = hb_get_titles( h );
+ title = (hb_title_t*)hb_list_item( list, titleindex );
+ if (title != NULL)
+ {
+ count = hb_list_count( title->list_audio );
+ }
+ }
+ if (count > 10) count = 10;
+ for (ii = 0; ii < count; ii++)
+ {
+ audio = (hb_audio_config_t*)hb_list_audio_config_item( title->list_audio, ii );
+ if ((strcmp(lang, audio->lang.iso639_2) == 0) ||
+ (strcmp(lang, "und") == 0))
+ {
+ // Candidate track. Will use if no better match found
+ track = ii;
+ if (audio->in.codec == acodec)
+ {
+ // Perfect match
+ return track;
+ }
+ }
+ }
+ return track;
+}
+
+static void
+generic_opts_set(GtkBuilder *builder, const gchar *name, combo_opts_t *opts)
+{
+ GtkTreeIter iter;
+ GtkListStore *store;
+ gint ii;
+
+ g_debug("generic_opts_set ()\n");
+ store = get_combo_box_store(builder, name);
+ gtk_list_store_clear(store);
+ for (ii = 0; ii < opts->count; ii++)
+ {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, opts->map[ii].option,
+ 1, TRUE,
+ 2, opts->map[ii].shortOpt,
+ 3, opts->map[ii].ivalue,
+ 4, opts->map[ii].svalue,
+ -1);
+ }
+}
+
+void
+ghb_update_ui_combo_box(GtkBuilder *builder, const gchar *name, gint user_data, gboolean all)
+{
+ GtkComboBox *combo = NULL;
+ gint signal_id;
+ gint handler_id = 0;
+
+ g_debug("ghb_update_ui_combo_box() %s\n", name);
+ if (name != NULL)
+ {
+ // Clearing a combo box causes a rash of "changed" events, even when
+ // the active item is -1 (inactive). To control things, I'm disabling
+ // the event till things are settled down.
+ combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name));
+ signal_id = g_signal_lookup("changed", GTK_TYPE_COMBO_BOX);
+ if (signal_id > 0)
+ {
+ // Valid signal id found. This should always succeed.
+ handler_id = g_signal_handler_find ((gpointer)combo, G_SIGNAL_MATCH_ID,
+ signal_id, 0, 0, 0, 0);
+ if (handler_id > 0)
+ {
+ // This should also always succeed
+ g_signal_handler_block ((gpointer)combo, handler_id);
+ }
+ }
+ }
+ if (all || strcmp(name, "audio_bitrate") == 0)
+ audio_bitrate_opts_set(builder, "audio_bitrate", hb_audio_bitrates, hb_audio_bitrates_count);
+ if (all || strcmp(name, "pref_audio_bitrate1") == 0)
+ audio_bitrate_opts_set(builder, "pref_audio_bitrate1", hb_audio_bitrates, hb_audio_bitrates_count);
+ if (all || strcmp(name, "pref_audio_bitrate2") == 0)
+ audio_bitrate_opts_set(builder, "pref_audio_bitrate2", hb_audio_bitrates, hb_audio_bitrates_count);
+ if (all || strcmp(name, "audio_sample_rate") == 0)
+ audio_samplerate_opts_set(builder, "audio_sample_rate", hb_audio_rates, hb_audio_rates_count);
+ if (all || strcmp(name, "pref_audio_rate1") == 0)
+ audio_samplerate_opts_set(builder, "pref_audio_rate1", hb_audio_rates, hb_audio_rates_count);
+ if (all || strcmp(name, "pref_audio_rate2") == 0)
+ audio_samplerate_opts_set(builder, "pref_audio_rate2", hb_audio_rates, hb_audio_rates_count);
+ if (all || strcmp(name, "framerate") == 0)
+ video_rate_opts_set(builder, "framerate", hb_video_rates, hb_video_rates_count);
+ if (all || strcmp(name, "audio_mix") == 0)
+ mix_opts_set(builder, "audio_mix");
+ if (all || strcmp(name, "pref_audio_mix1") == 0)
+ mix_opts_set(builder, "pref_audio_mix1");
+ if (all || strcmp(name, "pref_audio_mix2") == 0)
+ mix_opts_set(builder, "pref_audio_mix2");
+ if (all || strcmp(name, "pref_source_audio_lang") == 0)
+ language_opts_set(builder, "pref_source_audio_lang");
+ if (all || strcmp(name, "subtitle_lang") == 0)
+ subtitle_opts_set(builder, "subtitle_lang", user_data);
+ if (all || strcmp(name, "title") == 0)
+ title_opts_set(builder, "title");
+ if (all || strcmp(name, "audio_track") == 0)
+ audio_track_opts_set(builder, "audio_track", user_data);
+ if (all || strcmp(name, "container") == 0)
+ generic_opts_set(builder, "container", &container_opts);
+ if (all || strcmp(name, "deinterlace") == 0)
+ generic_opts_set(builder, "deinterlace", &deint_opts);
+ if (all || strcmp(name, "denoise") == 0)
+ generic_opts_set(builder, "denoise", &denoise_opts);
+ if (all || strcmp(name, "video_codec") == 0)
+ generic_opts_set(builder, "video_codec", &vcodec_opts);
+ if (all || strcmp(name, "audio_codec") == 0)
+ generic_opts_set(builder, "audio_codec", &acodec_opts);
+ if (all || strcmp(name, "pref_audio_codec1") == 0)
+ generic_opts_set(builder, "pref_audio_codec1", &pref_acodec_opts);
+ if (all || strcmp(name, "pref_audio_codec2") == 0)
+ generic_opts_set(builder, "pref_audio_codec2", &pref_acodec_opts);
+ if (all || strcmp(name, "pref_source_audio_codec") == 0)
+ generic_opts_set(builder, "pref_source_audio_codec", &source_acodec_opts);
+ if (all || strcmp(name, "x264_direct") == 0)
+ generic_opts_set(builder, "x264_direct", &direct_opts);
+ if (all || strcmp(name, "x264_me") == 0)
+ generic_opts_set(builder, "x264_me", &me_opts);
+ if (all || strcmp(name, "x264_subme") == 0)
+ generic_opts_set(builder, "x264_subme", &subme_opts);
+ if (all || strcmp(name, "x264_analyse") == 0)
+ generic_opts_set(builder, "x264_analyse", &analyse_opts);
+ if (all || strcmp(name, "x264_trellis") == 0)
+ generic_opts_set(builder, "x264_trellis", &trellis_opts);
+ if (handler_id > 0)
+ {
+ g_signal_handler_unblock ((gpointer)combo, handler_id);
+ }
+}
+
+static void
+init_ui_combo_boxes(GtkBuilder *builder)
+{
+ init_combo_box(builder, "audio_bitrate");
+ init_combo_box(builder, "pref_audio_bitrate1");
+ init_combo_box(builder, "pref_audio_bitrate2");
+ init_combo_box(builder, "audio_sample_rate");
+ init_combo_box(builder, "pref_audio_rate1");
+ init_combo_box(builder, "pref_audio_rate2");
+ init_combo_box(builder, "framerate");
+ init_combo_box(builder, "audio_mix");
+ init_combo_box(builder, "pref_audio_mix1");
+ init_combo_box(builder, "pref_audio_mix2");
+ init_combo_box(builder, "pref_source_audio_lang");
+ init_combo_box(builder, "subtitle_lang");
+ init_combo_box(builder, "title");
+ init_combo_box(builder, "audio_track");
+ init_combo_box(builder, "container");
+ init_combo_box(builder, "deinterlace");
+ init_combo_box(builder, "denoise");
+ init_combo_box(builder, "video_codec");
+ init_combo_box(builder, "audio_codec");
+ init_combo_box(builder, "pref_audio_codec1");
+ init_combo_box(builder, "pref_audio_codec2");
+ init_combo_box(builder, "pref_source_audio_codec");
+ init_combo_box(builder, "x264_direct");
+ init_combo_box(builder, "x264_me");
+ init_combo_box(builder, "x264_subme");
+ init_combo_box(builder, "x264_analyse");
+ init_combo_box(builder, "x264_trellis");
+}
+
+static const char * turbo_opts =
+ "ref=1:subme=1:me=dia:analyse=none:trellis=0:"
+ "no-fast-pskip=0:8x8dct=0:weightb=0";
+
+// Construct the x264 options string
+// The result is allocated, so someone must free it at some point.
+gchar*
+ghb_build_x264opts_string(GHashTable *settings)
+{
+ GString *x264opts = g_string_new("");
+ gint refs = ghb_settings_get_int(settings, "x264_refs");
+ if (refs != 1)
+ {
+ g_string_append_printf(x264opts, "ref=%d:", refs);
+ }
+ if (refs > 1)
+ {
+ if (ghb_settings_get_bool(settings, "x264_mixed_refs"))
+ {
+ g_string_append(x264opts, "mixed-refs=1:");
+ }
+ }
+ gint subme = ghb_settings_get_int(settings, "x264_subme");
+ if (subme != 5) // 5 is default
+ {
+ g_string_append_printf(x264opts, "subme=%d:", subme);
+ }
+ gint bframes = ghb_settings_get_int(settings, "x264_bframes");
+ if (bframes > 0)
+ {
+ g_string_append_printf(x264opts, "bframes=%d:", bframes);
+ if (ghb_settings_get_bool(settings, "x264_weighted_bframes"))
+ {
+ g_string_append(x264opts, "weightb=1:");
+ }
+ if (subme >= 6)
+ {
+ if (ghb_settings_get_bool(settings, "x264_brdo"))
+ {
+ g_string_append(x264opts, "brdo=1:");
+ }
+ }
+ if (ghb_settings_get_bool(settings, "x264_bime"))
+ {
+ g_string_append(x264opts, "bime=1:");
+ }
+ }
+ if (bframes > 1)
+ {
+ if (ghb_settings_get_bool(settings, "x264_bpyramid"))
+ {
+ g_string_append(x264opts, "b-pyramid=1:");
+ }
+ }
+ if (ghb_settings_get_bool(settings, "x264_no_fast_pskip"))
+ {
+ g_string_append(x264opts, "no-fast-pskip=1:");
+ }
+ if (ghb_settings_get_bool(settings, "x264_no_dct_decimate"))
+ {
+ g_string_append(x264opts, "no-dct-decimate=1:");
+ }
+ if (!ghb_settings_get_bool(settings, "x264_cabac"))
+ {
+ g_string_append(x264opts, "cabac=0:");
+ }
+ else
+ {
+ gint trellis = ghb_settings_get_int(settings, "x264_trellis");
+ if (trellis != 0); // != None
+ {
+ g_string_append_printf(x264opts, "trellis=%d:", trellis);
+ }
+ }
+ gint dba, dbb;
+ dba = ghb_settings_get_int(settings, "x264_deblock_alpha");
+ dbb = ghb_settings_get_int(settings, "x264_deblock_beta");
+ if (dba != 0 || dbb != 0)
+ {
+ g_string_append_printf(x264opts, "deblock=%d,%d:", dba, dbb);
+ }
+ const gchar *me = ghb_settings_get_string(settings, "x264_me");
+ g_string_append_printf(x264opts, "me=%s:", me);
+ gint analyse = ghb_settings_get_int(settings, "x264_analyse");
+ if (analyse != 0) // != Some
+ {
+ g_string_append_printf(x264opts, "analyse=%s:",
+ ghb_settings_get_string(settings, "x264_analyse"));
+ }
+ if (analyse != 1) // != none
+ {
+ gint direct = ghb_settings_get_int(settings, "x264_direct");
+ if (direct != 1) // !spatial
+ {
+ g_string_append_printf(x264opts, "direct=%s:",
+ ghb_settings_get_string(settings, "x264_direct"));
+ }
+ }
+ g_string_append_printf(x264opts, "merange=%d:",
+ ghb_settings_get_int(settings, "x264_merange"));
+
+ const gchar *opts = ghb_settings_get_string(settings, "x264_options");
+ if (opts != NULL && opts[0] != 0)
+ {
+ g_string_append_printf(x264opts, "%s:", opts);
+ }
+ // strip the trailing ":"
+ gchar *result;
+ gint len;
+ result = g_string_free(x264opts, FALSE);
+ len = strlen(result);
+ if (len > 0) result[len - 1] = 0;
+ return result;
+}
+
+gchar **
+ghb_get_chapters(gint titleindex)
+{
+ hb_list_t * list;
+ hb_title_t * title;
+ hb_chapter_t * chapter;
+ gint count, ii;
+ gchar **result = NULL;
+
+ g_debug("ghb_get_chapters (title = %d)\n", titleindex);
+ if (h == NULL) return NULL;
+ list = hb_get_titles( h );
+ title = (hb_title_t*)hb_list_item( list, titleindex );
+ if (title == NULL) return NULL;
+ count = hb_list_count( title->list_chapter );
+ result = g_malloc((count+1) * sizeof(gchar*));
+ for (ii = 0; ii < count; ii++)
+ {
+ chapter = hb_list_item(title->list_chapter, ii);
+ if (chapter == NULL) break;
+ if (chapter->title == NULL || chapter->title[0] == 0)
+ result[ii] = g_strdup_printf ("Chapter %2d", ii);
+ else
+ result[ii] = g_strdup(chapter->title);
+ }
+ result[ii] = NULL;
+ return result;
+}
+
+gboolean
+ghb_set_passthru_rate_opts(GtkBuilder *builder, gint bitrate)
+{
+ gboolean changed = FALSE;
+ changed = audio_rate_opts_add(builder, "audio_bitrate", bitrate);
+ return changed;
+}
+
+gboolean
+ghb_set_default_rate_opts(GtkBuilder *builder)
+{
+ gboolean changed = FALSE;
+ changed = audio_bitrate_opts_clean(builder, "audio_bitrate", hb_audio_bitrates, hb_audio_bitrates_count);
+ return changed;
+}
+
+void
+ghb_backend_init(GtkBuilder *builder, gint debug, gint update)
+{
+ /* Init libhb */
+ h = hb_init( debug, update );
+ // Set up the list model for the combos
+ init_ui_combo_boxes(builder);
+ // Populate all the combos
+ ghb_update_ui_combo_box(builder, NULL, 0, TRUE);
+}
+
+void
+ghb_backend_scan(const gchar *path, gint titleindex)
+{
+ hb_scan( h, path, titleindex );
+}
+
+gint
+ghb_backend_events(signal_user_data_t *ud, gint *unique_id)
+{
+ hb_state_t s;
+ gchar * status;
+ GtkProgressBar *progress;
+
+ if (h == NULL) return GHB_EVENT_NONE;
+ hb_get_state( h, &s );
+ *unique_id = s.param.working.sequence_id & 0xFFFFFF;
+ progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
+ switch( s.state )
+ {
+ case HB_STATE_IDLE:
+ /* Nothing to do */
+ //fprintf( stderr, "HB Idle\n");
+ break;
+
+#define p s.param.scanning
+ case HB_STATE_SCANNING:
+ {
+ status = g_strdup_printf ("Scanning title %d of %d...",
+ p.title_cur, p.title_count );
+ gtk_progress_bar_set_text (progress, status);
+ g_free(status);
+ gtk_progress_bar_set_fraction (progress, (gdouble)p.title_cur / p.title_count);
+ /* Show what title is currently being scanned */
+ } break;
+#undef p
+
+ case HB_STATE_SCANDONE:
+ {
+ status = g_strdup_printf ("Scan done");
+ gtk_progress_bar_set_text (progress, status);
+ g_free(status);
+ gtk_progress_bar_set_fraction (progress, 1.0);
+ return GHB_EVENT_SCAN_DONE;
+ break;
+ }
+
+#define p s.param.working
+ case HB_STATE_WORKING:
+ if( p.seconds > -1 )
+ {
+ status= g_strdup_printf(
+ "Encoding: task %d of %d, %.2f %%"
+ " (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)",
+ p.job_cur, p.job_count, 100.0 * p.progress,
+ p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds );
+ }
+ else
+ {
+ status= g_strdup_printf("Encoding: task %d of %d, %.2f %%",
+ p.job_cur, p.job_count, 100.0 * p.progress );
+ }
+ gtk_progress_bar_set_text (progress, status);
+ gtk_progress_bar_set_fraction (progress, p.progress);
+ g_free(status);
+ return GHB_EVENT_WORKING;
+ break;
+#undef p
+
+ case HB_STATE_PAUSED:
+ status = g_strdup_printf ("Paused");
+ gtk_progress_bar_set_text (progress, status);
+ g_free(status);
+ return GHB_EVENT_PAUSED;
+ break;
+
+#define p s.param.muxing
+ case HB_STATE_MUXING:
+ {
+ gtk_progress_bar_set_text( progress, "Muxing: this may take awhile...");
+ break;
+ }
+#undef p
+
+#define p s.param.workdone
+ case HB_STATE_WORKDONE:
+ {
+ hb_job_t *job;
+
+ // Delete all remaining jobs of this encode.
+ // An encode can be composed of multiple associated jobs.
+ // When a job is stopped, libhb removes it from the job list,
+ // but does not remove other jobs that may be associated with it.
+ // Associated jobs are taged in the sequence id.
+ while (((job = hb_job(h, 0)) != NULL) && ((job->sequence_id >> 24) != 0) )
+ hb_rem( h, job );
+
+ switch( p.error )
+ {
+ case HB_ERROR_NONE:
+ gtk_progress_bar_set_text( progress, "Rip done!" );
+ break;
+ case HB_ERROR_CANCELED:
+ gtk_progress_bar_set_text( progress, "Rip canceled." );
+ return GHB_EVENT_WORK_CANCELED;
+ break;
+ default:
+ gtk_progress_bar_set_text( progress, "Rip failed.");
+ }
+ return GHB_EVENT_WORK_DONE;
+ break;
+ }
+#undef p
+ }
+ return GHB_EVENT_NONE;
+}
+
+gboolean
+ghb_get_title_info(ghb_title_info_t *tinfo, gint titleindex)
+{
+ hb_list_t * list;
+ hb_title_t * title;
+
+ if (h == NULL) return FALSE;
+ list = hb_get_titles( h );
+ if( !hb_list_count( list ) )
+ {
+ /* No valid title, stop right there */
+ return FALSE;
+ }
+
+ title = hb_list_item( list, titleindex );
+ if (title == NULL) return FALSE; // Bad titleindex
+ tinfo->width = title->width;
+ tinfo->height = title->height;
+ memcpy(tinfo->crop, title->crop, 4 * sizeof(int));
+ // Don't allow crop to 0
+ if (title->crop[0] + title->crop[1] >= title->height)
+ title->crop[0] = title->crop[1] = 0;
+ if (title->crop[2] + title->crop[3] >= title->width)
+ title->crop[2] = title->crop[3] = 0;
+ tinfo->num_chapters = hb_list_count(title->list_chapter);
+ tinfo->rate_base = title->rate_base;
+ tinfo->rate = title->rate;
+ hb_reduce(&(tinfo->aspect_n), &(tinfo->aspect_d),
+ title->width * title->pixel_aspect_width,
+ title->height * title->pixel_aspect_height);
+ tinfo->hours = title->hours;
+ tinfo->minutes = title->minutes;
+ tinfo->seconds = title->seconds;
+ return TRUE;
+}
+
+gboolean
+ghb_get_audio_info(ghb_audio_info_t *ainfo, gint titleindex, gint audioindex)
+{
+ hb_audio_config_t *audio;
+
+ audio = get_hb_audio(titleindex, audioindex);
+ if (audio == NULL) return FALSE; // Bad audioindex
+ ainfo->codec = audio->in.codec;
+ ainfo->bitrate = audio->in.bitrate;
+ ainfo->samplerate = audio->in.samplerate;
+ return TRUE;
+}
+
+gboolean
+ghb_audio_is_passthru(gint acodec)
+{
+ g_debug("ghb_audio_is_passthru () \n");
+ g_debug("acodec %d\n", acodec);
+ return (acodec == HB_ACODEC_AC3);
+}
+
+gint
+ghb_get_default_acodec()
+{
+ return HB_ACODEC_FAAC;
+}
+
+void
+ghb_set_scale(signal_user_data_t *ud, gint mode)
+{
+ hb_list_t * list;
+ hb_title_t * title;
+ hb_job_t * job;
+ GHashTable *settings = ud->settings;
+ gboolean keep_aspect, round_dims, anamorphic;
+ gboolean autocrop, autoscale, noscale;
+ gint crop[4], width, height, par_width, par_height;
+ gint crop_width, crop_height;
+ gint aspect_n, aspect_d;
+ gboolean keep_width = (mode == GHB_SCALE_KEEP_WIDTH);
+ gboolean keep_height = (mode == GHB_SCALE_KEEP_HEIGHT);
+ gint step;
+ GtkWidget *widget;
+ gint modshift;
+ gint modround;
+ gint max_width = 0;
+ gint max_height = 0;
+
+ g_debug("ghb_set_scale ()\n");
+
+ if (h == NULL) return;
+ list = hb_get_titles( h );
+ if( !hb_list_count( list ) )
+ {
+ /* No valid title, stop right there */
+ return;
+ }
+ gint titleindex = ghb_settings_get_index(settings, "title");
+ title = hb_list_item( list, titleindex );
+ if (title == NULL) return;
+ job = title->job;
+ if (job == NULL) return;
+
+ // First configure widgets
+ round_dims = ghb_settings_get_bool(ud->settings, "round_dimensions");
+ anamorphic = ghb_settings_get_bool(ud->settings, "anamorphic");
+ keep_aspect = ghb_settings_get_bool(ud->settings, "keep_aspect");
+ autocrop = ghb_settings_get_bool(ud->settings, "autocrop");
+ autoscale = ghb_settings_get_bool(ud->settings, "autoscale");
+ // "Noscale" is a flag that says we prefer to crop extra to satisfy
+ // alignment constraints rather than scaling to satisfy them.
+ noscale = ghb_settings_get_bool(ud->settings, "noscale");
+ // Align dimensions to either 16 or 2 pixels
+ // The scaler crashes if the dimensions are not divisible by 2
+ // x264 also will not accept dims that are not multiple of 2
+ modshift = round_dims ? 4 : 1;
+ modround = round_dims ? 8 : 1;
+ if (autoscale)
+ {
+ keep_width = FALSE;
+ keep_height = FALSE;
+ }
+ if (anamorphic || keep_aspect)
+ {
+ keep_height = FALSE;
+ }
+ // Step needs to be at least 2 because odd widths cause scaler crash
+ step = round_dims ? 16 : 2;
+ widget = GHB_WIDGET (ud->builder, "scale_width");
+ gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), step, 16);
+ widget = GHB_WIDGET (ud->builder, "scale_height");
+ gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), step, 16);
+ if (autocrop)
+ {
+ ghb_title_info_t tinfo;
+
+ if (ghb_get_title_info (&tinfo, titleindex))
+ {
+ crop[0] = tinfo.crop[0];
+ crop[1] = tinfo.crop[1];
+ crop[2] = tinfo.crop[2];
+ crop[3] = tinfo.crop[3];
+ if (noscale)
+ {
+ gint need1, need2;
+
+ // Adjust the cropping to accomplish the desired width and height
+ crop_width = tinfo.width - crop[2] - crop[3];
+ crop_height = tinfo.height - crop[0] - crop[1];
+ width = (crop_width >> modshift) << modshift;
+ height = (crop_height >> modshift) << modshift;
+ need1 = (crop_height - height) / 2;
+ need2 = crop_height - height - need1;
+ crop[0] += need1;
+ crop[1] += need2;
+ need1 = (crop_width - width) / 2;
+ need2 = crop_width - width - need1;
+ crop[2] += need1;
+ crop[3] += need2;
+ }
+ ghb_ui_update_int (ud, "crop_top", crop[0]);
+ ghb_ui_update_int (ud, "crop_bottom", crop[1]);
+ ghb_ui_update_int (ud, "crop_left", crop[2]);
+ ghb_ui_update_int (ud, "crop_right", crop[3]);
+ }
+ }
+ crop[0] = ghb_settings_get_int(ud->settings, "crop_top");
+ crop[1] = ghb_settings_get_int(ud->settings, "crop_bottom");
+ crop[2] = ghb_settings_get_int(ud->settings, "crop_left");
+ crop[3] = ghb_settings_get_int(ud->settings, "crop_right");
+ hb_reduce(&aspect_n, &aspect_d,
+ title->width * title->pixel_aspect_width,
+ title->height * title->pixel_aspect_height);
+ crop_width = title->width - crop[2] - crop[3];
+ crop_height = title->height - crop[0] - crop[1];
+ if (autoscale)
+ {
+ width = crop_width;
+ height = crop_height;
+#if defined(ALLOW_UPSCALE)
+ max_width = 0;
+ max_height = 0;
+#else
+ max_width = crop_width;
+ max_height = crop_height;
+#endif
+ }
+ else
+ {
+ width = ghb_settings_get_int(ud->settings, "scale_width");
+ height = ghb_settings_get_int(ud->settings, "scale_height");
+ max_width = ghb_settings_get_int(ud->settings, "max_width");
+ max_height = ghb_settings_get_int(ud->settings, "max_width");
+ // Align max dims
+ max_width = (max_width >> modshift) << modshift;
+ max_height = (max_height >> modshift) << modshift;
+ // Adjust dims according to max values
+ if (!max_height)
+ {
+ max_height = crop_height;
+ }
+ if (!max_width)
+ {
+#if defined(ALLOW_UPSCALE)
+ if (anamorphic)
+ {
+ max_width = crop_width;
+ }
+ else
+ {
+ gdouble par = (gdouble)(title->height * aspect_n) / (title->width * aspect_d);
+ max_width = (crop_width * ((gdouble)max_height/crop_height) * par);
+ }
+#else
+ max_width = crop_width;
+#endif
+ }
+ height = MIN(height, max_height);
+ width = MIN(width, max_width);
+ g_debug("max_width %d, max_height %d\n", max_width, max_height);
+ }
+ if (width < 16)
+ width = title->width - crop[2] - crop[3];
+ if (height < 16)
+ height = title->height - crop[0] - crop[1];
+
+ if (anamorphic)
+ {
+ if (round_dims)
+ {
+ job->modulus = 0;
+ }
+ else
+ {
+ // The scaler crashes if the dimensions are not divisible by 2
+ // Align mod 2. And so does something in x264_encoder_headers()
+ job->modulus = 2;
+ }
+ job->width = width;
+ if (max_height)
+ job->maxHeight = max_height;
+ job->crop[0] = crop[0]; job->crop[1] = crop[1];
+ job->crop[2] = crop[2]; job->crop[3] = crop[3];
+ hb_set_anamorphic_size( job, &width, &height, &par_width, &par_height );
+ }
+ else
+ {
+ if (keep_aspect)
+ {
+ gdouble par;
+ gint new_width, new_height;
+
+ g_debug("kw %s kh %s\n", keep_width ? "y":"n", keep_height ? "y":"n");
+ g_debug("w %d h %d\n", width, height);
+ // Compute pixel aspect ration.
+ par = (gdouble)(title->height * aspect_n) / (title->width * aspect_d);
+ // Must scale so that par becomes 1:1
+ // Try to keep largest dimension
+ new_height = (crop_height * ((gdouble)width/crop_width) / par);
+ new_width = (crop_width * ((gdouble)height/crop_height) * par);
+ // Height and width are always multiples of 2, so do the rounding
+ new_height = ((new_height + 1) >> 1) << 1;
+ new_width = ((new_width + 1) >> 1) << 1;
+ g_debug("max w %d, new w %d\n", max_width, new_width);
+ if (max_width && (new_width > max_width))
+ {
+ height = new_height;
+ }
+ else if (max_height && (new_height > max_height))
+ {
+ width = new_width;
+ }
+ else if (keep_width)
+ {
+ height = new_height;
+ }
+ else if (keep_height)
+ {
+ width = new_width;
+ }
+ else if (width > new_width)
+ {
+ height = new_height;
+ }
+ else
+ {
+ width = new_width;
+ }
+ g_debug("new w %d h %d\n", width, height);
+ }
+ width = ((width + modround) >> modshift) << modshift;
+ height = ((height + modround) >> modshift) << modshift;
+ }
+ ghb_ui_update_int (ud, "scale_width", width);
+ ghb_ui_update_int (ud, "scale_height", height);
+}
+
+static void
+set_job_picture_settings(hb_job_t *job, GHashTable *settings)
+{
+ job->crop[0] = ghb_settings_get_int(settings, "crop_top");
+ job->crop[1] = ghb_settings_get_int(settings, "crop_bottom");
+ job->crop[2] = ghb_settings_get_int(settings, "crop_left");
+ job->crop[3] = ghb_settings_get_int(settings, "crop_right");
+
+ gboolean anamorphic = ghb_settings_get_bool(settings, "anamorphic");
+ gboolean round_dimensions = ghb_settings_get_bool(settings, "round_dimensions");
+ if (round_dimensions && anamorphic)
+ {
+ job->modulus = 16;
+ job->pixel_ratio = 2;
+ }
+ else if (anamorphic)
+ {
+ job->modulus = 2;
+ job->pixel_ratio = 2;
+ }
+ else
+ {
+ job->modulus = 2;
+ job->pixel_ratio = 0;
+ }
+ job->width = ghb_settings_get_int(settings, "scale_width");
+ job->height = ghb_settings_get_int(settings, "scale_height");
+ gint deint = ghb_settings_get_int(settings, "deinterlace");
+ job->deinterlace = (deint == 0) ? 0 : 1;
+}
+
+gint
+ghb_calculate_target_bitrate(GHashTable *settings, gint titleindex)
+{
+ hb_list_t * list;
+ hb_title_t * title;
+ hb_job_t * job;
+ gint size;
+
+ if (h == NULL) return 2000;
+ list = hb_get_titles( h );
+ title = hb_list_item( list, titleindex );
+ if (title == NULL) return 2000;
+ job = title->job;
+ if (job == NULL) return 2000;
+ size = ghb_settings_get_int(settings, "video_target_size");
+ return hb_calc_bitrate( job, size );
+}
+
+gint
+ghb_guess_bitrate(GHashTable *settings)
+{
+ gint bitrate;
+ if (ghb_settings_get_bool(settings, "vquality_type_constant"))
+ {
+ // This is really rough. I'm trying to err on the high
+ // side since this is used to estimate if there is
+ // sufficient disk space left
+ gint vcodec = ghb_settings_get_int(settings, "video_codec");
+ gdouble vquality = ghb_settings_get_dbl(settings, "video_quality")/100;
+ if (vcodec == HB_VCODEC_X264 &&
+ !ghb_settings_get_bool(settings, "linear_vquality"))
+ {
+ vquality = 51.0 - vquality * 51.0;
+ // Convert log curve to linear
+ vquality = exp2((vquality-12)/6);
+ // Don't let it go to 0
+ if (vquality >= 31) vquality = 30;
+ vquality = (31 - vquality) / 31;
+ }
+ // bitrate seems to be a log relasionship to quality
+ // with typical source material
+ // This is a real wag
+ bitrate = 20*1024*1024*exp10(vquality*14)/exp10(14);
+ // Add some bits for audio
+ bitrate += 500*1024;
+ }
+ else
+ {
+ // Add some fudge to the bitrate to leave breathing room
+ bitrate = ghb_settings_get_int(settings, "video_bitrate")*1024;
+ // Add some bits for audio
+ bitrate += 500*1024;
+ }
+ return bitrate;
+}
+
+gboolean
+ghb_validate_video(signal_user_data_t *ud)
+{
+ GHashTable *settings = ud->settings;
+ gint vcodec, mux;
+ gchar *message;
+
+ mux = ghb_settings_get_int(settings, "container");
+ vcodec = ghb_settings_get_int(settings, "video_codec");
+ if ((mux == HB_MUX_MP4 || mux == HB_MUX_AVI) &&
+ (vcodec == HB_VCODEC_THEORA))
+ {
+ // mp4|avi/theora combination is not supported.
+ message = g_strdup_printf(
+ "Theora is not supported in the MP4 and AVI containers.\n\n"
+ "You should choose a different video codec or container.\n"
+ "If you continue, XviD will be chosen for you.");
+ if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Cancel", "Continue"))
+ {
+ g_free(message);
+ return FALSE;
+ }
+ g_free(message);
+ vcodec = HB_VCODEC_XVID;
+ ghb_ui_update_int(ud, "video_codec", vcodec);
+ }
+ return TRUE;
+}
+
+gboolean
+ghb_validate_audio(signal_user_data_t *ud)
+{
+ hb_list_t * list;
+ hb_title_t * title;
+ GHashTable *settings = ud->settings;
+ gchar *message;
+ setting_value_t *value;
+
+ if (h == NULL) return FALSE;
+ list = hb_get_titles( h );
+ if( !hb_list_count( list ) )
+ {
+ /* No valid title, stop right there */
+ g_message("No title found.\n");
+ return FALSE;
+ }
+
+ gint titleindex = ghb_settings_get_index(settings, "title");
+ title = hb_list_item( list, titleindex );
+ if (title == NULL) return FALSE;
+ GSList *link = ud->audio_settings;
+ gint mux = ghb_settings_get_int(settings, "container");
+ while (link != NULL)
+ {
+ GHashTable *asettings;
+ hb_audio_config_t *taudio;
+
+ asettings = (GHashTable*)link->data;
+ gint track = ghb_settings_get_index(asettings, "audio_track");
+ gint codec = ghb_settings_get_int(asettings, "audio_codec");
+ taudio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, track );
+ if ((taudio->in.codec != HB_ACODEC_AC3) && (codec == HB_ACODEC_AC3))
+ {
+ // Not supported. AC3 is passthrough only, so input must be AC3
+ message = g_strdup_printf(
+ "The source does not support AC3 Pass-Thru.\n\n"
+ "You should choose a different audio codec.\n"
+ "If you continue, one will be chosen for you.");
+ if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Cancel", "Continue"))
+ {
+ g_free(message);
+ return FALSE;
+ }
+ g_free(message);
+ if (mux == HB_MUX_AVI)
+ {
+ codec = HB_ACODEC_LAME;
+ }
+ else
+ {
+ codec = HB_ACODEC_FAAC;
+ }
+ value = get_acodec_value(codec);
+ ghb_settings_set(asettings, "audio_codec", value);
+ }
+ gchar *a_unsup = NULL;
+ gchar *mux_s = NULL;
+ if (mux == HB_MUX_MP4)
+ {
+ mux_s = "MP4";
+ // mp4/mp3|vorbis combination is not supported.
+ if (codec == HB_ACODEC_LAME)
+ {
+ a_unsup = "MP3";
+ codec = HB_ACODEC_FAAC;
+ }
+ if (codec == HB_ACODEC_VORBIS)
+ {
+ a_unsup = "Vorbis";
+ codec = HB_ACODEC_FAAC;
+ }
+ }
+ else if (mux == HB_MUX_AVI)
+ {
+ mux_s = "AVI";
+ // avi/faac|vorbis combination is not supported.
+ if (codec == HB_ACODEC_FAAC)
+ {
+ a_unsup = "FAAC";
+ codec = HB_ACODEC_LAME;
+ }
+ if (codec == HB_ACODEC_VORBIS)
+ {
+ a_unsup = "Vorbis";
+ codec = HB_ACODEC_LAME;
+ }
+ }
+ else if (mux == HB_MUX_OGM)
+ {
+ mux_s = "OGM";
+ // avi/faac|vorbis combination is not supported.
+ if (codec == HB_ACODEC_FAAC)
+ {
+ a_unsup = "FAAC";
+ codec = HB_ACODEC_VORBIS;
+ }
+ if (codec == HB_ACODEC_AC3)
+ {
+ a_unsup = "AC-3";
+ codec = HB_ACODEC_VORBIS;
+ }
+ }
+ if (a_unsup)
+ {
+ message = g_strdup_printf(
+ "%s is not supported in the %s container.\n\n"
+ "You should choose a different audio codec.\n"
+ "If you continue, one will be chosen for you.", a_unsup, mux_s);
+ if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Cancel", "Continue"))
+ {
+ g_free(message);
+ return FALSE;
+ }
+ g_free(message);
+ value = get_acodec_value(codec);
+ ghb_settings_set(asettings, "audio_codec", value);
+ }
+ gint mix = ghb_settings_get_int (asettings, "audio_mix");
+ gboolean allow_mono = TRUE;
+ gboolean allow_stereo = TRUE;
+ gboolean allow_dolby = TRUE;
+ gboolean allow_dpl2 = TRUE;
+ gboolean allow_6ch = TRUE;
+ allow_mono =
+ (taudio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
+ (codec != HB_ACODEC_LAME);
+ gint layout = taudio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
+ allow_stereo =
+ ((layout == HB_INPUT_CH_LAYOUT_MONO && !allow_mono) || layout >= HB_INPUT_CH_LAYOUT_STEREO);
+ allow_dolby =
+ (layout == HB_INPUT_CH_LAYOUT_3F1R) ||
+ (layout == HB_INPUT_CH_LAYOUT_3F2R) ||
+ (layout == HB_INPUT_CH_LAYOUT_DOLBY);
+ allow_dpl2 = (layout == HB_INPUT_CH_LAYOUT_3F2R);
+ allow_6ch =
+ (taudio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
+ (codec != HB_ACODEC_LAME) &&
+ (layout == HB_INPUT_CH_LAYOUT_3F2R) &&
+ (taudio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE);
+
+ gchar *mix_unsup = NULL;
+ if (mix == HB_AMIXDOWN_MONO && !allow_mono)
+ {
+ mix_unsup = "mono";
+ }
+ if (mix == HB_AMIXDOWN_STEREO && !allow_stereo)
+ {
+ mix_unsup = "stereo";
+ }
+ if (mix == HB_AMIXDOWN_DOLBY && !allow_dolby)
+ {
+ mix_unsup = "Dolby";
+ }
+ if (mix == HB_AMIXDOWN_DOLBYPLII && !allow_dpl2)
+ {
+ mix_unsup = "Dolby Pro Logic II";
+ }
+ if (mix == HB_AMIXDOWN_6CH && !allow_6ch)
+ {
+ mix_unsup = "6 Channel";
+ }
+ if (mix_unsup)
+ {
+ message = g_strdup_printf(
+ "The source audio does not support %s mixdown.\n\n"
+ "You should choose a different mixdown.\n"
+ "If you continue, one will be chosen for you.", mix_unsup);
+ if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Cancel", "Continue"))
+ {
+ g_free(message);
+ return FALSE;
+ }
+ g_free(message);
+ mix = ghb_get_best_mix(titleindex, track, codec, mix);
+ value = get_amix_value(mix);
+ ghb_settings_set(asettings, "audio_mix", value);
+ }
+ link = link->next;
+ }
+ return TRUE;
+}
+
+gboolean
+ghb_validate_vquality(GHashTable *settings)
+{
+ gint vcodec;
+ gchar *message;
+
+ if (ghb_settings_get_bool(settings, "nocheckvquality")) return TRUE;
+ vcodec = ghb_settings_get_int(settings, "video_codec");
+ if (ghb_settings_get_bool(settings, "vquality_type_constant"))
+ {
+ gint vquality = ghb_settings_get_dbl(settings, "video_quality");
+ if (vcodec != HB_VCODEC_X264 || ghb_settings_get_bool(settings, "linear_vquality"))
+ {
+ if (vquality < 68 || vquality > 97)
+ {
+ message = g_strdup_printf(
+ "Interesting video quality choise: %d\n\n"
+ "Typical values range from 68 (low) to 97 (high).\n"
+ "Are you sure you wish to use this setting?",
+ vquality);
+ if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "Cancel", "Continue"))
+ {
+ g_free(message);
+ return FALSE;
+ }
+ g_free(message);
+ }
+ }
+ else if (vcodec == HB_VCODEC_X264)
+ {
+ if (vquality < 40 || vquality > 70)
+ {
+ message = g_strdup_printf(
+ "Interesting video quality choise: %d\n\n"
+ "Typical values range from 40 (low) to 70 (high).\n"
+ "Are you sure you wish to use this setting?",
+ vquality);
+ if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "Cancel", "Continue"))
+ {
+ g_free(message);
+ return FALSE;
+ }
+ g_free(message);
+ }
+ }
+ }
+ return TRUE;
+}
+
+void
+ghb_add_job(job_settings_t *js, gint unique_id)
+{
+ hb_list_t * list;
+ hb_title_t * title;
+ hb_job_t * job;
+ GHashTable *settings = js->settings;
+ static gchar *x264opts;
+ gint sub_id = 0;
+
+ g_debug("ghb_add_job()\n");
+ if (h == NULL) return;
+ list = hb_get_titles( h );
+ if( !hb_list_count( list ) )
+ {
+ /* No valid title, stop right there */
+ g_message("No title found.\n");
+ return;
+ }
+
+ gint titleindex = ghb_settings_get_index(settings, "title");
+ title = hb_list_item( list, titleindex );
+ if (title == NULL) return;
+
+ /* Set job settings */
+ job = title->job;
+ if (job == NULL) return;
+
+ job->mux = ghb_settings_get_int(settings, "container");
+ if (job->mux == HB_MUX_MP4)
+ {
+ job->largeFileSize = ghb_settings_get_bool(settings, "large_mp4");
+ job->mp4_optimize = ghb_settings_get_bool(settings, "http_optimize_mp4");
+ }
+ else
+ {
+ job->largeFileSize = FALSE;
+ job->mp4_optimize = FALSE;
+ }
+ gint chapter_start, chapter_end;
+ chapter_start = ghb_settings_get_int(settings, "start_chapter");
+ chapter_end = ghb_settings_get_int(settings, "end_chapter");
+ gint num_chapters = hb_list_count(title->list_chapter);
+ job->chapter_start = MIN( num_chapters, chapter_start );
+ job->chapter_end = MAX( job->chapter_start, chapter_end );
+
+ job->chapter_markers = ghb_settings_get_bool(settings, "chapter_markers");
+ if ( job->chapter_markers )
+ {
+ gint chapter;
+
+ for(chapter = chapter_start; chapter <= chapter_end; chapter++)
+ {
+ hb_chapter_t * chapter_s;
+ gchar *name;
+
+ if (js->chapter_list == NULL || js->chapter_list[chapter-1] == 0)
+ {
+ name = g_strdup_printf ("Chapter %2d", chapter);
+ }
+ else
+ {
+ name = g_strdup(js->chapter_list[chapter-1]);
+ }
+ chapter_s = hb_list_item( job->title->list_chapter, chapter - 1);
+ strncpy(chapter_s->title, name, 1023);
+ chapter_s->title[1023] = '\0';
+ g_free(name);
+ }
+ }
+ job->crop[0] = ghb_settings_get_int(settings, "crop_top");
+ job->crop[1] = ghb_settings_get_int(settings, "crop_bottom");
+ job->crop[2] = ghb_settings_get_int(settings, "crop_left");
+ job->crop[3] = ghb_settings_get_int(settings, "crop_right");
+
+
+ gboolean decomb = ghb_settings_get_bool(settings, "decomb");
+ gint deint = ghb_settings_get_int(settings, "deinterlace");
+ job->deinterlace = (deint == 0) ? 0 : 1;
+ job->grayscale = ghb_settings_get_bool(settings, "grayscale");
+
+ gboolean anamorphic = ghb_settings_get_bool(settings, "anamorphic");
+ gboolean round_dimensions = ghb_settings_get_bool(settings, "round_dimensions");
+ if (round_dimensions && anamorphic)
+ {
+ job->pixel_ratio = 2;
+ job->modulus = 16;
+ }
+ else if (anamorphic)
+ {
+ // Huh! I thought I wanted to use pixel_ratio 1 for this case, but
+ // when its 1, libhb discards the width and height and uses original
+ // title dims - crop. Thats not what I want.
+ // Also, x264 requires things to divisible by 2.
+ job->pixel_ratio = 2;
+ job->modulus = 2;
+ }
+ else
+ {
+ job->pixel_ratio = 0;
+ job->modulus = 2;
+ }
+ job->vfr = ghb_settings_get_bool(settings, "variable_frame_rate");
+ /* Add selected filters */
+ job->filters = hb_list_init();
+ if( ghb_settings_get_bool(settings, "detelecine" ) || job->vfr )
+ {
+ hb_filter_detelecine.settings = NULL;
+ hb_list_add( job->filters, &hb_filter_detelecine );
+ }
+ if( decomb )
+ {
+ // Use default settings
+ hb_filter_decomb.settings = NULL;
+ hb_filter_decomb.settings = "1:2:6:9:40:16:16";
+ hb_list_add( job->filters, &hb_filter_decomb );
+ }
+ if( !decomb && job->deinterlace )
+ {
+ hb_filter_deinterlace.settings = (gchar*)ghb_settings_get_string(settings, "deinterlace");
+ hb_list_add( job->filters, &hb_filter_deinterlace );
+ }
+ if( ghb_settings_get_bool(settings, "deblock") )
+ {
+ hb_filter_deblock.settings = NULL;
+ hb_list_add( job->filters, &hb_filter_deblock );
+ }
+ gint denoise = ghb_settings_get_int(settings, "denoise");
+ if( denoise != 0 )
+ {
+ hb_filter_denoise.settings = (gchar*)ghb_settings_get_string(settings, "denoise");
+ hb_list_add( job->filters, &hb_filter_denoise );
+ }
+ job->width = ghb_settings_get_int(settings, "scale_width");
+ job->height = ghb_settings_get_int(settings, "scale_height");
+
+ job->vcodec = ghb_settings_get_int(settings, "video_codec");
+ if ((job->mux == HB_MUX_MP4 || job->mux == HB_MUX_AVI) &&
+ (job->vcodec == HB_VCODEC_THEORA))
+ {
+ // mp4|avi/theora combination is not supported.
+ job->vcodec = HB_VCODEC_XVID;
+ }
+ if ((job->vcodec == HB_VCODEC_X264) && (job->mux == HB_MUX_MP4))
+ {
+ job->ipod_atom = ghb_settings_get_bool(settings, "ipod_file");
+ }
+ if (ghb_settings_get_bool(settings, "vquality_type_constant"))
+ {
+ gdouble vquality = ghb_settings_get_dbl(settings, "video_quality") / 100.0;
+ if (ghb_settings_get_bool(settings, "linear_vquality"))
+ {
+ if (job->vcodec == HB_VCODEC_X264)
+ {
+ // Adjust to same range as xvid and ffmpeg
+ vquality = 31.0 - vquality * 31.0;
+ if (vquality > 0)
+ {
+ // Convert linear to log curve
+ vquality = 12 + 6 * log2(vquality);
+ if (vquality > 51) vquality = 51;
+ if (vquality < 1) vquality = 1;
+ }
+ else
+ vquality = 0;
+ }
+ }
+ else
+ {
+ if (vquality == 0.0) vquality = 0.01;
+ if (vquality == 1.0) vquality = 0.0;
+ }
+ job->vquality = vquality;
+ job->vbitrate = 0;
+ }
+ else if (ghb_settings_get_bool(settings, "vquality_type_bitrate"))
+ {
+ job->vquality = -1.0;
+ job->vbitrate = ghb_settings_get_int(settings, "video_bitrate");
+ }
+ // AVI container does not support variable frame rate.
+ if (job->mux == HB_MUX_AVI)
+ {
+ job->vfr = FALSE;
+ }
+
+ gint vrate = ghb_settings_get_int(settings, "framerate");
+ if( vrate == 0 || job->vfr )
+ {
+ job->vrate = title->rate;
+ job->vrate_base = title->rate_base;
+ job->cfr = 0;
+ }
+ else
+ {
+ job->vrate = 27000000;
+ job->vrate_base = vrate;
+ job->cfr = 1;
+ }
+ // First remove any audios that are already in the list
+ // This happens if you are encoding the same title a second time.
+ gint num_audio_tracks = hb_list_count(job->list_audio);
+ gint ii;
+ for(ii = 0; ii < num_audio_tracks; ii++)
+ {
+ hb_audio_t *audio = (hb_audio_t*)hb_list_item(job->list_audio, 0);
+ hb_list_rem(job->list_audio, audio);
+ }
+ GSList *link = js->audio_settings;
+ gint count = 0;
+ while (link != NULL)
+ {
+ GHashTable *asettings;
+ hb_audio_config_t audio;
+ hb_audio_config_t *taudio;
+
+ hb_audio_config_init(&audio);
+ asettings = (GHashTable*)link->data;
+ audio.in.track = ghb_settings_get_index(asettings, "audio_track");
+ audio.out.track = count;
+ audio.out.codec = ghb_settings_get_int(asettings, "audio_codec");
+ taudio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, audio.in.track );
+ if ((taudio->in.codec != HB_ACODEC_AC3) && (audio.out.codec == HB_ACODEC_AC3))
+ {
+ // Not supported. AC3 is passthrough only, so input must be AC3
+ if (job->mux == HB_MUX_AVI)
+ {
+ audio.out.codec = HB_ACODEC_LAME;
+ }
+ else
+ {
+ audio.out.codec = HB_ACODEC_FAAC;
+ }
+ }
+ if ((job->mux == HB_MUX_MP4) &&
+ ((audio.out.codec == HB_ACODEC_LAME) ||
+ (audio.out.codec == HB_ACODEC_VORBIS)))
+ {
+ // mp4/mp3|vorbis combination is not supported.
+ audio.out.codec = HB_ACODEC_FAAC;
+ }
+ if ((job->mux == HB_MUX_AVI) &&
+ ((audio.out.codec == HB_ACODEC_FAAC) ||
+ (audio.out.codec == HB_ACODEC_VORBIS)))
+ {
+ // avi/faac|vorbis combination is not supported.
+ audio.out.codec = HB_ACODEC_LAME;
+ }
+ if ((job->mux == HB_MUX_OGM) &&
+ ((audio.out.codec == HB_ACODEC_FAAC) ||
+ (audio.out.codec == HB_ACODEC_AC3)))
+ {
+ // ogm/faac|ac3 combination is not supported.
+ audio.out.codec = HB_ACODEC_VORBIS;
+ }
+ audio.out.dynamic_range_compression = ghb_settings_get_dbl(asettings, "audio_drc");
+ // It would be better if this were done in libhb for us, but its not yet.
+ if (audio.out.codec == HB_ACODEC_AC3 || audio.out.codec == HB_ACODEC_DCA)
+ {
+ audio.out.mixdown = 0;
+ }
+ else
+ {
+ audio.out.mixdown = ghb_settings_get_int (asettings, "audio_mix");
+ // Make sure the mixdown is valid and pick a new one if not.
+ audio.out.mixdown = ghb_get_best_mix(titleindex, audio.in.track, audio.out.codec,
+ audio.out.mixdown);
+ audio.out.bitrate = ghb_settings_get_int(asettings, "audio_bitrate") / 1000;
+ gint srate = ghb_settings_get_int(asettings, "audio_sample_rate");
+ if (srate == 0) // 0 is same as source
+ audio.out.samplerate = taudio->in.samplerate;
+ else
+ audio.out.samplerate = srate;
+ }
+
+ // Add it to the jobs audio list
+ hb_audio_add( job, &audio );
+ count++;
+ link = link->next;
+ }
+ // I was tempted to move this up with the reset of the video quality
+ // settings, but I suspect the settings above need to be made
+ // first in order for hb_calc_bitrate to be accurate.
+ if (ghb_settings_get_bool(settings, "vquality_type_target"))
+ {
+ gint size;
+
+ size = ghb_settings_get_int(settings, "video_target_size");
+ job->vbitrate = hb_calc_bitrate( job, size );
+ job->vquality = -1.0;
+ }
+
+ job->file = ghb_settings_get_string(settings, "destination");
+ job->crf = ghb_settings_get_bool(settings, "constant_rate_factor");
+ // TODO: libhb holds onto a reference to the x264opts and is not
+ // finished with it until encoding the job is done. But I can't
+ // find a way to get at the job before it is removed in order to
+ // free up the memory I am allocating here.
+ // The short story is THIS LEAKS.
+ x264opts = ghb_build_x264opts_string(settings);
+
+ if( x264opts != NULL && *x264opts != '\0' )
+ {
+ job->x264opts = x264opts;
+ }
+ else /*avoids a bus error crash when options aren't specified*/
+ {
+ job->x264opts = NULL;
+ }
+ gint subtitle = ghb_settings_get_int(settings, "subtitle_lang");
+ gboolean forced_subtitles = ghb_settings_get_bool (settings, "forced_subtitles");
+ job->subtitle_force = forced_subtitles;
+ if (subtitle >= 0)
+ job->subtitle = subtitle;
+ else
+ job->subtitle = -1;
+ if (subtitle == -1)
+ {
+ // Subtitle scan. Look for subtitle matching audio language
+ char *x264opts_tmp;
+
+ /*
+ * When subtitle scan is enabled do a fast pre-scan job
+ * which will determine which subtitles to enable, if any.
+ */
+ job->pass = -1;
+ job->indepth_scan = 1;
+
+ x264opts_tmp = job->x264opts;
+ job->x264opts = NULL;
+
+ job->select_subtitle = malloc(sizeof(hb_subtitle_t*));
+ *(job->select_subtitle) = NULL;
+
+ /*
+ * Add the pre-scan job
+ */
+ job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24);
+ hb_add( h, job );
+ //if (job->x264opts != NULL)
+ // g_free(job->x264opts);
+
+ job->x264opts = x264opts_tmp;
+ }
+ else
+ {
+ job->select_subtitle = NULL;
+ }
+ if( ghb_settings_get_bool(settings, "two_pass") )
+ {
+ /*
+ * If subtitle_scan is enabled then only turn it on
+ * for the second pass and then off again for the
+ * second.
+ */
+ hb_subtitle_t **subtitle_tmp = job->select_subtitle;
+ job->select_subtitle = NULL;
+ job->pass = 1;
+ job->indepth_scan = 0;
+ gchar *x264opts2 = NULL;
+ if (x264opts)
+ {
+ x264opts2 = g_strdup(x264opts);
+ }
+ /*
+ * If turbo options have been selected then append them
+ * to the x264opts now (size includes one ':' and the '\0')
+ */
+ if( ghb_settings_get_bool(settings, "turbo") )
+ {
+ char *tmp_x264opts;
+
+ if ( x264opts )
+ {
+ tmp_x264opts = g_strdup_printf("%s:%s", x264opts, turbo_opts);
+ g_free(x264opts);
+ }
+ else
+ {
+ /*
+ * No x264opts to modify, but apply the turbo options
+ * anyway as they may be modifying defaults
+ */
+ tmp_x264opts = g_strdup_printf("%s", turbo_opts);
+ }
+ x264opts = tmp_x264opts;
+
+ job->x264opts = x264opts;
+ }
+ job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24);
+ hb_add( h, job );
+ //if (job->x264opts != NULL)
+ // g_free(job->x264opts);
+
+ job->select_subtitle = subtitle_tmp;
+ job->pass = 2;
+ /*
+ * On the second pass we turn off subtitle scan so that we
+ * can actually encode using any subtitles that were auto
+ * selected in the first pass (using the whacky select-subtitle
+ * attribute of the job).
+ */
+ job->indepth_scan = 0;
+ job->x264opts = x264opts2;
+ job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24);
+ hb_add( h, job );
+ //if (job->x264opts != NULL)
+ // g_free(job->x264opts);
+ }
+ else
+ {
+ job->indepth_scan = 0;
+ job->pass = 0;
+ job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24);
+ hb_add( h, job );
+ //if (job->x264opts != NULL)
+ // g_free(job->x264opts);
+ }
+}
+
+void
+ghb_remove_job(gint unique_id)
+{
+ hb_job_t * job;
+ gint ii;
+
+ // Multiples passes all get the same id
+ // remove them all.
+ // Go backwards through list, so reordering doesn't screw me.
+ ii = hb_count(h) - 1;
+ while ((job = hb_job(h, ii--)) != NULL)
+ {
+ if ((job->sequence_id & 0xFFFFFF) == unique_id)
+ hb_rem(h, job);
+ }
+}
+
+void
+ghb_start_queue()
+{
+ hb_start( h );
+}
+
+void
+ghb_stop_queue()
+{
+ hb_stop( h );
+}
+
+void
+ghb_pause_queue()
+{
+ hb_state_t s;
+ hb_get_state2( h, &s );
+
+ if( s.state == HB_STATE_PAUSED )
+ {
+ hb_resume( h );
+ }
+ else
+ {
+ hb_pause( h );
+ }
+}
+
+GdkPixbuf*
+ghb_get_preview_image(gint titleindex, gint index, GHashTable *settings, gboolean borders)
+{
+ hb_title_t *title;
+ hb_list_t *list;
+
+ list = hb_get_titles( h );
+ if( !hb_list_count( list ) )
+ {
+ /* No valid title, stop right there */
+ return NULL;
+ }
+ title = hb_list_item( list, titleindex );
+ if (title == NULL) return NULL;
+ if (title->job == NULL) return NULL;
+ set_job_picture_settings(title->job, settings);
+
+#if defined(ALLOW_UPSCALE)
+ gdouble scale = 1;
+ if (title->job->width > title->width)
+ scale = (gdouble) title->job->width / title->width;
+ if (title->job->height > title->height)
+ {
+ gdouble tmp;
+ tmp = (gdouble) title->job->height / title->height;
+ if (tmp > scale)
+ scale = tmp;
+ }
+ title->job->width /= scale;
+ title->job->height /= scale;
+#else
+ // hb_get_preview can't handle sizes that are larger than the original title
+ // dimensions
+ if (title->job->width > title->width)
+ title->job->width = title->width;
+
+ if (title->job->height > title->height)
+ title->job->height = title->height;
+#endif
+ // And also creates artifacts if the width is not a multiple of 8
+ //title->job->width = ((title->job->width + 4) >> 3) << 3;
+ // And the height must be a multiple of 2
+ //title->job->height = ((title->job->height + 1) >> 1) << 1;
+
+ // Make sure we have a big enough buffer to receive the image from libhb. libhb
+ // creates images with a one-pixel border around the original content. Hence we
+ // add 2 pixels horizontally and vertically to the buffer size.
+ gint srcWidth = title->width + 2;
+ gint srcHeight= title->height + 2;
+ gint dstWidth = title->width;
+ gint dstHeight= title->height;
+ gint borderTop = 1;
+ gint borderLeft = 1;
+ if (borders)
+ {
+ // |<---------- title->width ----------->|
+ // | |<---- title->job->width ---->| |
+ // | | | |
+ // .......................................
+ // ....+-----------------------------+....
+ // ....| |....<-- gray border
+ // ....| |....
+ // ....| |....
+ // ....| |<------- image
+ // ....| |....
+ // ....| |....
+ // ....| |....
+ // ....| |....
+ // ....| |....
+ // ....+-----------------------------+....
+ // .......................................
+ dstWidth = title->job->width;
+ dstHeight = title->job->height;
+ borderTop = (srcHeight - dstHeight) / 2;
+ borderLeft = (srcWidth - dstWidth) / 2;
+ g_debug("boarders removed\n");
+ }
+
+ g_debug("src %d x %d\n", srcWidth, srcHeight);
+ g_debug("dst %d x %d\n", dstWidth, dstHeight);
+ g_debug("job dim %d x %d\n", title->job->width, title->job->height);
+ g_debug("title crop %d:%d:%d:%d\n",
+ title->crop[0],
+ title->crop[1],
+ title->crop[2],
+ title->crop[3]);
+ g_debug("job crop %d:%d:%d:%d\n",
+ title->job->crop[0],
+ title->job->crop[1],
+ title->job->crop[2],
+ title->job->crop[3]);
+ static guint8 *buffer = NULL;
+ static gint bufferSize = 0;
+
+ gint newSize;
+ newSize = srcWidth * srcHeight * 4;
+ if( bufferSize < newSize )
+ {
+ bufferSize = newSize;
+ buffer = (guint8*) g_realloc( buffer, bufferSize );
+ }
+ hb_get_preview( h, title, index, buffer );
+
+ // Create an GdkPixbuf and copy the libhb image into it, converting it from
+ // libhb's format something suitable. Along the way, we'll strip off the
+ // border around libhb's image.
+
+ // The image data returned by hb_get_preview is 4 bytes per pixel, BGRA format.
+ // Alpha is ignored.
+
+ GdkPixbuf *preview = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, dstWidth, dstHeight);
+ guint8 *pixels = gdk_pixbuf_get_pixels (preview);
+
+ guint32 *src = (guint32*)buffer;
+ guint8 *dst = pixels;
+ src += borderTop * srcWidth; // skip top rows in src to get to first row of dst
+ src += borderLeft; // skip left pixels in src to get to first pixel of dst
+ gint ii, jj;
+ gint channels = gdk_pixbuf_get_n_channels (preview);
+ gint stride = gdk_pixbuf_get_rowstride (preview);
+ guint8 *tmp;
+ for (ii = 0; ii < dstHeight; ii++)
+ {
+ tmp = dst;
+ for (jj = 0; jj < dstWidth; jj++)
+ {
+ tmp[0] = src[0] >> 16;
+ tmp[1] = src[0] >> 8;
+ tmp[2] = src[0] >> 0;
+ tmp += channels;
+ src++;
+ }
+ dst += stride;
+ src += (srcWidth - dstWidth); // skip to next row in src
+ }
+ // Got it, but hb_get_preview doesn't compensate for anamorphic, so lets
+ // scale
+ gint width, height, par_width, par_height;
+ gboolean anamorphic = ghb_settings_get_bool (settings, "anamorphic");
+ if (anamorphic)
+ {
+ hb_set_anamorphic_size( title->job, &width, &height, &par_width, &par_height );
+ if (par_width > par_height)
+ dstWidth = dstWidth * par_width / par_height;
+ else
+ dstHeight = dstHeight * par_height / par_width;
+ }
+#if defined(ALLOW_UPSCALE)
+ dstWidth = ((gdouble)dstWidth + scale/2) * scale;
+ dstHeight = ((gdouble)dstHeight + scale/2) * scale;
+#endif
+
+ g_debug("scaled %d x %d\n", dstWidth, dstHeight);
+ GdkPixbuf *scaled_preview;
+ scaled_preview = gdk_pixbuf_scale_simple(preview, dstWidth, dstHeight, GDK_INTERP_HYPER);
+ g_object_unref (preview);
+ return scaled_preview;
+}
+
+gchar*
+ghb_dvd_volname(const gchar *device)
+{
+ gchar *name;
+ name = hb_dvd_name((gchar*)device);
+ if (name != NULL)
+ {
+ return g_strdup(name);
+ }
+ return name;
+}