diff options
-rw-r--r-- | gtk/src/hb-backend.c | 22 | ||||
-rw-r--r-- | gtk/src/internal_defaults.json | 11 | ||||
-rw-r--r-- | gtk/src/presets.c | 177 | ||||
-rw-r--r-- | gtk/src/queuehandler.c | 30 | ||||
-rw-r--r-- | gtk/src/standard_presets.json | 52 | ||||
-rw-r--r-- | libhb/builtin_presets.h | 965 | ||||
-rw-r--r-- | libhb/common.c | 21 | ||||
-rw-r--r-- | libhb/hb.c | 5 | ||||
-rw-r--r-- | libhb/hb.h | 3 | ||||
-rw-r--r-- | libhb/hb_dict.c | 5 | ||||
-rw-r--r-- | libhb/hb_dict.h | 1 | ||||
-rw-r--r-- | libhb/lang.c | 8 | ||||
-rw-r--r-- | libhb/lang.h | 2 | ||||
-rw-r--r-- | libhb/libhb_presets.list | 9 | ||||
-rw-r--r-- | libhb/module.defs | 6 | ||||
-rw-r--r-- | libhb/plist.c | 678 | ||||
-rw-r--r-- | libhb/plist.h | 13 | ||||
-rw-r--r-- | libhb/preset.c | 2198 | ||||
-rw-r--r-- | libhb/preset.h | 84 | ||||
-rw-r--r-- | libhb/preset_builtin.json | 855 | ||||
-rw-r--r-- | libhb/preset_template.json | 104 | ||||
-rw-r--r-- | scripts/create_resources.py | 161 | ||||
-rw-r--r-- | scripts/quotestring.py | 62 | ||||
-rw-r--r-- | test/test.c | 5269 |
24 files changed, 7796 insertions, 2945 deletions
diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c index f02ec32e7..836f4d714 100644 --- a/gtk/src/hb-backend.c +++ b/gtk/src/hb-backend.c @@ -119,10 +119,10 @@ combo_opts_t when_complete_opts = static options_map_t d_par_opts[] = { - {N_("Off"), "0", 0, "0"}, - {N_("Strict"), "1", 1, "1"}, - {N_("Loose"), "2", 2, "2"}, - {N_("Custom"), "3", 3, "3"}, + {N_("Off"), "off", 0, "0"}, + {N_("Strict"), "strict", 1, "1"}, + {N_("Loose"), "loose", 2, "2"}, + {N_("Custom"), "custom", 3, "3"}, }; combo_opts_t par_opts = { @@ -1242,9 +1242,9 @@ ghb_audio_samplerate_opts_set(GtkComboBox *combo) gtk_list_store_set(store, &iter, 0, str, 1, TRUE, - 2, "source", + 2, "auto", 3, 0.0, - 4, "source", + 4, "auto", -1); g_free(str); @@ -1285,7 +1285,7 @@ ghb_audio_samplerate_get_short_name(int rate) const char *name; name = hb_audio_samplerate_get_name(rate); if (name == NULL) - name = "source"; + name = "auto"; return name; } @@ -1293,7 +1293,7 @@ const hb_rate_t* ghb_lookup_audio_samplerate(const char *name) { // Check for special "Same as source" value - if (!strncmp(name, "source", 8)) + if (!strncmp(name, "auto", 8)) return &sas_rate; // First find an enabled rate @@ -1354,9 +1354,9 @@ video_framerate_opts_set(GtkBuilder *builder, const gchar *name) gtk_list_store_set(store, &iter, 0, _("Same as source"), 1, TRUE, - 2, "source", + 2, "auto", 3, 0.0, - 4, "source", + 4, "auto", -1); const hb_rate_t *rate; @@ -1394,7 +1394,7 @@ const hb_rate_t* ghb_lookup_video_framerate(const char *name) { // Check for special "Same as source" value - if (!strncmp(name, "source", 8)) + if (!strncmp(name, "auto", 8)) return &sas_rate; // First find an enabled rate diff --git a/gtk/src/internal_defaults.json b/gtk/src/internal_defaults.json index 76e78c6ff..0d4fd7657 100644 --- a/gtk/src/internal_defaults.json +++ b/gtk/src/internal_defaults.json @@ -12,7 +12,7 @@ "AudioTrackGainSlider": 0.0, "AudioTrackDRCSlider": 0.0, "AudioMixdown": "dpl2", - "AudioSamplerate": "source", + "AudioSamplerate": "auto", "AudioTrackName": "", "angle_count": 1, "angle": 1, @@ -105,7 +105,6 @@ "window_height": 1 }, "Presets": { - "PresetBuildNumber": "", "PictureAutoCrop": true, "ChapterMarkers": true, "FileFormat": "mp4", @@ -129,12 +128,12 @@ "PictureDenoiseCustom": "", "PictureDetelecine": "off", "PictureDetelecineCustom": "", - "PicturePAR": 2, + "PicturePAR": "loose", "PicturePARWidth": 853, "PicturePARHeight": 720, "PictureHeight": 0, "PictureWidth": 0, - "VideoFramerate": "source", + "VideoFramerate": "auto", "VideoFramerateMode": "vfr", "VideoGrayScale": false, "Mp4HttpOptimize": false, @@ -151,7 +150,7 @@ "AudioCopyMask": [ "copy:ac3" ], - "AudioEncoderFallback": "ffac3", + "AudioEncoderFallback": "ac3", "AudioLanguageList": [ "und" ], @@ -164,7 +163,7 @@ "AudioTrackGainSlider": 0.0, "AudioTrackDRCSlider": 0.0, "AudioMixdown": "dpl2", - "AudioSamplerate": "source" + "AudioSamplerate": "auto" } ], "AudioSecondaryEncoderMode": true, diff --git a/gtk/src/presets.c b/gtk/src/presets.c index 2354bc618..5c2825c7e 100644 --- a/gtk/src/presets.c +++ b/gtk/src/presets.c @@ -1973,39 +1973,6 @@ value_map_t decomb_xlat[] = }; static GhbValue* -export_value_xlat2(value_map_t *value_map, GhbValue *lin_val, GhbType mac_type) -{ - GhbValue *gval; - - if (lin_val == NULL) return NULL; - gint ii; - gchar *str; - GhbValue *sval; - - str = ghb_value_get_string_xform(lin_val); - for (ii = 0; value_map[ii].mac_val; ii++) - { - if (strcmp(str, value_map[ii].lin_val) == 0) - { - sval = ghb_string_value_new(value_map[ii].mac_val); - g_free(str); - gval = ghb_value_xform(sval, mac_type); - if (gval == NULL) - { - g_warning("can't transform"); - ghb_value_free(&sval); - return NULL; - } - ghb_value_free(&sval); - return gval; - } - } - g_debug("Can't map value: (%s)", str); - g_free(str); - return NULL; -} - -static GhbValue* export_value_video_framerate(GhbValue *lin_val) { GhbValue *sval = NULL; @@ -2045,7 +2012,7 @@ export_value_mixdown(GhbValue *lin_val) const gchar *mix; str = ghb_value_get_string(lin_val); - mix = hb_mixdown_get_name(hb_mixdown_get_from_name(str)); + mix = hb_mixdown_get_short_name(hb_mixdown_get_from_name(str)); if (mix != NULL) sval = ghb_string_value_new(mix); @@ -2060,7 +2027,7 @@ export_value_video_encoder(GhbValue *lin_val) const gchar *enc; str = ghb_value_get_string(lin_val); - enc = hb_video_encoder_get_name(hb_video_encoder_get_from_name(str)); + enc = hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(str)); if (enc != NULL) sval = ghb_string_value_new(enc); @@ -2075,7 +2042,7 @@ export_value_audio_encoder(GhbValue *lin_val) const gchar *enc; str = ghb_value_get_string(lin_val); - enc = hb_audio_encoder_get_name(hb_audio_encoder_get_from_name(str)); + enc = hb_audio_encoder_get_short_name(hb_audio_encoder_get_from_name(str)); if (enc != NULL) sval = ghb_string_value_new(enc); @@ -2090,7 +2057,7 @@ export_value_container(GhbValue *lin_val) const gchar *mux; str = ghb_value_get_string(lin_val); - mux = hb_container_get_name(hb_container_get_from_name(str)); + mux = hb_container_get_short_name(hb_container_get_from_name(str)); if (mux != NULL) sval = ghb_string_value_new(mux); @@ -2104,6 +2071,12 @@ export_value_xlat(GhbValue *dict) GhbValue *lin_val, *gval; const gchar *key; + // Convert PictureModulus to correct data type + key = "PictureModulus"; + lin_val = ghb_dict_get(dict, key); + gval = ghb_value_xform(lin_val, GHB_INT); + if (gval) + ghb_dict_set(dict, key, gval); key = "VideoEncoder"; lin_val = ghb_dict_get(dict, key); gval = export_value_video_encoder(lin_val); @@ -2119,28 +2092,6 @@ export_value_xlat(GhbValue *dict) gval = export_value_video_framerate(lin_val); if (gval) ghb_dict_set(dict, key, gval); - key = "PictureDetelecine"; - lin_val = ghb_dict_get(dict, key); - gval = export_value_xlat2(detel_xlat, lin_val, GHB_INT); - if (gval) - ghb_dict_set(dict, key, gval); - key = "PictureDecomb"; - lin_val = ghb_dict_get(dict, key); - gval = export_value_xlat2(decomb_xlat, lin_val, GHB_INT); - if (gval) - ghb_dict_set(dict, key, gval); - key = "PictureDeinterlace"; - lin_val = ghb_dict_get(dict, key); - gval = export_value_xlat2(deint_xlat, lin_val, GHB_INT); - if (gval) - ghb_dict_set(dict, key, gval); -#if 0 - key = "PictureDenoisePreset"; - lin_val = ghb_dict_get(dict, key); - gval = export_value_xlat2(denoise_xlat, lin_val, GHB_INT); - if (gval) - ghb_dict_set(dict, key, gval); -#endif gint count, ii; GhbValue *alist; @@ -2803,6 +2754,41 @@ export_xlat_preset(GhbValue *dict) } } + GhbValue *copy_mask = ghb_array_new(); + if (ghb_value_get_bool(ghb_dict_get(dict, "AudioAllowMP3Pass"))) + { + ghb_array_append(copy_mask, ghb_string_value_new("copy:mp3")); + } + if (ghb_value_get_bool(ghb_dict_get(dict, "AudioAllowAACPass"))) + { + ghb_array_append(copy_mask, ghb_string_value_new("copy:aac")); + } + if (ghb_value_get_bool(ghb_dict_get(dict, "AudioAllowAC3Pass"))) + { + ghb_array_append(copy_mask, ghb_string_value_new("copy:ac3")); + } + if (ghb_value_get_bool(ghb_dict_get(dict, "AudioAllowDTSPass"))) + { + ghb_array_append(copy_mask, ghb_string_value_new("copy:dts")); + } + if (ghb_value_get_bool(ghb_dict_get(dict, "AudioAllowDTSHDPass"))) + { + ghb_array_append(copy_mask, ghb_string_value_new("copy:dtshd")); + } + if (ghb_value_get_bool(ghb_dict_get(dict, "AudioAllowEAC3Pass"))) + { + ghb_array_append(copy_mask, ghb_string_value_new("copy:eac3")); + } + if (ghb_value_get_bool(ghb_dict_get(dict, "AudioAllowFLACPass"))) + { + ghb_array_append(copy_mask, ghb_string_value_new("copy:flac")); + } + if (ghb_value_get_bool(ghb_dict_get(dict, "AudioAllowTRUEHDPass"))) + { + ghb_array_append(copy_mask, ghb_string_value_new("copy:truehd")); + } + ghb_dict_set(dict, "AudioCopyMask", copy_mask); + if (ghb_value_get_bool(ghb_dict_get(dict, "x264UseAdvancedOptions"))) { ghb_dict_remove(dict, "VideoPreset"); @@ -2841,31 +2827,50 @@ export_xlat_preset(GhbValue *dict) export_value_xlat(dict); } +static void export_xlat_presets(GhbValue *presets); + +static void +export_xlat_dict(GhbValue *dict) +{ + gboolean folder; + folder = ghb_value_get_bool(ghb_dict_get(dict, "Folder")); + if (folder) + { + GhbValue *nested; + + nested = ghb_dict_get(dict, "ChildrenArray"); + export_xlat_presets(nested); + } + else + { + export_xlat_preset(dict); + } +} + static void export_xlat_presets(GhbValue *presets) { gint count, ii; GhbValue *dict; - gboolean folder; if (presets == NULL) return; - count = ghb_array_len(presets); - for (ii = 0; ii < count; ii++) + if (ghb_value_type(presets) == GHB_DICT) { - dict = ghb_array_get(presets, ii); - folder = ghb_value_get_bool(ghb_dict_get(dict, "Folder")); - if (folder) - { - GhbValue *nested; - - nested = ghb_dict_get(dict, "ChildrenArray"); - export_xlat_presets(nested); - } - else + export_xlat_dict(presets); + } + else if (ghb_value_type(presets) == GHB_ARRAY) + { + count = ghb_array_len(presets); + for (ii = 0; ii < count; ii++) { - export_xlat_preset(dict); + dict = ghb_array_get(presets, ii); + export_xlat_dict(dict); } } + else + { + g_warning("export_xlat_presets: Invalid presets format"); + } } static guint prefs_timeout_id = 0; @@ -2933,8 +2938,6 @@ ghb_presets_reload(signal_user_data_t *ud) std_dict = ghb_array_get(std_presets, ii); copy_dict = ghb_value_dup(std_dict); - ghb_dict_set(copy_dict, "PresetBuildNumber", - ghb_int_value_new(hb_get_build(NULL))); ghb_presets_insert(presetsPlist, copy_dict, &indices, 1); presets_list_insert(ud, &indices, 1); } @@ -3009,8 +3012,6 @@ replace_standard_presets(GhbValue *presetsArray) std_dict = ghb_array_get(std_presets, ii); copy_dict = ghb_value_dup(std_dict); - ghb_dict_set(copy_dict, "PresetBuildNumber", - ghb_int_value_new(hb_get_build(NULL))); ghb_presets_insert(presetsArray, copy_dict, &indices, 1); } ghb_value_free(&std_presets); @@ -3026,7 +3027,6 @@ update_standard_presets(signal_user_data_t *ud, GhbValue *presetsArray) { GhbValue *dict; const GhbValue *gval; - gint64 build; gint type; dict = ghb_array_get(presetsArray, ii); @@ -3041,6 +3041,10 @@ update_standard_presets(signal_user_data_t *ud, GhbValue *presetsArray) type = ghb_value_get_int(gval); if (type == 0) { + // TODO: check preset version +#if 0 + gint64 build; + gval = ghb_dict_get(dict, "PresetBuildNumber"); if (gval == NULL) { @@ -3056,6 +3060,7 @@ update_standard_presets(signal_user_data_t *ud, GhbValue *presetsArray) replace_standard_presets(presetsArray); return 1; } +#endif } } return 0; @@ -3134,7 +3139,6 @@ settings_save(signal_user_data_t *ud, const GhbValue *path) } current_preset = dict; ghb_dict_set_int(dict, "Type", PRESETS_CUSTOM); - ghb_dict_set_int(dict, "PresetBuildNumber", hb_get_build(NULL)); ghb_dict_set(dict, "PresetName", ghb_string_value_new(name)); if (replace) @@ -3499,7 +3503,7 @@ preset_export_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { exportDir = "."; } - filename = g_strdup_printf("%s.plist", name); + filename = g_strdup_printf("%s.json", name); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), exportDir); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename); g_free(filename); @@ -3515,7 +3519,7 @@ preset_export_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) gtk_widget_hide(dialog); if (response == GTK_RESPONSE_ACCEPT) { - GhbValue *export, *dict, *array; + GhbValue *export, *dict; gchar *dir; filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); @@ -3524,14 +3528,9 @@ preset_export_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) dict = presets_get_dict(presetsPlist, indices, len); export = ghb_value_dup(dict); - array = ghb_array_new(); - ghb_array_append(array, export); - presets_clear_default(array); - presets_customize(array); - export_xlat_presets(array); - - store_plist(filename, array); - ghb_value_free(&array); + export_xlat_presets(export); + hb_preset_write_json(export, filename); + ghb_value_free(&export); exportDir = ghb_dict_get_string(ud->prefs, "ExportDirectory"); dir = g_path_get_dirname(filename); diff --git a/gtk/src/queuehandler.c b/gtk/src/queuehandler.c index 67ce7dca4..876d49ec9 100644 --- a/gtk/src/queuehandler.c +++ b/gtk/src/queuehandler.c @@ -236,13 +236,14 @@ add_to_queue_list(signal_user_data_t *ud, GhbValue *settings, GtkTreeIter *piter // Next line in the display (Picture settings) // Picture: Source: W x H, Output W x H (Animorphic), Display W x H - int width, height, pic_par; + const char *pic_par; + int width, height; int crop[4]; gboolean keep_aspect; width = ghb_dict_get_int(settings, "scale_width"); height = ghb_dict_get_int(settings, "scale_height"); - pic_par = ghb_dict_get_int(settings, "PicturePAR"); + pic_par = ghb_dict_get_string(settings, "PicturePAR"); keep_aspect = ghb_dict_get_bool(settings, "PictureKeepRatio"); crop[0] = ghb_dict_get_int(settings, "PictureTopCrop"); crop[1] = ghb_dict_get_int(settings, "PictureBottomCrop"); @@ -250,9 +251,7 @@ add_to_queue_list(signal_user_data_t *ud, GhbValue *settings, GtkTreeIter *piter crop[3] = ghb_dict_get_int(settings, "PictureRightCrop"); gchar *aspect_desc; - switch (pic_par) - { - case 0: + if (pic_par == NULL || !strcasecmp(pic_par, "off")) { if (keep_aspect) { @@ -262,27 +261,18 @@ add_to_queue_list(signal_user_data_t *ud, GhbValue *settings, GtkTreeIter *piter { aspect_desc = _("(Aspect Lost)"); } - } break; - - case 1: - { - aspect_desc = _("(Anamorphic)"); - } break; - - case 2: + } + else if (!strcasecmp(pic_par, "strict") || !strcasecmp(pic_par, "loose")) { aspect_desc = _("(Anamorphic)"); - } break; - - case 3: + } + else if (!strcasecmp(pic_par, "custom")) { aspect_desc = _("(Custom Anamorphic)"); - } break; - - default: + } + else { aspect_desc = ""; - } break; } gint source_width, source_height; diff --git a/gtk/src/standard_presets.json b/gtk/src/standard_presets.json index 160a09d82..41c7be767 100644 --- a/gtk/src/standard_presets.json +++ b/gtk/src/standard_presets.json @@ -50,7 +50,7 @@ "PictureKeepRatio": 0, "PictureLeftCrop": 0, "PictureModulus": 2, - "PicturePAR": 2, + "PicturePAR": "loose", "PictureRightCrop": 0, "PictureTopCrop": 0, "PictureWidth": 720, @@ -58,7 +58,7 @@ "PresetName": "Universal", "Subtitles": "None", "Type": 0, - "UsesPictureFilters": 1, + "UsesPictureFilters": true, "UsesPictureSettings": 1, "VideoAvgBitrate": "2500", "VideoEncoder": "H.264 (x264)", @@ -117,7 +117,7 @@ "PictureKeepRatio": 1, "PictureLeftCrop": 0, "PictureModulus": 2, - "PicturePAR": 0, + "PicturePAR": "off", "PictureRightCrop": 0, "PictureTopCrop": 0, "PictureWidth": 320, @@ -125,7 +125,7 @@ "PresetName": "iPod", "Subtitles": "None", "Type": 0, - "UsesPictureFilters": 1, + "UsesPictureFilters": true, "UsesPictureSettings": 1, "VideoAvgBitrate": "2500", "VideoEncoder": "H.264 (x264)", @@ -184,7 +184,7 @@ "PictureKeepRatio": 0, "PictureLeftCrop": 0, "PictureModulus": 2, - "PicturePAR": 2, + "PicturePAR": "loose", "PictureRightCrop": 0, "PictureTopCrop": 0, "PictureWidth": 960, @@ -192,7 +192,7 @@ "PresetName": "iPhone & iPod touch", "Subtitles": "None", "Type": 0, - "UsesPictureFilters": 1, + "UsesPictureFilters": true, "UsesPictureSettings": 1, "VideoAvgBitrate": "2500", "VideoEncoder": "H.264 (x264)", @@ -251,7 +251,7 @@ "PictureKeepRatio": 0, "PictureLeftCrop": 0, "PictureModulus": 2, - "PicturePAR": 2, + "PicturePAR": "loose", "PictureRightCrop": 0, "PictureTopCrop": 0, "PictureWidth": 1280, @@ -259,7 +259,7 @@ "PresetName": "iPad", "Subtitles": "None", "Type": 0, - "UsesPictureFilters": 1, + "UsesPictureFilters": true, "UsesPictureSettings": 1, "VideoAvgBitrate": "2500", "VideoEncoder": "H.264 (x264)", @@ -327,7 +327,7 @@ "PictureKeepRatio": 0, "PictureLeftCrop": 0, "PictureModulus": 2, - "PicturePAR": 2, + "PicturePAR": "loose", "PictureRightCrop": 0, "PictureTopCrop": 0, "PictureWidth": 960, @@ -335,7 +335,7 @@ "PresetName": "AppleTV", "Subtitles": "None", "Type": 0, - "UsesPictureFilters": 1, + "UsesPictureFilters": true, "UsesPictureSettings": 1, "VideoAvgBitrate": "2500", "VideoEncoder": "H.264 (x264)", @@ -403,7 +403,7 @@ "PictureKeepRatio": 0, "PictureLeftCrop": 0, "PictureModulus": 2, - "PicturePAR": 2, + "PicturePAR": "loose", "PictureRightCrop": 0, "PictureTopCrop": 0, "PictureWidth": 1280, @@ -411,7 +411,7 @@ "PresetName": "AppleTV 2", "Subtitles": "None", "Type": 0, - "UsesPictureFilters": 1, + "UsesPictureFilters": true, "UsesPictureSettings": 1, "VideoAvgBitrate": "2500", "VideoEncoder": "H.264 (x264)", @@ -479,7 +479,7 @@ "PictureKeepRatio": 0, "PictureLeftCrop": 0, "PictureModulus": 2, - "PicturePAR": 2, + "PicturePAR": "loose", "PictureRightCrop": 0, "PictureTopCrop": 0, "PictureWidth": 1920, @@ -487,7 +487,7 @@ "PresetName": "AppleTV 3", "Subtitles": "None", "Type": 0, - "UsesPictureFilters": 1, + "UsesPictureFilters": true, "UsesPictureSettings": 1, "VideoAvgBitrate": "2500", "VideoEncoder": "H.264 (x264)", @@ -546,7 +546,7 @@ "PictureKeepRatio": 0, "PictureLeftCrop": 0, "PictureModulus": 2, - "PicturePAR": 2, + "PicturePAR": "loose", "PictureRightCrop": 0, "PictureTopCrop": 0, "PictureWidth": 720, @@ -554,7 +554,7 @@ "PresetName": "Android", "Subtitles": "None", "Type": 0, - "UsesPictureFilters": 1, + "UsesPictureFilters": true, "UsesPictureSettings": 1, "VideoAvgBitrate": "2500", "VideoEncoder": "H.264 (x264)", @@ -613,7 +613,7 @@ "PictureKeepRatio": 0, "PictureLeftCrop": 0, "PictureModulus": 2, - "PicturePAR": 2, + "PicturePAR": "loose", "PictureRightCrop": 0, "PictureTopCrop": 0, "PictureWidth": 1280, @@ -621,7 +621,7 @@ "PresetName": "Android Tablet", "Subtitles": "None", "Type": 0, - "UsesPictureFilters": 1, + "UsesPictureFilters": true, "UsesPictureSettings": 1, "VideoAvgBitrate": "2500", "VideoEncoder": "H.264 (x264)", @@ -680,7 +680,7 @@ "PictureKeepRatio": 1, "PictureLeftCrop": 0, "PictureModulus": 2, - "PicturePAR": 0, + "PicturePAR": "off", "PictureRightCrop": 0, "PictureTopCrop": 0, "PictureWidth": 1280, @@ -688,7 +688,7 @@ "PresetName": "Windows Phone 8", "Subtitles": "None", "Type": 0, - "UsesPictureFilters": 1, + "UsesPictureFilters": true, "UsesPictureSettings": 1, "VideoAvgBitrate": "2500", "VideoEncoder": "H.264 (x264)", @@ -710,7 +710,6 @@ ], "Default": 0, "Folder": true, - "PresetBuildNumber": 2013061301, "PresetName": "Devices", "Type": 0 }, @@ -756,7 +755,7 @@ "PictureKeepRatio": 0, "PictureLeftCrop": 0, "PictureModulus": 2, - "PicturePAR": 2, + "PicturePAR": "loose", "PictureRightCrop": 0, "PictureTopCrop": 0, "PictureWidth": 0, @@ -764,7 +763,7 @@ "PresetName": "Normal", "Subtitles": "None", "Type": 0, - "UsesPictureFilters": 1, + "UsesPictureFilters": true, "UsesPictureSettings": 1, "VideoAvgBitrate": "2500", "VideoEncoder": "H.264 (x264)", @@ -832,7 +831,7 @@ "PictureKeepRatio": 0, "PictureLeftCrop": 0, "PictureModulus": 2, - "PicturePAR": 2, + "PicturePAR": "loose", "PictureRightCrop": 0, "PictureTopCrop": 0, "PictureWidth": 0, @@ -840,7 +839,7 @@ "PresetName": "High Profile", "Subtitles": "None", "Type": 0, - "UsesPictureFilters": 1, + "UsesPictureFilters": true, "UsesPictureSettings": 1, "VideoAvgBitrate": "2500", "VideoEncoder": "H.264 (x264)", @@ -862,8 +861,7 @@ ], "Default": 0, "Folder": true, - "PresetBuildNumber": 2013061301, "PresetName": "Regular", "Type": 0 } -]
\ No newline at end of file +] diff --git a/libhb/builtin_presets.h b/libhb/builtin_presets.h new file mode 100644 index 000000000..b0b3e5cd8 --- /dev/null +++ b/libhb/builtin_presets.h @@ -0,0 +1,965 @@ +const char hb_builtin_presets_json[] = +"{\n" +" \"PresetBuiltin\": [\n" +" {\n" +" \"ChildrenArray\": [\n" +" {\n" +" \"AudioAllowAACPass\": 1, \n" +" \"AudioAllowAC3Pass\": 1, \n" +" \"AudioAllowDTSHDPass\": 1, \n" +" \"AudioAllowDTSPass\": 1, \n" +" \"AudioAllowMP3Pass\": 1, \n" +" \"AudioEncoderFallback\": \"ac3\", \n" +" \"AudioList\": [\n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"aac\", \n" +" \"AudioMixdown\": \"dpl2\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }, \n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"copy:ac3\", \n" +" \"AudioMixdown\": \"none\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }\n" +" ], \n" +" \"ChapterMarkers\": 1, \n" +" \"Default\": 0, \n" +" \"FileFormat\": \"mp4\", \n" +" \"Folder\": false, \n" +" \"Mp4HttpOptimize\": 0, \n" +" \"Mp4iPodCompatible\": 0, \n" +" \"PictureAutoCrop\": 1, \n" +" \"PictureBottomCrop\": 0, \n" +" \"PictureDeblock\": 0, \n" +" \"PictureDecomb\": 0, \n" +" \"PictureDecombCustom\": \"\", \n" +" \"PictureDecombDeinterlace\": 1, \n" +" \"PictureDeinterlace\": 0, \n" +" \"PictureDeinterlaceCustom\": \"\", \n" +" \"PictureDenoiseCustom\": \"\", \n" +" \"PictureDenoiseFilter\": \"off\", \n" +" \"PictureDetelecine\": 0, \n" +" \"PictureDetelecineCustom\": \"\", \n" +" \"PictureHeight\": 576, \n" +" \"PictureKeepRatio\": 0, \n" +" \"PictureLeftCrop\": 0, \n" +" \"PictureModulus\": 2, \n" +" \"PicturePAR\": \"loose\", \n" +" \"PictureRightCrop\": 0, \n" +" \"PictureTopCrop\": 0, \n" +" \"PictureWidth\": 720, \n" +" \"PresetDescription\": \"HandBrake's settings for compatibility with all Apple devices (including the iPod 6G and later). Includes Dolby Digital audio for surround sound.\", \n" +" \"PresetName\": \"Universal\", \n" +" \"Type\": 0, \n" +" \"UsesPictureFilters\": 1, \n" +" \"UsesPictureSettings\": 1, \n" +" \"VideoAvgBitrate\": \"2500\", \n" +" \"VideoEncoder\": \"x264\", \n" +" \"VideoFramerate\": \"30\", \n" +" \"VideoFramerateMode\": \"pfr\", \n" +" \"VideoGrayScale\": 0, \n" +" \"VideoLevel\": \"3.0\", \n" +" \"VideoOptionExtra\": \"\", \n" +" \"VideoPreset\": \"fast\", \n" +" \"VideoProfile\": \"baseline\", \n" +" \"VideoQualitySlider\": 20.0, \n" +" \"VideoQualityType\": 2, \n" +" \"VideoTune\": \"\", \n" +" \"VideoTurboTwoPass\": 0, \n" +" \"VideoTwoPass\": 0, \n" +" \"x264Option\": \"\", \n" +" \"x264UseAdvancedOptions\": 0\n" +" }, \n" +" {\n" +" \"AudioAllowAACPass\": 1, \n" +" \"AudioAllowAC3Pass\": 1, \n" +" \"AudioAllowDTSHDPass\": 1, \n" +" \"AudioAllowDTSPass\": 1, \n" +" \"AudioAllowMP3Pass\": 1, \n" +" \"AudioEncoderFallback\": \"ac3\", \n" +" \"AudioList\": [\n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"aac\", \n" +" \"AudioMixdown\": \"dpl2\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }\n" +" ], \n" +" \"ChapterMarkers\": 1, \n" +" \"Default\": 0, \n" +" \"FileFormat\": \"mp4\", \n" +" \"Folder\": false, \n" +" \"Mp4HttpOptimize\": 0, \n" +" \"Mp4iPodCompatible\": 1, \n" +" \"PictureAutoCrop\": 1, \n" +" \"PictureBottomCrop\": 0, \n" +" \"PictureDeblock\": 0, \n" +" \"PictureDecomb\": 0, \n" +" \"PictureDecombCustom\": \"\", \n" +" \"PictureDecombDeinterlace\": 1, \n" +" \"PictureDeinterlace\": 0, \n" +" \"PictureDeinterlaceCustom\": \"\", \n" +" \"PictureDenoiseCustom\": \"\", \n" +" \"PictureDenoiseFilter\": \"off\", \n" +" \"PictureDetelecine\": 0, \n" +" \"PictureDetelecineCustom\": \"\", \n" +" \"PictureHeight\": 240, \n" +" \"PictureKeepRatio\": 1, \n" +" \"PictureLeftCrop\": 0, \n" +" \"PictureModulus\": 2, \n" +" \"PicturePAR\": \"off\", \n" +" \"PictureRightCrop\": 0, \n" +" \"PictureTopCrop\": 0, \n" +" \"PictureWidth\": 320, \n" +" \"PresetDescription\": \"HandBrake's settings for playback on the iPod with Video (all generations).\", \n" +" \"PresetName\": \"iPod\", \n" +" \"Type\": 0, \n" +" \"UsesPictureFilters\": 1, \n" +" \"UsesPictureSettings\": 1, \n" +" \"VideoAvgBitrate\": \"2500\", \n" +" \"VideoEncoder\": \"x264\", \n" +" \"VideoFramerate\": \"30\", \n" +" \"VideoFramerateMode\": \"pfr\", \n" +" \"VideoGrayScale\": 0, \n" +" \"VideoLevel\": \"1.3\", \n" +" \"VideoOptionExtra\": \"\", \n" +" \"VideoPreset\": \"medium\", \n" +" \"VideoProfile\": \"baseline\", \n" +" \"VideoQualitySlider\": 22.0, \n" +" \"VideoQualityType\": 2, \n" +" \"VideoTune\": \"\", \n" +" \"VideoTurboTwoPass\": 0, \n" +" \"VideoTwoPass\": 0, \n" +" \"x264Option\": \"\", \n" +" \"x264UseAdvancedOptions\": 0\n" +" }, \n" +" {\n" +" \"AudioAllowAACPass\": 1, \n" +" \"AudioAllowAC3Pass\": 1, \n" +" \"AudioAllowDTSHDPass\": 1, \n" +" \"AudioAllowDTSPass\": 1, \n" +" \"AudioAllowMP3Pass\": 1, \n" +" \"AudioEncoderFallback\": \"ac3\", \n" +" \"AudioList\": [\n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"aac\", \n" +" \"AudioMixdown\": \"dpl2\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }\n" +" ], \n" +" \"ChapterMarkers\": 1, \n" +" \"Default\": 0, \n" +" \"FileFormat\": \"mp4\", \n" +" \"Folder\": false, \n" +" \"Mp4HttpOptimize\": 0, \n" +" \"Mp4iPodCompatible\": 0, \n" +" \"PictureAutoCrop\": 1, \n" +" \"PictureBottomCrop\": 0, \n" +" \"PictureDeblock\": 0, \n" +" \"PictureDecomb\": 0, \n" +" \"PictureDecombCustom\": \"\", \n" +" \"PictureDecombDeinterlace\": 1, \n" +" \"PictureDeinterlace\": 0, \n" +" \"PictureDeinterlaceCustom\": \"\", \n" +" \"PictureDenoiseCustom\": \"\", \n" +" \"PictureDenoiseFilter\": \"off\", \n" +" \"PictureDetelecine\": 0, \n" +" \"PictureDetelecineCustom\": \"\", \n" +" \"PictureHeight\": 640, \n" +" \"PictureKeepRatio\": 0, \n" +" \"PictureLeftCrop\": 0, \n" +" \"PictureModulus\": 2, \n" +" \"PicturePAR\": \"loose\", \n" +" \"PictureRightCrop\": 0, \n" +" \"PictureTopCrop\": 0, \n" +" \"PictureWidth\": 960, \n" +" \"PresetDescription\": \"HandBrake's settings for handheld iOS devices (iPhone 4, iPod touch 3G and later).\", \n" +" \"PresetName\": \"iPhone & iPod touch\", \n" +" \"Type\": 0, \n" +" \"UsesPictureFilters\": 1, \n" +" \"UsesPictureSettings\": 1, \n" +" \"VideoAvgBitrate\": \"2500\", \n" +" \"VideoEncoder\": \"x264\", \n" +" \"VideoFramerate\": \"30\", \n" +" \"VideoFramerateMode\": \"pfr\", \n" +" \"VideoGrayScale\": 0, \n" +" \"VideoLevel\": \"3.1\", \n" +" \"VideoOptionExtra\": \"\", \n" +" \"VideoPreset\": \"medium\", \n" +" \"VideoProfile\": \"high\", \n" +" \"VideoQualitySlider\": 22.0, \n" +" \"VideoQualityType\": 2, \n" +" \"VideoTune\": \"\", \n" +" \"VideoTurboTwoPass\": 0, \n" +" \"VideoTwoPass\": 0, \n" +" \"x264Option\": \"\", \n" +" \"x264UseAdvancedOptions\": 0\n" +" }, \n" +" {\n" +" \"AudioAllowAACPass\": 1, \n" +" \"AudioAllowAC3Pass\": 1, \n" +" \"AudioAllowDTSHDPass\": 1, \n" +" \"AudioAllowDTSPass\": 1, \n" +" \"AudioAllowMP3Pass\": 1, \n" +" \"AudioEncoderFallback\": \"ac3\", \n" +" \"AudioList\": [\n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"aac\", \n" +" \"AudioMixdown\": \"dpl2\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }\n" +" ], \n" +" \"ChapterMarkers\": 1, \n" +" \"Default\": 0, \n" +" \"FileFormat\": \"mp4\", \n" +" \"Folder\": false, \n" +" \"Mp4HttpOptimize\": 0, \n" +" \"Mp4iPodCompatible\": 0, \n" +" \"PictureAutoCrop\": 1, \n" +" \"PictureBottomCrop\": 0, \n" +" \"PictureDeblock\": 0, \n" +" \"PictureDecomb\": 0, \n" +" \"PictureDecombCustom\": \"\", \n" +" \"PictureDecombDeinterlace\": 1, \n" +" \"PictureDeinterlace\": 0, \n" +" \"PictureDeinterlaceCustom\": \"\", \n" +" \"PictureDenoiseCustom\": \"\", \n" +" \"PictureDenoiseFilter\": \"off\", \n" +" \"PictureDetelecine\": 0, \n" +" \"PictureDetelecineCustom\": \"\", \n" +" \"PictureHeight\": 720, \n" +" \"PictureKeepRatio\": 0, \n" +" \"PictureLeftCrop\": 0, \n" +" \"PictureModulus\": 2, \n" +" \"PicturePAR\": \"loose\", \n" +" \"PictureRightCrop\": 0, \n" +" \"PictureTopCrop\": 0, \n" +" \"PictureWidth\": 1280, \n" +" \"PresetDescription\": \"HandBrake's settings for playback on the iPad (all generations).\", \n" +" \"PresetName\": \"iPad\", \n" +" \"Type\": 0, \n" +" \"UsesPictureFilters\": 1, \n" +" \"UsesPictureSettings\": 1, \n" +" \"VideoAvgBitrate\": \"2500\", \n" +" \"VideoEncoder\": \"x264\", \n" +" \"VideoFramerate\": \"30\", \n" +" \"VideoFramerateMode\": \"pfr\", \n" +" \"VideoGrayScale\": 0, \n" +" \"VideoLevel\": \"3.1\", \n" +" \"VideoOptionExtra\": \"\", \n" +" \"VideoPreset\": \"medium\", \n" +" \"VideoProfile\": \"high\", \n" +" \"VideoQualitySlider\": 20.0, \n" +" \"VideoQualityType\": 2, \n" +" \"VideoTune\": \"\", \n" +" \"VideoTurboTwoPass\": 0, \n" +" \"VideoTwoPass\": 0, \n" +" \"x264Option\": \"\", \n" +" \"x264UseAdvancedOptions\": 0\n" +" }, \n" +" {\n" +" \"AudioAllowAACPass\": 1, \n" +" \"AudioAllowAC3Pass\": 1, \n" +" \"AudioAllowDTSHDPass\": 1, \n" +" \"AudioAllowDTSPass\": 1, \n" +" \"AudioAllowMP3Pass\": 1, \n" +" \"AudioEncoderFallback\": \"ac3\", \n" +" \"AudioList\": [\n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"aac\", \n" +" \"AudioMixdown\": \"dpl2\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }, \n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"copy:ac3\", \n" +" \"AudioMixdown\": \"none\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }\n" +" ], \n" +" \"ChapterMarkers\": 1, \n" +" \"Default\": 0, \n" +" \"FileFormat\": \"mp4\", \n" +" \"Folder\": false, \n" +" \"Mp4HttpOptimize\": 0, \n" +" \"Mp4iPodCompatible\": 0, \n" +" \"PictureAutoCrop\": 1, \n" +" \"PictureBottomCrop\": 0, \n" +" \"PictureDeblock\": 0, \n" +" \"PictureDecomb\": 0, \n" +" \"PictureDecombCustom\": \"\", \n" +" \"PictureDecombDeinterlace\": 1, \n" +" \"PictureDeinterlace\": 0, \n" +" \"PictureDeinterlaceCustom\": \"\", \n" +" \"PictureDenoiseCustom\": \"\", \n" +" \"PictureDenoiseFilter\": \"off\", \n" +" \"PictureDetelecine\": 0, \n" +" \"PictureDetelecineCustom\": \"\", \n" +" \"PictureHeight\": 720, \n" +" \"PictureKeepRatio\": 0, \n" +" \"PictureLeftCrop\": 0, \n" +" \"PictureModulus\": 2, \n" +" \"PicturePAR\": \"loose\", \n" +" \"PictureRightCrop\": 0, \n" +" \"PictureTopCrop\": 0, \n" +" \"PictureWidth\": 960, \n" +" \"PresetDescription\": \"HandBrake's settings for the original AppleTV. Includes Dolby Digital audio for surround sound. Also compatible with iOS devices released since 2009.\", \n" +" \"PresetName\": \"AppleTV\", \n" +" \"Type\": 0, \n" +" \"UsesPictureFilters\": 1, \n" +" \"UsesPictureSettings\": 1, \n" +" \"VideoAvgBitrate\": \"2500\", \n" +" \"VideoEncoder\": \"x264\", \n" +" \"VideoFramerate\": \"30\", \n" +" \"VideoFramerateMode\": \"pfr\", \n" +" \"VideoGrayScale\": 0, \n" +" \"VideoLevel\": \"3.1\", \n" +" \"VideoOptionExtra\": \"qpmin=4:cabac=0:ref=2:b-pyramid=none:weightb=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500\", \n" +" \"VideoPreset\": \"medium\", \n" +" \"VideoProfile\": \"high\", \n" +" \"VideoQualitySlider\": 20.0, \n" +" \"VideoQualityType\": 2, \n" +" \"VideoTune\": \"\", \n" +" \"VideoTurboTwoPass\": 0, \n" +" \"VideoTwoPass\": 0, \n" +" \"x264Option\": \"\", \n" +" \"x264UseAdvancedOptions\": 0\n" +" }, \n" +" {\n" +" \"AudioAllowAACPass\": 1, \n" +" \"AudioAllowAC3Pass\": 1, \n" +" \"AudioAllowDTSHDPass\": 1, \n" +" \"AudioAllowDTSPass\": 1, \n" +" \"AudioAllowMP3Pass\": 1, \n" +" \"AudioEncoderFallback\": \"ac3\", \n" +" \"AudioList\": [\n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"aac\", \n" +" \"AudioMixdown\": \"dpl2\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }, \n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"copy:ac3\", \n" +" \"AudioMixdown\": \"none\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }\n" +" ], \n" +" \"ChapterMarkers\": 1, \n" +" \"Default\": 0, \n" +" \"FileFormat\": \"mp4\", \n" +" \"Folder\": false, \n" +" \"Mp4HttpOptimize\": 0, \n" +" \"Mp4iPodCompatible\": 0, \n" +" \"PictureAutoCrop\": 1, \n" +" \"PictureBottomCrop\": 0, \n" +" \"PictureDeblock\": 0, \n" +" \"PictureDecomb\": 0, \n" +" \"PictureDecombCustom\": \"\", \n" +" \"PictureDecombDeinterlace\": 1, \n" +" \"PictureDeinterlace\": 0, \n" +" \"PictureDeinterlaceCustom\": \"\", \n" +" \"PictureDenoiseCustom\": \"\", \n" +" \"PictureDenoiseFilter\": \"off\", \n" +" \"PictureDetelecine\": 0, \n" +" \"PictureDetelecineCustom\": \"\", \n" +" \"PictureHeight\": 720, \n" +" \"PictureKeepRatio\": 0, \n" +" \"PictureLeftCrop\": 0, \n" +" \"PictureModulus\": 2, \n" +" \"PicturePAR\": \"loose\", \n" +" \"PictureRightCrop\": 0, \n" +" \"PictureTopCrop\": 0, \n" +" \"PictureWidth\": 1280, \n" +" \"PresetDescription\": \"HandBrake's settings for the second-generation AppleTV. Includes Dolby Digital audio for surround sound. NOT compatible with the original AppleTV.\", \n" +" \"PresetName\": \"AppleTV 2\", \n" +" \"Type\": 0, \n" +" \"UsesPictureFilters\": 1, \n" +" \"UsesPictureSettings\": 1, \n" +" \"VideoAvgBitrate\": \"2500\", \n" +" \"VideoEncoder\": \"x264\", \n" +" \"VideoFramerate\": \"30\", \n" +" \"VideoFramerateMode\": \"pfr\", \n" +" \"VideoGrayScale\": 0, \n" +" \"VideoLevel\": \"3.1\", \n" +" \"VideoOptionExtra\": \"\", \n" +" \"VideoPreset\": \"medium\", \n" +" \"VideoProfile\": \"high\", \n" +" \"VideoQualitySlider\": 20.0, \n" +" \"VideoQualityType\": 2, \n" +" \"VideoTune\": \"\", \n" +" \"VideoTurboTwoPass\": 0, \n" +" \"VideoTwoPass\": 0, \n" +" \"x264Option\": \"\", \n" +" \"x264UseAdvancedOptions\": 0\n" +" }, \n" +" {\n" +" \"AudioAllowAACPass\": 1, \n" +" \"AudioAllowAC3Pass\": 1, \n" +" \"AudioAllowDTSHDPass\": 1, \n" +" \"AudioAllowDTSPass\": 1, \n" +" \"AudioAllowMP3Pass\": 1, \n" +" \"AudioEncoderFallback\": \"ac3\", \n" +" \"AudioList\": [\n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"aac\", \n" +" \"AudioMixdown\": \"dpl2\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }, \n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"copy:ac3\", \n" +" \"AudioMixdown\": \"none\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }\n" +" ], \n" +" \"ChapterMarkers\": 1, \n" +" \"Default\": 0, \n" +" \"FileFormat\": \"mp4\", \n" +" \"Folder\": false, \n" +" \"Mp4HttpOptimize\": 0, \n" +" \"Mp4iPodCompatible\": 0, \n" +" \"PictureAutoCrop\": 1, \n" +" \"PictureBottomCrop\": 0, \n" +" \"PictureDeblock\": 0, \n" +" \"PictureDecomb\": 3, \n" +" \"PictureDecombCustom\": \"\", \n" +" \"PictureDecombDeinterlace\": 1, \n" +" \"PictureDeinterlace\": 0, \n" +" \"PictureDeinterlaceCustom\": \"\", \n" +" \"PictureDenoiseCustom\": \"\", \n" +" \"PictureDenoiseFilter\": \"off\", \n" +" \"PictureDetelecine\": 0, \n" +" \"PictureDetelecineCustom\": \"\", \n" +" \"PictureHeight\": 1080, \n" +" \"PictureKeepRatio\": 0, \n" +" \"PictureLeftCrop\": 0, \n" +" \"PictureModulus\": 2, \n" +" \"PicturePAR\": \"loose\", \n" +" \"PictureRightCrop\": 0, \n" +" \"PictureTopCrop\": 0, \n" +" \"PictureWidth\": 1920, \n" +" \"PresetDescription\": \"HandBrake's settings for the third-generation AppleTV. Includes Dolby Digital audio for surround sound. NOT compatible with the original AppleTV. May stutter on the second-generation AppleTV.\", \n" +" \"PresetName\": \"AppleTV 3\", \n" +" \"Type\": 0, \n" +" \"UsesPictureFilters\": 1, \n" +" \"UsesPictureSettings\": 1, \n" +" \"VideoAvgBitrate\": \"2500\", \n" +" \"VideoEncoder\": \"x264\", \n" +" \"VideoFramerate\": \"30\", \n" +" \"VideoFramerateMode\": \"pfr\", \n" +" \"VideoGrayScale\": 0, \n" +" \"VideoLevel\": \"4.0\", \n" +" \"VideoOptionExtra\": \"\", \n" +" \"VideoPreset\": \"medium\", \n" +" \"VideoProfile\": \"high\", \n" +" \"VideoQualitySlider\": 20.0, \n" +" \"VideoQualityType\": 2, \n" +" \"VideoTune\": \"\", \n" +" \"VideoTurboTwoPass\": 0, \n" +" \"VideoTwoPass\": 0, \n" +" \"x264Option\": \"\", \n" +" \"x264UseAdvancedOptions\": 0\n" +" }, \n" +" {\n" +" \"AudioAllowAACPass\": 1, \n" +" \"AudioAllowAC3Pass\": 1, \n" +" \"AudioAllowDTSHDPass\": 1, \n" +" \"AudioAllowDTSPass\": 1, \n" +" \"AudioAllowMP3Pass\": 1, \n" +" \"AudioEncoderFallback\": \"ac3\", \n" +" \"AudioList\": [\n" +" {\n" +" \"AudioBitrate\": \"128\", \n" +" \"AudioEncoder\": \"aac\", \n" +" \"AudioMixdown\": \"dpl2\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }\n" +" ], \n" +" \"ChapterMarkers\": 0, \n" +" \"Default\": 0, \n" +" \"FileFormat\": \"mp4\", \n" +" \"Folder\": false, \n" +" \"Mp4HttpOptimize\": 0, \n" +" \"Mp4iPodCompatible\": 0, \n" +" \"PictureAutoCrop\": 1, \n" +" \"PictureBottomCrop\": 0, \n" +" \"PictureDeblock\": 0, \n" +" \"PictureDecomb\": 0, \n" +" \"PictureDecombCustom\": \"\", \n" +" \"PictureDecombDeinterlace\": 1, \n" +" \"PictureDeinterlace\": 0, \n" +" \"PictureDeinterlaceCustom\": \"\", \n" +" \"PictureDenoiseCustom\": \"\", \n" +" \"PictureDenoiseFilter\": \"off\", \n" +" \"PictureDetelecine\": 0, \n" +" \"PictureDetelecineCustom\": \"\", \n" +" \"PictureHeight\": 576, \n" +" \"PictureKeepRatio\": 0, \n" +" \"PictureLeftCrop\": 0, \n" +" \"PictureModulus\": 2, \n" +" \"PicturePAR\": \"loose\", \n" +" \"PictureRightCrop\": 0, \n" +" \"PictureTopCrop\": 0, \n" +" \"PictureWidth\": 720, \n" +" \"PresetDescription\": \"HandBrake's settings for midrange devices running Android 2.3 or later.\", \n" +" \"PresetName\": \"Android\", \n" +" \"Type\": 0, \n" +" \"UsesPictureFilters\": 1, \n" +" \"UsesPictureSettings\": 1, \n" +" \"VideoAvgBitrate\": \"2500\", \n" +" \"VideoEncoder\": \"x264\", \n" +" \"VideoFramerate\": \"30\", \n" +" \"VideoFramerateMode\": \"pfr\", \n" +" \"VideoGrayScale\": 0, \n" +" \"VideoLevel\": \"3.0\", \n" +" \"VideoOptionExtra\": \"\", \n" +" \"VideoPreset\": \"medium\", \n" +" \"VideoProfile\": \"main\", \n" +" \"VideoQualitySlider\": 22.0, \n" +" \"VideoQualityType\": 2, \n" +" \"VideoTune\": \"\", \n" +" \"VideoTurboTwoPass\": 0, \n" +" \"VideoTwoPass\": 0, \n" +" \"x264Option\": \"\", \n" +" \"x264UseAdvancedOptions\": 0\n" +" }, \n" +" {\n" +" \"AudioAllowAACPass\": 1, \n" +" \"AudioAllowAC3Pass\": 1, \n" +" \"AudioAllowDTSHDPass\": 1, \n" +" \"AudioAllowDTSPass\": 1, \n" +" \"AudioAllowMP3Pass\": 1, \n" +" \"AudioEncoderFallback\": \"ac3\", \n" +" \"AudioList\": [\n" +" {\n" +" \"AudioBitrate\": \"128\", \n" +" \"AudioEncoder\": \"aac\", \n" +" \"AudioMixdown\": \"dpl2\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }\n" +" ], \n" +" \"ChapterMarkers\": 0, \n" +" \"Default\": 0, \n" +" \"FileFormat\": \"mp4\", \n" +" \"Folder\": false, \n" +" \"Mp4HttpOptimize\": 0, \n" +" \"Mp4iPodCompatible\": 0, \n" +" \"PictureAutoCrop\": 1, \n" +" \"PictureBottomCrop\": 0, \n" +" \"PictureDeblock\": 0, \n" +" \"PictureDecomb\": 0, \n" +" \"PictureDecombCustom\": \"\", \n" +" \"PictureDecombDeinterlace\": 1, \n" +" \"PictureDeinterlace\": 0, \n" +" \"PictureDeinterlaceCustom\": \"\", \n" +" \"PictureDenoiseCustom\": \"\", \n" +" \"PictureDenoiseFilter\": \"off\", \n" +" \"PictureDetelecine\": 0, \n" +" \"PictureDetelecineCustom\": \"\", \n" +" \"PictureHeight\": 720, \n" +" \"PictureKeepRatio\": 0, \n" +" \"PictureLeftCrop\": 0, \n" +" \"PictureModulus\": 2, \n" +" \"PicturePAR\": \"loose\", \n" +" \"PictureRightCrop\": 0, \n" +" \"PictureTopCrop\": 0, \n" +" \"PictureWidth\": 1280, \n" +" \"PresetDescription\": \"HandBrake's preset for tablets running Android 2.3 or later.\", \n" +" \"PresetName\": \"Android Tablet\", \n" +" \"Type\": 0, \n" +" \"UsesPictureFilters\": 1, \n" +" \"UsesPictureSettings\": 1, \n" +" \"VideoAvgBitrate\": \"2500\", \n" +" \"VideoEncoder\": \"x264\", \n" +" \"VideoFramerate\": \"30\", \n" +" \"VideoFramerateMode\": \"pfr\", \n" +" \"VideoGrayScale\": 0, \n" +" \"VideoLevel\": \"3.1\", \n" +" \"VideoOptionExtra\": \"\", \n" +" \"VideoPreset\": \"medium\", \n" +" \"VideoProfile\": \"main\", \n" +" \"VideoQualitySlider\": 22.0, \n" +" \"VideoQualityType\": 2, \n" +" \"VideoTune\": \"\", \n" +" \"VideoTurboTwoPass\": 0, \n" +" \"VideoTwoPass\": 0, \n" +" \"x264Option\": \"\", \n" +" \"x264UseAdvancedOptions\": 0\n" +" }, \n" +" {\n" +" \"AudioAllowAACPass\": 1, \n" +" \"AudioAllowAC3Pass\": 1, \n" +" \"AudioAllowDTSHDPass\": 1, \n" +" \"AudioAllowDTSPass\": 1, \n" +" \"AudioAllowMP3Pass\": 1, \n" +" \"AudioEncoderFallback\": \"ac3\", \n" +" \"AudioList\": [\n" +" {\n" +" \"AudioBitrate\": \"128\", \n" +" \"AudioEncoder\": \"aac\", \n" +" \"AudioMixdown\": \"dpl2\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }\n" +" ], \n" +" \"ChapterMarkers\": 0, \n" +" \"Default\": 0, \n" +" \"FileFormat\": \"mp4\", \n" +" \"Folder\": false, \n" +" \"Mp4HttpOptimize\": 0, \n" +" \"Mp4iPodCompatible\": 0, \n" +" \"PictureAutoCrop\": 1, \n" +" \"PictureBottomCrop\": 0, \n" +" \"PictureDeblock\": 0, \n" +" \"PictureDecomb\": 0, \n" +" \"PictureDecombCustom\": \"\", \n" +" \"PictureDecombDeinterlace\": 1, \n" +" \"PictureDeinterlace\": 0, \n" +" \"PictureDeinterlaceCustom\": \"\", \n" +" \"PictureDenoiseCustom\": \"\", \n" +" \"PictureDenoiseFilter\": \"off\", \n" +" \"PictureDetelecine\": 0, \n" +" \"PictureDetelecineCustom\": \"\", \n" +" \"PictureHeight\": 720, \n" +" \"PictureKeepRatio\": 1, \n" +" \"PictureLeftCrop\": 0, \n" +" \"PictureModulus\": 2, \n" +" \"PicturePAR\": \"off\", \n" +" \"PictureRightCrop\": 0, \n" +" \"PictureTopCrop\": 0, \n" +" \"PictureWidth\": 1280, \n" +" \"PresetDescription\": \"HandBrake's preset for Windows Phone 8 devices\", \n" +" \"PresetName\": \"Windows Phone 8\", \n" +" \"Type\": 0, \n" +" \"UsesPictureFilters\": 1, \n" +" \"UsesPictureSettings\": 1, \n" +" \"VideoAvgBitrate\": \"2500\", \n" +" \"VideoEncoder\": \"x264\", \n" +" \"VideoFramerate\": \"30\", \n" +" \"VideoFramerateMode\": \"pfr\", \n" +" \"VideoGrayScale\": 0, \n" +" \"VideoLevel\": \"3.1\", \n" +" \"VideoOptionExtra\": \"\", \n" +" \"VideoPreset\": \"medium\", \n" +" \"VideoProfile\": \"main\", \n" +" \"VideoQualitySlider\": 22.0, \n" +" \"VideoQualityType\": 2, \n" +" \"VideoTune\": \"\", \n" +" \"VideoTurboTwoPass\": 0, \n" +" \"VideoTwoPass\": 0, \n" +" \"x264Option\": \"\", \n" +" \"x264UseAdvancedOptions\": 0\n" +" }\n" +" ], \n" +" \"Default\": 0, \n" +" \"Folder\": true, \n" +" \"PresetName\": \"Devices\", \n" +" \"Type\": 0\n" +" }, \n" +" {\n" +" \"ChildrenArray\": [\n" +" {\n" +" \"AudioAllowAACPass\": 1, \n" +" \"AudioAllowAC3Pass\": 1, \n" +" \"AudioAllowDTSHDPass\": 1, \n" +" \"AudioAllowDTSPass\": 1, \n" +" \"AudioAllowMP3Pass\": 1, \n" +" \"AudioEncoderFallback\": \"ac3\", \n" +" \"AudioList\": [\n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"aac\", \n" +" \"AudioMixdown\": \"dpl2\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }\n" +" ], \n" +" \"ChapterMarkers\": 1, \n" +" \"Default\": 1, \n" +" \"FileFormat\": \"mp4\", \n" +" \"Folder\": false, \n" +" \"Mp4HttpOptimize\": 0, \n" +" \"Mp4iPodCompatible\": 0, \n" +" \"PictureAutoCrop\": 1, \n" +" \"PictureBottomCrop\": 0, \n" +" \"PictureDeblock\": 0, \n" +" \"PictureDecomb\": 0, \n" +" \"PictureDecombCustom\": \"\", \n" +" \"PictureDecombDeinterlace\": 1, \n" +" \"PictureDeinterlace\": 0, \n" +" \"PictureDeinterlaceCustom\": \"\", \n" +" \"PictureDenoiseCustom\": \"\", \n" +" \"PictureDenoiseFilter\": \"off\", \n" +" \"PictureDetelecine\": 0, \n" +" \"PictureDetelecineCustom\": \"\", \n" +" \"PictureHeight\": 0, \n" +" \"PictureKeepRatio\": 0, \n" +" \"PictureLeftCrop\": 0, \n" +" \"PictureModulus\": 2, \n" +" \"PicturePAR\": \"loose\", \n" +" \"PictureRightCrop\": 0, \n" +" \"PictureTopCrop\": 0, \n" +" \"PictureWidth\": 0, \n" +" \"PresetDescription\": \"HandBrake's normal, default settings.\", \n" +" \"PresetName\": \"Normal\", \n" +" \"Type\": 0, \n" +" \"UsesPictureFilters\": 1, \n" +" \"UsesPictureSettings\": 1, \n" +" \"VideoAvgBitrate\": \"2500\", \n" +" \"VideoEncoder\": \"x264\", \n" +" \"VideoFramerate\": \"auto\", \n" +" \"VideoFramerateMode\": \"vfr\", \n" +" \"VideoGrayScale\": 0, \n" +" \"VideoLevel\": \"4.0\", \n" +" \"VideoOptionExtra\": \"\", \n" +" \"VideoPreset\": \"veryfast\", \n" +" \"VideoProfile\": \"main\", \n" +" \"VideoQualitySlider\": 20.0, \n" +" \"VideoQualityType\": 2, \n" +" \"VideoTune\": \"\", \n" +" \"VideoTurboTwoPass\": 0, \n" +" \"VideoTwoPass\": 0, \n" +" \"x264Option\": \"\", \n" +" \"x264UseAdvancedOptions\": 0\n" +" }, \n" +" {\n" +" \"AudioAllowAACPass\": 1, \n" +" \"AudioAllowAC3Pass\": 1, \n" +" \"AudioAllowDTSHDPass\": 1, \n" +" \"AudioAllowDTSPass\": 1, \n" +" \"AudioAllowMP3Pass\": 1, \n" +" \"AudioEncoderFallback\": \"ac3\", \n" +" \"AudioList\": [\n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"aac\", \n" +" \"AudioMixdown\": \"dpl2\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }, \n" +" {\n" +" \"AudioBitrate\": \"160\", \n" +" \"AudioEncoder\": \"copy:ac3\", \n" +" \"AudioMixdown\": \"none\", \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrack\": 1, \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0\n" +" }\n" +" ], \n" +" \"ChapterMarkers\": 1, \n" +" \"Default\": 0, \n" +" \"FileFormat\": \"mp4\", \n" +" \"Folder\": false, \n" +" \"Mp4HttpOptimize\": 0, \n" +" \"Mp4iPodCompatible\": 0, \n" +" \"PictureAutoCrop\": 1, \n" +" \"PictureBottomCrop\": 0, \n" +" \"PictureDeblock\": 0, \n" +" \"PictureDecomb\": 2, \n" +" \"PictureDecombCustom\": \"\", \n" +" \"PictureDecombDeinterlace\": 1, \n" +" \"PictureDeinterlace\": 0, \n" +" \"PictureDeinterlaceCustom\": \"\", \n" +" \"PictureDenoiseCustom\": \"\", \n" +" \"PictureDenoiseFilter\": \"off\", \n" +" \"PictureDetelecine\": 0, \n" +" \"PictureDetelecineCustom\": \"\", \n" +" \"PictureHeight\": 0, \n" +" \"PictureKeepRatio\": 0, \n" +" \"PictureLeftCrop\": 0, \n" +" \"PictureModulus\": 2, \n" +" \"PicturePAR\": \"loose\", \n" +" \"PictureRightCrop\": 0, \n" +" \"PictureTopCrop\": 0, \n" +" \"PictureWidth\": 0, \n" +" \"PresetDescription\": \"HandBrake's general-purpose preset for High Profile H.264 video.\", \n" +" \"PresetName\": \"High Profile\", \n" +" \"Type\": 0, \n" +" \"UsesPictureFilters\": 1, \n" +" \"UsesPictureSettings\": 1, \n" +" \"VideoAvgBitrate\": \"2500\", \n" +" \"VideoEncoder\": \"x264\", \n" +" \"VideoFramerate\": \"auto\", \n" +" \"VideoFramerateMode\": \"vfr\", \n" +" \"VideoGrayScale\": 0, \n" +" \"VideoLevel\": \"4.1\", \n" +" \"VideoOptionExtra\": \"\", \n" +" \"VideoPreset\": \"medium\", \n" +" \"VideoProfile\": \"high\", \n" +" \"VideoQualitySlider\": 20.0, \n" +" \"VideoQualityType\": 2, \n" +" \"VideoTune\": \"\", \n" +" \"VideoTurboTwoPass\": 0, \n" +" \"VideoTwoPass\": 0, \n" +" \"x264Option\": \"\", \n" +" \"x264UseAdvancedOptions\": 0\n" +" }\n" +" ], \n" +" \"Default\": 0, \n" +" \"Folder\": true, \n" +" \"PresetName\": \"Regular\", \n" +" \"Type\": 0\n" +" }\n" +" ], \n" +" \"PresetTemplate\": {\n" +" \"Preset\": {\n" +" \"AudioAllowAACPass\": false, \n" +" \"AudioAllowAC3Pass\": true, \n" +" \"AudioAllowDTSHDPass\": false, \n" +" \"AudioAllowDTSPass\": false, \n" +" \"AudioAllowEAC3Pass\": false, \n" +" \"AudioAllowFLACPass\": false, \n" +" \"AudioAllowMP3Pass\": false, \n" +" \"AudioAllowTRUEHDPass\": false, \n" +" \"AudioCopyMask\": [], \n" +" \"AudioEncoderFallback\": \"ac3\", \n" +" \"AudioLanguageList\": [\n" +" \"und\"\n" +" ], \n" +" \"AudioList\": [\n" +" {\n" +" \"AudioBitrate\": \"192\", \n" +" \"AudioCompressionLevel\": -1.0, \n" +" \"AudioDitherMethod\": \"auto\", \n" +" \"AudioEncoder\": \"copy:ac3\", \n" +" \"AudioMixdown\": \"dpl2\", \n" +" \"AudioNormalizeMixLevel\": false, \n" +" \"AudioSamplerate\": \"auto\", \n" +" \"AudioTrackDRCSlider\": 0.0, \n" +" \"AudioTrackGainSlider\": 0.0, \n" +" \"AudioTrackQuality\": -1.0, \n" +" \"AudioTrackQualityEnable\": false\n" +" }\n" +" ], \n" +" \"AudioSecondaryEncoderMode\": true, \n" +" \"AudioTrackSelectionBehavior\": \"first\", \n" +" \"ChapterMarkers\": true, \n" +" \"Default\": false, \n" +" \"FileFormat\": \"mp4\", \n" +" \"Folder\": false, \n" +" \"Mp4HttpOptimize\": false, \n" +" \"Mp4iPodCompatible\": false, \n" +" \"PictureAutoCrop\": true, \n" +" \"PictureBottomCrop\": 0, \n" +" \"PictureDARWidth\": 0, \n" +" \"PictureDeblock\": 0, \n" +" \"PictureDecomb\": \"off\", \n" +" \"PictureDecombCustom\": \"\", \n" +" \"PictureDecombDeinterlace\": true, \n" +" \"PictureDeinterlace\": \"off\", \n" +" \"PictureDeinterlaceCustom\": \"\", \n" +" \"PictureDenoiseCustom\": \"\", \n" +" \"PictureDenoiseFilter\": \"off\", \n" +" \"PictureDenoisePreset\": \"medium\", \n" +" \"PictureDenoiseTune\": \"none\", \n" +" \"PictureDetelecine\": \"off\", \n" +" \"PictureDetelecineCustom\": \"\", \n" +" \"PictureForceHeight\": 0, \n" +" \"PictureForceWidth\": 0, \n" +" \"PictureHeight\": 0, \n" +" \"PictureItuPAR\": false, \n" +" \"PictureKeepRatio\": true, \n" +" \"PictureLeftCrop\": 0, \n" +" \"PictureLooseCrop\": false, \n" +" \"PictureModulus\": 2, \n" +" \"PicturePAR\": \"loose\", \n" +" \"PicturePARHeight\": 720, \n" +" \"PicturePARWidth\": 853, \n" +" \"PictureRightCrop\": 0, \n" +" \"PictureRotate\": 0, \n" +" \"PictureTopCrop\": 0, \n" +" \"PictureWidth\": 0, \n" +" \"PresetDescription\": \"\", \n" +" \"PresetName\": \"Name Missing\", \n" +" \"SubtitleAddCC\": false, \n" +" \"SubtitleAddForeignAudioSearch\": false, \n" +" \"SubtitleAddForeignAudioSubtitle\": false, \n" +" \"SubtitleBurnBDSub\": false, \n" +" \"SubtitleBurnBehavior\": \"none\", \n" +" \"SubtitleBurnDVDSub\": false, \n" +" \"SubtitleLanguageList\": [], \n" +" \"SubtitleTrackSelectionBehavior\": \"none\", \n" +" \"Type\": 1, \n" +" \"UsesPictureFilters\": true, \n" +" \"UsesPictureSettings\": 2, \n" +" \"VideoAvgBitrate\": 1800, \n" +" \"VideoColorMatrixCode\": 0, \n" +" \"VideoEncoder\": \"x264\", \n" +" \"VideoFramerate\": \"auto\", \n" +" \"VideoFramerateMode\": \"vfr\", \n" +" \"VideoGrayScale\": false, \n" +" \"VideoHWDecode\": false, \n" +" \"VideoLevel\": \"auto\", \n" +" \"VideoOptionExtra\": \"\", \n" +" \"VideoPreset\": \"medium\", \n" +" \"VideoProfile\": \"auto\", \n" +" \"VideoQSVAsyncDepth\": 4, \n" +" \"VideoQSVDecode\": false, \n" +" \"VideoQualitySlider\": 20.0, \n" +" \"VideoQualityType\": 2, \n" +" \"VideoScaler\": \"swscale\", \n" +" \"VideoTune\": \"none\", \n" +" \"VideoTurboTwoPass\": false, \n" +" \"VideoTwoPass\": false, \n" +" \"x264Option\": \"\", \n" +" \"x264UseAdvancedOptions\": false\n" +" }, \n" +" \"VersionMajor\": 10, \n" +" \"VersionMicro\": 0, \n" +" \"VersionMinor\": 0\n" +" }\n" +"}\n"; diff --git a/libhb/common.c b/libhb/common.c index 7bb3f2a56..8a7b72522 100644 --- a/libhb/common.c +++ b/libhb/common.c @@ -262,6 +262,9 @@ hb_encoder_internal_t hb_audio_encoders[] = { { "Vorbis (vorbis)", "libvorbis", NULL, HB_ACODEC_VORBIS, HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_VORBIS, }, { { "FLAC (ffmpeg)", "ffflac", NULL, HB_ACODEC_FFFLAC, HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_FLAC, }, { { "FLAC (24-bit)", "ffflac24", NULL, HB_ACODEC_FFFLAC24, HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_FLAC, }, + // generic names + { { "AAC", "aac", NULL, 0, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_AAC, }, + { { "HE-AAC", "haac", NULL, 0, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_AAC_HE, }, // actual encoders { { "AAC (CoreAudio)", "ca_aac", "AAC (Apple AudioToolbox)", HB_ACODEC_CA_AAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AAC, }, { { "HE-AAC (CoreAudio)", "ca_haac", "HE-AAC (Apple AudioToolbox)", HB_ACODEC_CA_HAAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AAC_HE, }, @@ -1793,7 +1796,7 @@ int hb_video_encoder_get_default(int muxer) } fail: - return 0; + return HB_VCODEC_INVALID; } hb_encoder_t * hb_video_encoder_get_from_codec(int codec) @@ -1826,7 +1829,7 @@ int hb_video_encoder_get_from_name(const char *name) } fail: - return 0; + return HB_VCODEC_INVALID; } const char* hb_video_encoder_get_name(int encoder) @@ -1937,10 +1940,10 @@ int hb_audio_encoder_get_fallback_for_passthru(int passthru) } // passthru tracks are often the second audio from the same source track - // if we don't have an encoder matching the passthru codec, return 0 + // if we don't have an encoder matching the passthru codec, return INVALID // dropping the track, as well as ensuring that there is at least one // audio track in the output is then up to the UIs - return 0; + return HB_ACODEC_INVALID; } int hb_audio_encoder_get_default(int muxer) @@ -1974,7 +1977,7 @@ int hb_audio_encoder_get_default(int muxer) } fail: - return 0; + return HB_ACODEC_INVALID; } hb_encoder_t* hb_audio_encoder_get_from_codec(int codec) @@ -1988,7 +1991,7 @@ hb_encoder_t* hb_audio_encoder_get_from_codec(int codec) } } - return 0; + return NULL; } int hb_audio_encoder_get_from_name(const char *name) @@ -2007,7 +2010,7 @@ int hb_audio_encoder_get_from_name(const char *name) } fail: - return 0; + return HB_ACODEC_INVALID; } const char* hb_audio_encoder_get_name(int encoder) @@ -2302,7 +2305,7 @@ int hb_container_get_from_name(const char *name) } fail: - return 0; + return HB_MUX_INVALID; } int hb_container_get_from_extension(const char *extension) @@ -2320,7 +2323,7 @@ int hb_container_get_from_extension(const char *extension) } fail: - return 0; + return HB_MUX_INVALID; } const char* hb_container_get_name(int format) diff --git a/libhb/hb.c b/libhb/hb.c index 7a7429153..f4ec2308f 100644 --- a/libhb/hb.c +++ b/libhb/hb.c @@ -1655,6 +1655,9 @@ int hb_global_init() */ hb_buffer_pool_init(); + // Initialize the builtin presets hb_dict_t + hb_presets_builtin_init(); + return result; } @@ -1667,6 +1670,8 @@ void hb_global_close() DIR * dir; struct dirent * entry; + hb_presets_free(); + /* Find and remove temp folder */ memset( dirname, 0, 1024 ); hb_get_temporary_directory( dirname ); diff --git a/libhb/hb.h b/libhb/hb.h index 38418ac3b..215455cda 100644 --- a/libhb/hb.h +++ b/libhb/hb.h @@ -14,11 +14,12 @@ extern "C" { #endif -#include "project.h" #include "common.h" +#include "project.h" #include "compat.h" #include "hb_dict.h" #include "hb_json.h" +#include "preset.h" #include "param.h" /* hb_init() diff --git a/libhb/hb_dict.c b/libhb/hb_dict.c index 7fd12b565..866803f58 100644 --- a/libhb/hb_dict.c +++ b/libhb/hb_dict.c @@ -21,6 +21,11 @@ hb_value_type_t hb_value_type(const hb_value_t *value) return type; } +int hb_value_is_number(const hb_value_t *value) +{ + return json_is_number(value); +} + hb_value_t * hb_value_dup(const hb_value_t *value) { if (value == NULL) return NULL; diff --git a/libhb/hb_dict.h b/libhb/hb_dict.h index 52898e413..2a4f71cfe 100644 --- a/libhb/hb_dict.h +++ b/libhb/hb_dict.h @@ -84,6 +84,7 @@ size_t hb_value_array_len(const hb_value_array_t *array); /* hb_value_t */ int hb_value_type(const hb_value_t *value); +int hb_value_is_number(const hb_value_t *value); hb_value_t * hb_value_dup(const hb_value_t *value); void hb_value_incref(hb_value_t *value); void hb_value_decref(hb_value_t *value); diff --git a/libhb/lang.c b/libhb/lang.c index f40520a23..34466da34 100644 --- a/libhb/lang.c +++ b/libhb/lang.c @@ -202,10 +202,16 @@ static const iso639_lang_t languages[] = static const int lang_count = sizeof(languages) / sizeof(languages[0]); -iso639_lang_t * lang_lookup( const char * str ) +const iso639_lang_t * lang_lookup( const char * str ) { iso639_lang_t * lang; + // We use "Any" as a synonym for undefined + if (!strcasecmp("any", str)) + { + return &languages[0]; + } + for (lang = (iso639_lang_t*) languages; lang->eng_name; lang++) { if ((lang->iso639_1 != NULL && !strcasecmp(lang->iso639_1, str)) || diff --git a/libhb/lang.h b/libhb/lang.h index 21aab70af..7e7b82bd5 100644 --- a/libhb/lang.h +++ b/libhb/lang.h @@ -24,7 +24,7 @@ typedef struct iso639_lang_t extern "C" { #endif /* find language, match any of names in lang struct */ -iso639_lang_t * lang_lookup( const char * str ); +const iso639_lang_t * lang_lookup( const char * str ); /* find language associated with ISO-639-1 language code */ iso639_lang_t * lang_for_code( int code ); diff --git a/libhb/libhb_presets.list b/libhb/libhb_presets.list new file mode 100644 index 000000000..3742b1d40 --- /dev/null +++ b/libhb/libhb_presets.list @@ -0,0 +1,9 @@ +<resources> + <section name="PresetTemplate"> + <integer name="VersionMajor" value="10" /> + <integer name="VersionMinor" value="0" /> + <integer name="VersionMicro" value="0" /> + <json name="Preset" file="preset_template.json" /> + </section> + <json name="PresetBuiltin" file="preset_builtin.json" /> +</resources> diff --git a/libhb/module.defs b/libhb/module.defs index 3d7d55ee3..f98930dd5 100644 --- a/libhb/module.defs +++ b/libhb/module.defs @@ -47,6 +47,12 @@ endif LIBHB.GCC.D += __LIBHB__ USE_PTHREAD LIBHB.GCC.I += $(LIBHB.build/) $(CONTRIB.build/)include +ifneq (,$(filter $(BUILD.system),darwin cygwin mingw)) +LIBHB.GCC.I += $(CONTRIB.build/)include/libxml2 +else +LIBHB.GCC.I += /usr/include/libxml2 +endif + ifeq ($(BUILD.system),cygwin) LIBHB.GCC.D += SYS_CYGWIN else ifeq ($(BUILD.system),darwin) diff --git a/libhb/plist.c b/libhb/plist.c new file mode 100644 index 000000000..f61014537 --- /dev/null +++ b/libhb/plist.c @@ -0,0 +1,678 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <inttypes.h> +#include "libxml/parser.h" + +#include "common.h" +#include "hb_dict.h" +#include "plist.h" + +#define BUF_SZ (128*1024) + +static char *preamble = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + "<plist version=\"1.0\">\n"; +static char *postfix = + "</plist>\n"; + +typedef struct queue_item_s queue_item_t; +struct queue_item_s +{ + void *value; + queue_item_t *next; +}; + +typedef struct +{ + queue_item_t *head; +} queue_t; + +queue_t * queue_new() +{ + return calloc(1, sizeof(queue_t)); +} + +void queue_free(queue_t **_q) +{ + queue_t *q = *_q; + if (q == NULL) + return; + + queue_item_t *n, *i = q->head; + while (i != NULL) + { + n = i->next; + free(i); + i = n; + } + free(q); + *_q = NULL; +} + +void queue_push_head(queue_t *q, void *v) +{ + queue_item_t *i = calloc(1, sizeof(queue_item_t)); + i->value = v; + i->next = q->head; + q->head = i; +} + +void * queue_peek_head(queue_t *q) +{ + if (q->head != NULL) + return q->head->value; + return NULL; +} + +void * queue_pop_head(queue_t *q) +{ + void *result; + queue_item_t *i; + + if (q->head == NULL) + return NULL; + + i = q->head; + result = i->value; + q->head = i->next; + free(i); + + return result; +} + +int queue_is_empty(queue_t *q) +{ + return q->head == NULL; +} + +char * markup_escape_text(const char *str) +{ + int ii, jj; + int len = strlen(str); + int step = 40; + int alloc = len + step; + char *markup = malloc(alloc); + + for (ii = 0, jj = 0; ii < len; ii++) + { + if (jj > alloc - 8) + { + alloc += step; + char *tmp = realloc(markup, alloc); + if (tmp == NULL) + { + markup[jj] = 0; + return markup; + } + markup = tmp; + } + switch (str[ii]) + { + case '<': + markup[jj++] = '&'; + markup[jj++] = 'l'; + markup[jj++] = 't'; + markup[jj++] = ';'; + break; + case '>': + markup[jj++] = '&'; + markup[jj++] = 'g'; + markup[jj++] = 't'; + markup[jj++] = ';'; + break; + case '\'': + markup[jj++] = '&'; + markup[jj++] = 'a'; + markup[jj++] = 'p'; + markup[jj++] = 'o'; + markup[jj++] = 's'; + markup[jj++] = ';'; + break; + case '"': + markup[jj++] = '&'; + markup[jj++] = 'q'; + markup[jj++] = 'u'; + markup[jj++] = 'o'; + markup[jj++] = 't'; + markup[jj++] = ';'; + break; + case '&': + markup[jj++] = '&'; + markup[jj++] = 'a'; + markup[jj++] = 'm'; + markup[jj++] = 'p'; + markup[jj++] = ';'; + break; + default: + markup[jj++] = str[ii]; + break; + } + markup[jj] = 0; + } + return markup; +} + +enum +{ + P_NONE = 0, + P_PLIST, + P_KEY, + P_ARRAY, + P_DICT, + P_INTEGER, + P_REAL, + P_STRING, + P_DATE, + P_TRUE, + P_FALSE, + P_DATA, +}; + +typedef struct +{ + char *tag; + int id; +} tag_map_t; + +static tag_map_t tag_map[] = +{ + {"plist", P_PLIST}, + {"key", P_KEY}, + {"array", P_ARRAY}, + {"dict", P_DICT}, + {"integer", P_INTEGER}, + {"real", P_REAL}, + {"string", P_STRING}, + {"date", P_DATE}, + {"true", P_TRUE}, + {"false", P_FALSE}, + {"data", P_DATA}, +}; +#define TAG_MAP_SZ (sizeof(tag_map)/sizeof(tag_map_t)) + +typedef struct +{ + char *key; + char *value; + hb_value_t *plist; + queue_t *stack; + queue_t *tag_stack; + int closed_top; +} parse_data_t; + +static void +start_element( + void *ud, + const xmlChar *xname, + const xmlChar **attr_names) +{ + char *name = (char*)xname; + parse_data_t *pd = (parse_data_t*)ud; + union + { + int id; + void * pid; + } id; + int ii; + + // Check to see if the first element found has been closed + // If so, ignore any junk following it. + if (pd->closed_top) + return; + + for (ii = 0; ii < TAG_MAP_SZ; ii++) + { + if (strcmp(name, tag_map[ii].tag) == 0) + { + id.id = tag_map[ii].id; + break; + } + } + if (ii == TAG_MAP_SZ) + { + hb_error("Unrecognized start tag (%s)", name); + return; + } + if (pd->value) + { + free(pd->value); + pd->value = NULL; + } + queue_push_head(pd->tag_stack, id.pid); + hb_value_type_t gtype = 0; + hb_value_t *gval = NULL; + hb_value_t *current = queue_peek_head(pd->stack); + switch (id.id) + { + case P_PLIST: + { // Ignore + } break; + case P_KEY: + { + if (pd->key) free(pd->key); + pd->key = NULL; + } break; + case P_DICT: + { + gval = hb_dict_init(); + queue_push_head(pd->stack, gval); + } break; + case P_ARRAY: + { + gval = hb_value_array_init(); + queue_push_head(pd->stack, gval); + } break; + case P_INTEGER: + { + } break; + case P_REAL: + { + } break; + case P_STRING: + { + } break; + case P_DATE: + { + } break; + case P_TRUE: + { + } break; + case P_FALSE: + { + } break; + case P_DATA: + { + } break; + } + // Add the element to the current container + if (gval) + { // There's an element to add + if (current == NULL) + { + pd->plist = gval; + return; + } + gtype = hb_value_type(current); + if (gtype == HB_VALUE_TYPE_ARRAY) + { + hb_value_array_append(current, gval); + } + else if (gtype == HB_VALUE_TYPE_DICT) + { + if (pd->key == NULL) + { + hb_error("No key for dictionary item"); + hb_value_free(&gval); + } + else + { + hb_dict_set(current, pd->key, gval); + } + } + else + { + hb_error("Invalid container type. This shouldn't happen"); + } + } +} + +static void +end_element( + void *ud, + const xmlChar *xname) +{ + char *name = (char*)xname; + parse_data_t *pd = (parse_data_t*)ud; + int id; + union + { + int id; + void * pid; + } start_id; + int ii; + + // Check to see if the first element found has been closed + // If so, ignore any junk following it. + if (pd->closed_top) + return; + + for (ii = 0; ii < TAG_MAP_SZ; ii++) + { + if (strcmp(name, tag_map[ii].tag) == 0) + { + id = tag_map[ii].id; + break; + } + } + if (ii == TAG_MAP_SZ) + { + hb_error("Unrecognized start tag (%s)", name); + return; + } + start_id.pid = queue_pop_head(pd->tag_stack); + if (start_id.id != id) + hb_error("start tag != end tag: (%s %d) %d", name, id, id); + + hb_value_t *gval = NULL; + hb_value_t *current = queue_peek_head(pd->stack); + hb_value_type_t gtype = 0; + const char *value; + if (pd->value != NULL) + value = pd->value; + else + value = ""; + switch (id) + { + case P_PLIST: + { // Ignore + } break; + case P_KEY: + { + if (pd->key) free(pd->key); + pd->key = strdup(value); + return; + } break; + case P_DICT: + { + queue_pop_head(pd->stack); + } break; + case P_ARRAY: + { + queue_pop_head(pd->stack); + } break; + case P_INTEGER: + { + uint64_t val = strtoll(value, NULL, 0); + gval = hb_value_int(val); + } break; + case P_REAL: + { + double val = strtod(value, NULL); + gval = hb_value_double(val); + } break; + case P_STRING: + { + gval = hb_value_string(value); + } break; + case P_TRUE: + { + gval = hb_value_bool(1); + } break; + case P_FALSE: + { + gval = hb_value_bool(0); + } break; + default: + { + hb_error("Unhandled plist type %d", id); + } break; + } + if (gval) + { + // Get the top of the data structure stack and if it's an array + // or dict, add the current element + if (current == NULL) + { + pd->plist = gval; + pd->closed_top = 1; + return; + } + gtype = hb_value_type(current); + if (gtype == HB_VALUE_TYPE_ARRAY) + { + hb_value_array_append(current, gval); + } + else if (gtype == HB_VALUE_TYPE_DICT) + { + if (pd->key == NULL) + { + hb_error("No key for dictionary item"); + hb_value_free(&gval); + } + else + { + hb_dict_set(current, pd->key, gval); + } + } + else + { + hb_error("Invalid container type. This shouldn't happen"); + } + } + if (queue_is_empty(pd->stack)) + pd->closed_top = 1; +} + +static void +text_data( + void *ud, + const xmlChar *xtext, + int len) +{ + char *text = (char*)xtext; + parse_data_t *pd = (parse_data_t*)ud; + if (pd->value) free(pd->value); + pd->value = malloc(len + 1); + strncpy(pd->value, text, len); + pd->value[len] = 0; +} + +static void +parse_warning(void *ud, const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + hb_valog(0, "Plist parse warning: ", msg, args); + va_end(args); +} + +static void +parse_error(void *ud, const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + hb_valog(0, "Plist parse error: ", msg, args); + va_end(args); +} + +hb_value_t* +hb_plist_parse(const char *buf, size_t len) +{ + xmlSAXHandler parser; + parse_data_t pd; + + pd.stack = queue_new(); + pd.tag_stack = queue_new(); + pd.key = NULL; + pd.value = NULL; + pd.plist = NULL; + pd.closed_top = 0; + + memset(&parser, 0, sizeof(parser)); + parser.initialized = XML_SAX2_MAGIC; + parser.startElement = start_element; + parser.endElement = end_element; + parser.characters = text_data; + parser.warning = parse_warning; + parser.error = parse_error; + int result = xmlSAXUserParseMemory(&parser, &pd, buf, len); + if (result != 0) + { + hb_error("Plist parse failed"); + return NULL; + } + xmlCleanupParser(); + + if (pd.key) free(pd.key); + if (pd.value) free(pd.value); + queue_free(&pd.stack); + queue_free(&pd.tag_stack); + + return pd.plist; +} + +hb_value_t* +hb_plist_parse_file(const char *filename) +{ + char *buffer; + size_t size; + hb_value_t *gval; + FILE *fd; + + fd = fopen(filename, "r"); + if (fd == NULL) + { + // File doesn't exist + return NULL; + } + fseek(fd, 0, SEEK_END); + size = ftell(fd); + fseek(fd, 0, SEEK_SET); + buffer = malloc(size+1); + size = fread(buffer, 1, size, fd); + buffer[size] = 0; + gval = hb_plist_parse(buffer, size); + free(buffer); + fclose(fd); + return gval; +} + +static void +indent_fprintf(FILE *file, int indent, const char *fmt, ...) +{ + va_list ap; + + for (; indent; indent--) + putc('\t', file); + va_start(ap, fmt); + vfprintf(file, fmt, ap); + va_end(ap); +} + +static void +gval_write(FILE *file, hb_value_t *gval) +{ + static int indent = 0; + int ii; + hb_value_type_t gtype; + + if (gval == NULL) return; + gtype = hb_value_type(gval); + if (gtype == HB_VALUE_TYPE_ARRAY) + { + hb_value_t *val; + int count; + + indent_fprintf(file, indent, "<array>\n"); + indent++; + count = hb_value_array_len(gval); + for (ii = 0; ii < count; ii++) + { + val = hb_value_array_get(gval, ii); + gval_write(file, val); + } + indent--; + indent_fprintf(file, indent, "</array>\n"); + } + else if (gtype == HB_VALUE_TYPE_DICT) + { + const char *key; + hb_value_t *val; + hb_dict_iter_t iter; + + indent_fprintf(file, indent, "<dict>\n"); + indent++; + + for (iter = hb_dict_iter_init(gval); + iter != HB_DICT_ITER_DONE; + iter = hb_dict_iter_next(gval, iter)) + { + key = hb_dict_iter_key(iter); + val = hb_dict_iter_value(iter); + indent_fprintf(file, indent, "<key>%s</key>\n", key); + gval_write(file, val); + } + + indent--; + indent_fprintf(file, indent, "</dict>\n"); + } + else if (gtype == HB_VALUE_TYPE_BOOL) + { + char *tag; + if (hb_value_get_bool(gval)) + { + tag = "true"; + } + else + { + tag = "false"; + } + indent_fprintf(file, indent, "<%s />\n", tag); + } + else if (gtype == HB_VALUE_TYPE_DOUBLE) + { + double val = hb_value_get_double(gval); + indent_fprintf(file, indent, "<real>%.17g</real>\n", val); + } + else if (gtype == HB_VALUE_TYPE_INT) + { + int64_t val = hb_value_get_int(gval); + indent_fprintf(file, indent, "<integer>%"PRId64"</integer>\n", val); + } + else if (gtype == HB_VALUE_TYPE_STRING) + { + const char *str = hb_value_get_string(gval); + char *esc = markup_escape_text(str); + indent_fprintf(file, indent, "<string>%s</string>\n", esc); + free(esc); + } + else + { + // Try to make anything thats unrecognized into a string + hb_error("Unhandled data type %d", gtype); + } +} + +void +hb_plist_write(FILE *file, hb_value_t *gval) +{ + fprintf(file, "%s", preamble); + gval_write(file, gval); + fprintf(file, "%s", postfix); +} + +void +hb_plist_write_file(const char *filename, hb_value_t *gval) +{ + FILE *file; + + file = fopen(filename, "w"); + if (file == NULL) + return; + + hb_plist_write(file, gval); + fclose(file); +} + + +#if defined(PL_TEST) +int +main(int argc, char *argv[]) +{ + hb_value_t *gval; + + file = fopen(argv[1], "r"); + gval = hb_plist_parse_file(file); + if (argc > 2) + hb_plist_write_file(argv[2], gval); + else + hb_plist_write(stdout, gval); + if (file) fclose (file); + return 0; +} +#endif diff --git a/libhb/plist.h b/libhb/plist.h new file mode 100644 index 000000000..901aeb4b0 --- /dev/null +++ b/libhb/plist.h @@ -0,0 +1,13 @@ +#if !defined(_HB_PLIST_H_) +#define _HB_PLIST_H_ + +#include <stdio.h> +#include "hb_dict.h" + +hb_value_t * hb_plist_parse(const char *buf, size_t len); +hb_value_t * hb_plist_parse_file(const char *filename); +void hb_plist_write(FILE *file, hb_value_t *val); +void hb_plist_write_file(const char *filename, hb_value_t *val); + +#endif // _HB_PLIST_H_ + diff --git a/libhb/preset.c b/libhb/preset.c new file mode 100644 index 000000000..3fef7c686 --- /dev/null +++ b/libhb/preset.c @@ -0,0 +1,2198 @@ +/* hb_preset.c + + Copyright (c) 2003-2015 HandBrake Team + This file is part of the HandBrake source code + Homepage: <http://handbrake.fr/>. + It may be used under the terms of the GNU General Public License v2. + For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html + */ + +#include "builtin_presets.h" +#include "hb.h" +#include "hb_dict.h" +#include "plist.h" + +#if defined(SYS_LINUX) +#define HB_PRESET_PLIST_FILE "ghb/presets" +#define HB_PRESET_JSON_FILE "ghb/presets.json" +#elif defined(SYS_MINGW) +#define HB_PRESET_PLIST_FILE "HandBrake\\user_presets.xml" +#define HB_PRESET_JSON_FILE "HandBrake\\user_presets.json" +#elif defined(SYS_DARWIN) +#define HB_PRESET_PLIST_FILE "HandBrake/UserPresets.plist" +#endif + +int hb_preset_version_major; +int hb_preset_version_minor; +int hb_preset_version_micro; + +static hb_value_t *hb_preset_template = NULL; +static hb_value_t *hb_presets = NULL; +static hb_value_t *hb_presets_custom = NULL; +static hb_value_t *hb_presets_builtin = NULL; + +static int get_job_mux(hb_dict_t *job_dict) +{ + int mux; + + hb_dict_t *dest_dict = hb_dict_get(job_dict, "Destination"); + hb_value_t *mux_value = hb_dict_get(dest_dict, "Mux"); + if (hb_value_type(mux_value) == HB_VALUE_TYPE_STRING) + { + mux = hb_container_get_from_name(hb_value_get_string(mux_value)); + if (mux == 0) + mux = hb_container_get_from_extension( + hb_value_get_string(mux_value)); + } + else + { + mux = hb_value_get_int(mux_value); + } + hb_container_t *container = hb_container_get_from_format(mux); + if (container == NULL) + { + char *str = hb_value_get_string_xform(mux_value); + hb_error("Invalid container (%s)", str); + free(str); + return HB_MUX_INVALID; + } + return mux; +} + +static int get_audio_copy_mask(hb_dict_t * preset) +{ + int mask = HB_ACODEC_PASS_FLAG; + + hb_value_array_t *copy_mask_array = hb_dict_get(preset, "AudioCopyMask"); + if (copy_mask_array != NULL) + { + int count = hb_value_array_len(copy_mask_array); + int ii; + for (ii = 0; ii < count; ii++) + { + int codec; + hb_value_t *value; + value = hb_value_array_get(copy_mask_array, ii); + if (hb_value_type(value) == HB_VALUE_TYPE_STRING) + { + char *tmp = NULL; + const char * s = hb_value_get_string(value); + // Only codecs that start with 'copy:' can be copied + if (strncmp(s, "copy:", 5)) + { + s = tmp = hb_strdup_printf("copy:%s", s); + } + codec = hb_audio_encoder_get_from_name(s); + if (codec == 0) + { + hb_error("Invalid audio codec in autopassthru copy mask (%s)", s); + hb_error("Codec name is invalid or can not be copied"); + free(tmp); + return HB_ACODEC_INVALID; + } + free(tmp); + } + else + { + codec = hb_value_get_int(value); + } + mask |= codec; + } + } + else + { + mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowMP3Pass")) * + HB_ACODEC_MP3; + mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowAACPass")) * + HB_ACODEC_FFAAC; + mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowAC3Pass")) * + HB_ACODEC_AC3; + mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowDTSPass")) * + HB_ACODEC_DCA; + mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowDTSHDPass")) * + HB_ACODEC_DCA_HD; + mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowEAC3Pass")) * + HB_ACODEC_FFEAC3; + mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowFLACPass")) * + HB_ACODEC_FFFLAC; + mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowTRUEHDPass")) * + HB_ACODEC_FFTRUEHD; + } + return mask; +} + +static hb_dict_t * source_audio_track_used(hb_dict_t *track_dict, int track) +{ + // Kind of hacky, but keys must be strings + char key[8]; + snprintf(key, sizeof(key), "%d", track); + + hb_dict_t *used = hb_dict_get(track_dict, key); + if (used == NULL) + { + used = hb_dict_init(); + hb_dict_set(track_dict, key, used); + } + return used; +} + +// Find a source audio track matching given language +static int find_audio_track(const hb_title_t *title, + const char *lang, int start) +{ + hb_audio_config_t * audio; + int ii, count; + + count = hb_list_count(title->list_audio); + for (ii = start; ii < count; ii++) + { + audio = hb_list_audio_config_item(title->list_audio, ii); + // Ignore secondary audio types + if ((audio->lang.type == HB_AUDIO_TYPE_NONE || + audio->lang.type == HB_AUDIO_TYPE_NORMAL) && + (!strcmp(lang, audio->lang.iso639_2) || !strcmp(lang, "und"))) + { + return ii; + } + } + return -1; +} + +static int validate_audio_encoders(hb_dict_t *preset) +{ + hb_value_array_t * encoder_list = hb_dict_get(preset, "AudioList"); + int count = hb_value_array_len(encoder_list); + int ii; + for (ii = 0; ii < count; ii++) + { + hb_value_t *audio_dict = hb_value_array_get(encoder_list, ii); + hb_value_t *value; + int codec, mix, sr; + value = hb_dict_get(audio_dict, "AudioEncoder"); + if (hb_value_type(value) == HB_VALUE_TYPE_STRING) + { + codec = hb_audio_encoder_get_from_name(hb_value_get_string(value)); + } + else + { + codec = hb_value_get_int(value); + } + if (hb_audio_encoder_get_from_codec(codec) == NULL) + { + char *str = hb_value_get_string_xform(value); + hb_error("Invalid audio encoder (%s)", str); + free(str); + return -1; + } + + value = hb_dict_get(audio_dict, "AudioMixdown"); + if (hb_value_type(value) == HB_VALUE_TYPE_STRING) + { + mix = hb_audio_encoder_get_from_name(hb_value_get_string(value)); + } + else + { + mix = hb_value_get_int(value); + } + if (hb_mixdown_get_from_mixdown(mix) == NULL) + { + char *str = hb_value_get_string_xform(value); + hb_error("Invalid audio mixdown (%s)", str); + free(str); + return -1; + } + + value = hb_dict_get(audio_dict, "AudioSamplerate"); + if (hb_value_type(value) == HB_VALUE_TYPE_STRING) + { + const char *str = hb_value_get_string(value); + if (!strcasecmp(str, "source") || + !strcasecmp(str, "auto") || + !strcasecmp(str, "same as source")) + { + sr = 0; + } + else + { + sr = hb_audio_samplerate_get_from_name(str); + } + } + else + { + sr = hb_value_get_int(value); + } + if (sr != 0 && hb_audio_samplerate_get_name(sr) == NULL) + { + char *str = hb_value_get_string_xform(value); + hb_error("Invalid audio samplerate (%s)", str); + free(str); + return -1; + } + } + return 0; +} + +static int sanitize_audio_codec(int in_codec, int out_codec, + int copy_mask, int fallback, int mux) +{ + int codec = out_codec; + if (out_codec == HB_ACODEC_AUTO_PASS) + { + codec = hb_autopassthru_get_encoder(in_codec, copy_mask, fallback, mux); + } + else if ((out_codec & HB_ACODEC_PASS_FLAG) && + !(in_codec & out_codec & HB_ACODEC_PASS_MASK)) + { + codec = hb_audio_encoder_get_fallback_for_passthru(out_codec); + if (codec == 0) + codec = fallback; + } + + // Check that encoder is valid for mux + const hb_encoder_t *encoder = NULL; + while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) + { + if (encoder->codec == codec && + !(encoder->muxers & mux)) + { + codec = hb_audio_encoder_get_default(mux); + break; + } + } + if (codec == 0) + codec = hb_audio_encoder_get_default(mux); + return codec; +} + +static void add_audio_for_lang(hb_value_array_t *list, hb_dict_t *preset, + hb_title_t *title, int mux, int copy_mask, + int fallback, const char *lang, + int behavior, int mode, hb_dict_t *track_dict) +{ + hb_value_array_t * encoder_list = hb_dict_get(preset, "AudioList"); + int count = hb_value_array_len(encoder_list); + int track = find_audio_track(title, lang, 0); + int current_mode = 0; + while (track >= 0) + { + char key[8]; + snprintf(key, sizeof(key), "%d", track); + + count = current_mode ? 1 : count; + int ii; + for (ii = 0; ii < count; ii++) + { + // Check if this source track has already been added using these + // same encoder settings. If so, continue to next track. + hb_dict_t *used = source_audio_track_used(track_dict, ii); + if (hb_value_get_bool(hb_dict_get(used, key))) + continue; + hb_dict_set(used, key, hb_value_bool(1)); + + // Create new audio output track settings + hb_dict_t *audio_dict = hb_dict_init(); + hb_value_t *acodec_value; + hb_dict_t *encoder_dict = hb_value_array_get(encoder_list, ii); + int out_codec; + + acodec_value = hb_dict_get(encoder_dict, "AudioEncoder"); + if (hb_value_type(acodec_value) == HB_VALUE_TYPE_STRING) + { + out_codec = hb_audio_encoder_get_from_name( + hb_value_get_string(acodec_value)); + } + else + { + out_codec = hb_value_get_int(acodec_value); + } + // Save the encoder value before sanitizing. This value is + // useful to the frontends. + hb_dict_set(audio_dict, "PresetEncoder", hb_value_int(out_codec)); + + hb_audio_config_t *aconfig; + aconfig = hb_list_audio_config_item(title->list_audio, track); + out_codec = sanitize_audio_codec(aconfig->in.codec, out_codec, + copy_mask, fallback, mux); + hb_dict_set(audio_dict, "Track", hb_value_int(track)); + hb_dict_set(audio_dict, "Encoder", hb_value_int(out_codec)); + if (hb_dict_get(encoder_dict, "AudioTrackName") != NULL) + { + hb_dict_set(audio_dict, "Name", hb_value_dup( + hb_dict_get(encoder_dict, "AudioTrackName"))); + } + if (!(out_codec & HB_ACODEC_PASS_FLAG)) + { + if (hb_dict_get(encoder_dict, "AudioTrackGainSlider") != NULL) + { + hb_dict_set(audio_dict, "Gain", hb_value_dup( + hb_dict_get(encoder_dict, "AudioTrackGainSlider"))); + } + if (hb_dict_get(encoder_dict, "AudioTrackDRCSlider") != NULL) + { + hb_dict_set(audio_dict, "DRC", hb_value_dup( + hb_dict_get(encoder_dict, "AudioTrackDRCSlider"))); + } + if (hb_dict_get(encoder_dict, "AudioMixdown") != NULL) + { + hb_dict_set(audio_dict, "Mixdown", hb_value_dup( + hb_dict_get(encoder_dict, "AudioMixdown"))); + } + if (hb_dict_get(encoder_dict, "AudioNormalizeMixLevel") != NULL) + { + hb_dict_set(audio_dict, "NormalizeMixLevel", hb_value_dup( + hb_dict_get(encoder_dict, "AudioNormalizeMixLevel"))); + } + if (hb_dict_get(encoder_dict, "AudioDitherMethod") != NULL) + { + hb_dict_set(audio_dict, "DitherMethod", hb_value_dup( + hb_dict_get(encoder_dict, "AudioDitherMethod"))); + } + if (hb_dict_get(encoder_dict, "AudioSamplerate") != NULL) + { + hb_dict_set(audio_dict, "Samplerate", hb_value_dup( + hb_dict_get(encoder_dict, "AudioSamplerate"))); + } + if (hb_dict_get(encoder_dict, "AudioCompressionLevel") != NULL) + { + hb_dict_set(audio_dict, "CompressionLevel", hb_value_dup( + hb_dict_get(encoder_dict, "AudioCompressionLevel"))); + } + if (hb_value_get_bool(hb_dict_get(encoder_dict, + "AudioTrackQualityEnable"))) + { + hb_dict_set(audio_dict, "Quality", hb_value_xform( + hb_dict_get(encoder_dict, "AudioTrackQuality"), + HB_VALUE_TYPE_DOUBLE)); + } + else + { + hb_dict_set(audio_dict, "Bitrate", hb_value_xform( + hb_dict_get(encoder_dict, "AudioBitrate"), + HB_VALUE_TYPE_INT)); + } + } + hb_value_array_append(list, audio_dict); + } + if (behavior == 2) + track = find_audio_track(title, lang, track + 1); + else + break; + } +} + +// This function assumes that Mux has already been initialized in +// the job_dict +int hb_preset_job_add_audio(hb_handle_t *h, int title_index, + hb_dict_t *preset, hb_dict_t *job_dict) +{ + hb_title_t *title = hb_find_title_by_index(h, title_index); + if (title == NULL) + { + // Can't create audio track list without knowing source audio tracks + hb_error("Invalid title index (%d)", title_index); + return -1; + } + if (hb_list_count(title->list_audio) <= 0) + { + // Source has no audio + return 0; + } + + int mux = get_job_mux(job_dict); + if (mux == HB_MUX_INVALID) + { + return -1; + } + + hb_dict_t *audio_dict = hb_dict_get(job_dict, "Audio"); + int copy_mask = get_audio_copy_mask(preset); + if (copy_mask == HB_ACODEC_INVALID) + { + return -1; + } + int fallback = 0; + hb_dict_set(audio_dict, "CopyMask", hb_value_int(copy_mask)); + hb_value_t *fallback_value = hb_dict_get(preset, "AudioEncoderFallback"); + if (fallback_value != NULL) + { + hb_dict_set(audio_dict, "FallbackEncoder", + hb_value_dup(fallback_value)); + if (hb_value_type(fallback_value) == HB_VALUE_TYPE_STRING) + { + const char * s = hb_value_get_string(fallback_value); + fallback = hb_audio_encoder_get_from_name(s); + if (fallback == 0) + { + hb_error("Invalid fallback audio codec (%s)", s); + return -1; + } + } + else + { + fallback = hb_value_get_int(fallback_value); + } + } + if (validate_audio_encoders(preset) < 0) + return -1; + + hb_value_array_t *list = hb_dict_get(audio_dict, "AudioList"); + if (list == NULL) + { + list = hb_value_array_init(); + hb_dict_set(audio_dict, "AudioList", list); + } + + int behavior = 1; // default first + const char *s; + s = hb_value_get_string(hb_dict_get(preset, "AudioTrackSelectionBehavior")); + if (s != NULL) + { + if (!strcasecmp(s, "none")) + return 0; + else if (!strcasecmp(s, "all")) + behavior = 2; + } + + // Create hash that is used to track which tracks have been already added + // We do not want to add the same track with the same settings twice + hb_dict_t *track_dict = hb_dict_init(); + + // Add tracks for all languages in the language list + int mode; + hb_value_array_t *lang_list = hb_dict_get(preset, "AudioLanguageList"); + mode = hb_value_get_bool(hb_dict_get(preset, "AudioSecondaryEncoderMode")); + int count = hb_value_array_len(lang_list); + int ii; + for (ii = 0; ii < count; ii++) + { + const char *lang; + lang = hb_value_get_string(hb_value_array_get(lang_list, ii)); + add_audio_for_lang(list, preset, title, mux, copy_mask, fallback, + lang, behavior, mode, track_dict); + } + // If no audios found, try "und" language option + if (hb_value_array_len(list) <= 0) + { + add_audio_for_lang(list, preset, title, mux, copy_mask, fallback, + "und", behavior, mode, track_dict); + } + hb_dict_free(&track_dict); + return 0; +} + +// Find a source audio track matching given language +static int find_subtitle_track(const hb_title_t *title, + const char *lang, int start) +{ + hb_subtitle_t * subtitle; + int ii, count; + + count = hb_list_count(title->list_subtitle); + for (ii = start; ii < count; ii++) + { + subtitle = hb_list_item(title->list_subtitle, ii); + if (!strcmp(lang, subtitle->iso639_2) || !strcmp(lang, "und")) + { + return ii; + } + } + return -1; +} + +static void add_subtitle(hb_value_array_t *list, int track, + int make_default, int force, int burn) +{ + hb_dict_t *subtitle_dict = hb_dict_init(); + hb_dict_set(subtitle_dict, "Track", hb_value_int(track)); + hb_dict_set(subtitle_dict, "Default", hb_value_bool(make_default)); + hb_dict_set(subtitle_dict, "Forced", hb_value_bool(force)); + hb_dict_set(subtitle_dict, "Burn", hb_value_bool(burn)); + hb_value_array_append(list, subtitle_dict); +} + +typedef struct subtitle_behavior_s +{ + int one; + int burn_foreign; + int make_default; + int burn_first; + int burn_dvd; + int burn_bd; + int one_burned; + uint8_t *used; +} subtitle_behavior_t; + +static void add_subtitle_for_lang(hb_value_array_t *list, hb_title_t *title, + int mux, const char *lang, + subtitle_behavior_t *behavior) +{ + int t; + t = find_subtitle_track(title, lang, 0); + for (t = find_subtitle_track(title, lang, 0); + t >= 0; + t = behavior->one ? -1 : find_subtitle_track(title, lang, t + 1)) + { + if (behavior->used[t]) + { + if (behavior->one) + break; + continue; + } + int burn, make_default; + hb_subtitle_t *subtitle; + subtitle = hb_list_item(title->list_subtitle, t); + burn = !behavior->one_burned && + ((subtitle->source == VOBSUB && behavior->burn_dvd) || + (subtitle->source == PGSSUB && behavior->burn_bd) || + !hb_subtitle_can_pass(subtitle->source, mux) || + behavior->burn_first || behavior->burn_foreign); + make_default = !burn && behavior->make_default; + behavior->burn_first &= !burn; + behavior->one_burned |= burn; + behavior->used[t] = 1; + add_subtitle(list, t, make_default, 0 /*!force*/, burn); + } +} + +// This function assumes that the AudioList and Mux have already been +// initialized in the job_dict +int hb_preset_job_add_subtitles(hb_handle_t *h, int title_index, + hb_dict_t *preset, hb_dict_t *job_dict) +{ + hb_title_t *title = hb_find_title_by_index(h, title_index); + if (title == NULL) + { + // Can't create subtitle track list without knowing source + hb_error("Invalid title index (%d)", title_index); + return -1; + } + + int mux = get_job_mux(job_dict); + if (mux == HB_MUX_INVALID) + { + return -1; + } + + // Get the language of the first audio output track + // Needed for subtitle track selection + hb_dict_t *audio_dict = hb_dict_get(job_dict, "Audio"); + hb_value_array_t *audio_list = hb_dict_get(audio_dict, "AudioList"); + const char *first_audio_lang = NULL; + if (hb_value_array_len(audio_list) > 0) + { + int track; + hb_value_t *audio = hb_value_array_get(audio_list, 0); + track = hb_value_get_int(hb_dict_get(audio, "Track")); + if (hb_list_count(title->list_audio) > track) + { + hb_audio_config_t *aconfig; + aconfig = hb_list_audio_config_item(title->list_audio, track); + if (aconfig != NULL) + first_audio_lang = aconfig->lang.iso639_2; + } + } + + int source_subtitle_count = hb_list_count(title->list_subtitle); + if (source_subtitle_count == 0) + return 0; + + hb_dict_t *subtitle_dict = hb_dict_get(job_dict, "Subtitle"); + hb_value_array_t *list = hb_dict_get(subtitle_dict, "SubtitleList"); + if (list == NULL) + { + list = hb_value_array_init(); + hb_dict_set(subtitle_dict, "SubtitleList", list); + } + + int track_behavior = 0; // default no subtitles + int burn_behavior = 0; + int burn_foreign; + + struct subtitle_behavior_s behavior; + behavior.one = 0; + behavior.burn_foreign = 0; + behavior.make_default = 0; + behavior.burn_first = 0; + behavior.burn_dvd = 0; + behavior.burn_bd = 0; + behavior.one_burned = 0; + // Create array that is used to track which tracks have been already added + // We do not want to add the same track with the same settings twice + behavior.used = calloc(source_subtitle_count, sizeof(*behavior.used)); + + const char *s; + s = hb_value_get_string(hb_dict_get(preset, + "SubtitleTrackSelectionBehavior")); + if (s != NULL) + { + if (!strcasecmp(s, "first")) + track_behavior = 1; + else if (!strcasecmp(s, "all")) + track_behavior = 2; + } + + s = hb_value_get_string(hb_dict_get(preset, "SubtitleBurnBehavior")); + if (s != NULL) + { + if (!strcasecmp(s, "foreign")) + burn_behavior = 1; + else if (!strcasecmp(s, "first")) + burn_behavior = 2; + else if (!strcasecmp(s, "foreign_first")) + burn_behavior = 3; + } + + behavior.burn_dvd = hb_value_get_int(hb_dict_get(preset, + "SubtitleBurnDVDSub")); + behavior.burn_bd = hb_value_get_int(hb_dict_get(preset, + "SubtitleBurnBDSub")); + + burn_foreign = burn_behavior == 1 || burn_behavior == 3; + behavior.burn_first = burn_behavior == 2 || burn_behavior == 3; + + int foreign_audio_search, foreign_first_audio; + foreign_audio_search = hb_value_get_bool(hb_dict_get(preset, + "SubtitleAddForeignAudioSearch")); + foreign_first_audio = hb_value_get_bool(hb_dict_get(preset, + "SubtitleAddForeignAudioSubtitle")); + + + // Add tracks for all languages in the language list + hb_value_array_t *lang_list = hb_dict_get(preset, "SubtitleLanguageList"); + int count = hb_value_array_len(lang_list); + const char *pref_lang = "und"; + if (count > 0) + { + pref_lang = hb_value_get_string(hb_value_array_get(lang_list, 0)); + } + if (!strcmp(pref_lang, "und")) + { + foreign_audio_search = foreign_first_audio = 0; + } + + int track; + if (first_audio_lang != NULL && + foreign_first_audio && strncmp(first_audio_lang, pref_lang, 4)) + { + // First audio lang does not match the preferred subittle lang. + // Preset says to add pref lang subtitle. + // Foreign audio search is not necessary since entire audio track + // is foreign. + foreign_audio_search = 0; + behavior.one = 1; + behavior.burn_foreign = burn_foreign; + behavior.make_default = 1; + add_subtitle_for_lang(list, title, mux, pref_lang, &behavior); + } + + hb_dict_t *search_dict = hb_dict_get(subtitle_dict, "Search"); + if (search_dict == NULL) + { + search_dict = hb_dict_init(); + hb_dict_set(subtitle_dict, "Search", search_dict); + } + if (first_audio_lang != NULL && + foreign_audio_search && !strncmp(first_audio_lang, pref_lang, 4)) + { + // First audio lang matches the preferred subittle lang. + // Preset says to add search for foreign audio subtitles. + int burn = burn_foreign || behavior.burn_first; + // If not burning, make this the default track. + hb_dict_set(search_dict, "Enable", hb_value_bool(1)); + hb_dict_set(search_dict, "Default", hb_value_bool(!burn)); + hb_dict_set(search_dict, "Forced", hb_value_bool(1)); + hb_dict_set(search_dict, "Burn", hb_value_bool(burn)); + } + else + { + hb_dict_set(search_dict, "Enable", hb_value_bool(0)); + } + + if (track_behavior > 0) + { + int ii; + behavior.one = track_behavior == 1; + behavior.burn_foreign = 0; + behavior.make_default = 0; + for (ii = 0; ii < count; ii++) + { + const char *lang; + lang = hb_value_get_string(hb_value_array_get(lang_list, ii)); + add_subtitle_for_lang(list, title, mux, lang, &behavior); + } + if (count <= 0) + { + add_subtitle_for_lang(list, title, mux, "und", &behavior); + } + } + + if (hb_value_get_bool(hb_dict_get(preset, "SubtitleAddCC"))) + { + // Add Closed Caption track + for (track = 0; track < source_subtitle_count; track++) + { + if (behavior.used[track]) + { + continue; + } + hb_subtitle_t *subtitle = hb_list_item(title->list_subtitle, track); + if (subtitle->source == CC608SUB || subtitle->source == CC708SUB) + { + int burn; + burn = !behavior.one_burned && + (!hb_subtitle_can_pass(subtitle->source, mux) || + behavior.burn_first); + behavior.used[track] = 1; + behavior.one_burned |= burn; + add_subtitle(list, track, 0 /*default*/, 0 /*!force*/, burn); + break; + } + } + } + free(behavior.used); + + return 0; +} + +static int get_video_framerate(hb_value_t *rate_value) +{ + int rate = 0; + if (hb_value_type(rate_value) != HB_VALUE_TYPE_STRING) + { + double d; + d = hb_value_get_double(rate_value); + if (d != 0 && d <= 600) + { + // Assume the value is an actual framerate and compute + // 27Mhz based denominator + rate = (int)(27000000 / d); + } + else + { + // Assume the value is a 27Mhz based denominator + rate = (int)d; + } + } + else + { + const char *rate_name = hb_value_get_string(rate_value); + if (strcasecmp(rate_name, "source") && + strcasecmp(rate_name, "auto") && + strcasecmp(rate_name, "same as source")) + { + rate = hb_video_framerate_get_from_name(rate_name); + if (rate < 0) + { + // No matching rate found. Error out. + rate = -1; + } + } + } + return rate; +} + +/** + * Initialize an hb_job_t and return a hb_dict_t representation of the job. + * This dict will have key/value pairs compatible with json jobs. + * @param h - Pointer to hb_handle_t instance that contains the + * specified title_index + * @param title_index - Index of hb_title_t to use for job initialization. + * Index comes from title->index or "Index" key + * in json representation of a title. + * @param preset - Preset to initialize job with + */ +hb_dict_t* hb_preset_job_init(hb_handle_t *h, int title_index, hb_dict_t *preset) +{ + hb_title_t *title = hb_find_title_by_index(h, title_index); + if (title == NULL) + { + hb_error("Invalid title index (%d)", title_index); + return NULL; + } + + hb_job_t *job = hb_job_init(title); + hb_dict_t *job_dict = hb_job_to_dict(job); + hb_job_close(&job); + + // Now apply preset settings to the job dict + + hb_value_t *mux_value = hb_dict_get(preset, "FileFormat"); + int mux; + if (hb_value_type(mux_value) == HB_VALUE_TYPE_STRING) + { + mux = hb_container_get_from_name(hb_value_get_string(mux_value)); + if (mux == 0) + mux = hb_container_get_from_extension( + hb_value_get_string(mux_value)); + } + else + { + mux = hb_value_get_int(mux_value); + } + hb_container_t *container = hb_container_get_from_format(mux); + if (container == NULL) + { + char *str = hb_value_get_string_xform(mux_value); + hb_error("Invalid container (%s)", str); + free(str); + goto fail; + } + + hb_value_t *vcodec_value = hb_dict_get(preset, "VideoEncoder"); + int vcodec; + if (hb_value_type(vcodec_value) == HB_VALUE_TYPE_STRING) + { + vcodec = hb_video_encoder_get_from_name( + hb_value_get_string(vcodec_value)); + } + else + { + vcodec = hb_value_get_int(vcodec_value); + } + hb_encoder_t *encoder = hb_video_encoder_get_from_codec(vcodec); + if (encoder == NULL) + { + char *str = hb_value_get_string_xform(vcodec_value); + hb_error("Invalid video encoder (%s)", str); + free(str); + goto fail; + } + if (!(encoder->muxers & mux)) + { + hb_error("Incompatible video encoder (%s) for muxer (%s)", + hb_video_encoder_get_name(vcodec), + hb_container_get_name(mux)); + goto fail; + } + + int chapters; + chapters = hb_value_get_bool(hb_dict_get(preset, "ChapterMarkers")); + if (hb_list_count(title->list_chapter) <= 1) + chapters = 0; + + // Set "Destination" settings in job + hb_dict_t *dest_dict = hb_dict_get(job_dict, "Destination"); + hb_dict_set(dest_dict, "ChapterMarkers", hb_value_bool(chapters)); + hb_dict_set(dest_dict, "Mux", hb_value_dup(mux_value)); + if (mux == HB_MUX_MASK_MP4) + { + hb_dict_t *mp4_dict = hb_dict_init(); + hb_dict_set(mp4_dict, "Mp4Optimize", + hb_value_xform(hb_dict_get(preset, "Mp4HttpOptimize"), + HB_VALUE_TYPE_BOOL)); + if (vcodec == HB_VCODEC_X264) + { + hb_dict_set(mp4_dict, "IpodAtom", + hb_value_xform(hb_dict_get(preset, "Mp4iPodCompatible"), + HB_VALUE_TYPE_BOOL)); + } + hb_dict_set(dest_dict, "Mp4Options", mp4_dict); + } + dest_dict = NULL; + + // Calculate default job geometry settings + hb_geometry_t srcGeo, resultGeo; + hb_geometry_settings_t geo; + int keep_aspect; + + srcGeo = title->geometry; + if (!hb_value_get_bool(hb_dict_get(preset, "PictureAutoCrop"))) + { + geo.crop[0] = hb_value_get_int(hb_dict_get(preset, "PictureTopCrop")); + geo.crop[1] = hb_value_get_int(hb_dict_get(preset, "PictureBottomCrop")); + geo.crop[2] = hb_value_get_int(hb_dict_get(preset, "PictureLeftCrop")); + geo.crop[3] = hb_value_get_int(hb_dict_get(preset, "PictureRightCrop")); + } + else + { + memcpy(geo.crop, title->crop, sizeof(geo.crop)); + } + geo.modulus = hb_value_get_int(hb_dict_get(preset, "PictureModulus")); + if (geo.modulus < 2) + geo.modulus = 2; + if (hb_value_get_bool(hb_dict_get(preset, "PictureLooseCrop"))) + { + // Crop a few extra pixels to avoid scaling to fit Modulus + int extra1, extra2, crop_width, crop_height, width, height; + + crop_width = srcGeo.width - geo.crop[2] - geo.crop[3]; + crop_height = srcGeo.height - geo.crop[0] - geo.crop[1]; + width = MULTIPLE_MOD_DOWN(crop_width, geo.modulus); + height = MULTIPLE_MOD_DOWN(crop_height, geo.modulus); + + extra1 = EVEN((crop_height - height) / 2); + extra2 = crop_height - height - extra1; + geo.crop[0] += extra1; + geo.crop[1] += extra2; + extra1 = EVEN((crop_width - width) / 2); + extra2 = crop_width - width - extra1; + geo.crop[2] += extra1; + geo.crop[3] += extra2; + } + hb_value_t *ana_mode_value = hb_dict_get(preset, "PicturePAR"); + if (hb_value_type(ana_mode_value) == HB_VALUE_TYPE_STRING) + { + const char *s = hb_value_get_string(ana_mode_value); + if (!strcasecmp(s, "none")) + geo.mode = 0; + else if (!strcasecmp(s, "strict")) + geo.mode = 1; + else if (!strcasecmp(s, "custom")) + geo.mode = 3; + else // default loose + geo.mode = 2; + } + else + { + geo.mode = hb_value_get_int(hb_dict_get(preset, "PicturePAR")); + } + keep_aspect = hb_value_get_bool(hb_dict_get(preset, "PictureKeepRatio")); + if (geo.mode == HB_ANAMORPHIC_STRICT || geo.mode == HB_ANAMORPHIC_LOOSE) + keep_aspect = 1; + geo.keep = keep_aspect * HB_KEEP_DISPLAY_ASPECT; + geo.itu_par = hb_value_get_bool(hb_dict_get(preset, "PictureItuPAR")); + geo.maxWidth = hb_value_get_int(hb_dict_get(preset, "PictureWidth")); + geo.maxHeight = hb_value_get_int(hb_dict_get(preset, "PictureHeight")); + geo.geometry = title->geometry; + int width = hb_value_get_int(hb_dict_get(preset, "PictureForceWidth")); + int height = hb_value_get_int(hb_dict_get(preset, "PictureForceHeight")); + if (width > 0) + geo.geometry.width = width; + else + geo.geometry.width -= geo.crop[2] + geo.crop[3]; + if (height > 0) + geo.geometry.height = height; + else + geo.geometry.height -= geo.crop[0] + geo.crop[1]; + if (geo.mode == HB_ANAMORPHIC_CUSTOM && !keep_aspect) + { + int dar_width; + dar_width = hb_value_get_int(hb_dict_get(preset, "PictureDARWidth")); + if (dar_width > 0) + { + geo.geometry.par.num = dar_width; + geo.geometry.par.num = geo.geometry.width; + } + else + { + geo.geometry.par.num = + hb_value_get_int(hb_dict_get(preset, "PicturePARWidth")); + geo.geometry.par.num = + hb_value_get_int(hb_dict_get(preset, "PicturePARHeight")); + } + } + hb_set_anamorphic_size2(&srcGeo, &geo, &resultGeo); + hb_dict_t *par_dict = hb_dict_get(job_dict, "PAR"); + hb_dict_set(par_dict, "Num", hb_value_int(resultGeo.par.num)); + hb_dict_set(par_dict, "Den", hb_value_int(resultGeo.par.den)); + par_dict = NULL; + + // Filters + hb_dict_t *filters_dict = hb_dict_get(job_dict, "Filters"); + if (filters_dict == NULL) + { + filters_dict = hb_dict_init(); + hb_dict_set(job_dict, "Filters", filters_dict); + } + hb_dict_set(filters_dict, "Grayscale", hb_value_xform( + hb_dict_get(preset, "VideoGrayScale"), HB_VALUE_TYPE_BOOL)); + hb_value_array_t *filter_list = hb_dict_get(filters_dict, "FilterList"); + if (filter_list == NULL) + { + filter_list = hb_value_array_init(); + hb_dict_set(filters_dict, "FilterList", filter_list); + } + + hb_dict_t *filter_dict; + char *filter_str; + + // Setup scale filter + filter_str = hb_strdup_printf("%d:%d:%d:%d:%d:%d", + resultGeo.width, resultGeo.height, + geo.crop[0], geo.crop[1], + geo.crop[2], geo.crop[3]); + + filter_dict = hb_dict_init(); + hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_CROP_SCALE)); + hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str)); + free(filter_str); + hb_value_array_append(filter_list, filter_dict); + + // Detelecine filter + hb_value_t *detel_val = hb_dict_get(preset, "PictureDetelecine"); + if (detel_val != NULL) + { + const char *custom; + custom = hb_value_get_string(hb_dict_get(preset, + "PictureDetelecineCustom")); + if (hb_value_type(detel_val) == HB_VALUE_TYPE_STRING) + { + filter_str = hb_generate_filter_settings( + HB_FILTER_DETELECINE, hb_value_get_string(detel_val), custom); + } + else + { + filter_str = hb_generate_filter_settings_by_index( + HB_FILTER_DETELECINE, hb_value_get_int(detel_val), custom); + } + if (filter_str == NULL) + { + char *s = hb_value_get_string_xform(detel_val); + hb_error("Invalid detelecine filter settings (%s)", s); + free(s); + goto fail; + } + else if (filter_str != hb_filter_off) + { + filter_dict = hb_dict_init(); + hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_DETELECINE)); + hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str)); + hb_value_array_append(filter_list, filter_dict); + free(filter_str); + } + } + + // Decomb or deinterlace filters + int decomb_or_deint; + decomb_or_deint = hb_value_get_bool(hb_dict_get(preset, + "PictureDecombDeinterlace")); + hb_value_t *decomb_val = hb_dict_get(preset, "PictureDecomb"); + if (decomb_or_deint && decomb_val != NULL) + { + const char *custom; + custom = hb_value_get_string(hb_dict_get(preset, + "PictureDecombCustom")); + if (hb_value_type(decomb_val) == HB_VALUE_TYPE_STRING) + { + filter_str = hb_generate_filter_settings( + HB_FILTER_DECOMB, hb_value_get_string(decomb_val), custom); + } + else + { + filter_str = hb_generate_filter_settings_by_index( + HB_FILTER_DECOMB, hb_value_get_int(decomb_val), custom); + } + if (filter_str == NULL) + { + char *s = hb_value_get_string_xform(decomb_val); + hb_error("Invalid decomb filter settings (%s)", s); + free(s); + goto fail; + } + else if (filter_str != hb_filter_off) + { + filter_dict = hb_dict_init(); + hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_DECOMB)); + hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str)); + hb_value_array_append(filter_list, filter_dict); + free(filter_str); + } + } + + hb_value_t *deint_val = hb_dict_get(preset, "PictureDeinterlace"); + if (!decomb_or_deint && deint_val != NULL) + { + const char *custom; + custom = hb_value_get_string(hb_dict_get(preset, + "PictureDeinterlaceCustom")); + if (hb_value_type(deint_val) == HB_VALUE_TYPE_STRING) + { + filter_str = hb_generate_filter_settings( + HB_FILTER_DEINTERLACE, hb_value_get_string(deint_val), custom); + } + else + { + filter_str = hb_generate_filter_settings_by_index( + HB_FILTER_DEINTERLACE, hb_value_get_int(deint_val), custom); + } + if (filter_str == NULL) + { + char *s = hb_value_get_string_xform(deint_val); + hb_error("Invalid deinterlace filter settings (%s)", s); + free(s); + goto fail; + } + else if (filter_str != hb_filter_off) + { + filter_dict = hb_dict_init(); + hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_DECOMB)); + hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str)); + hb_value_array_append(filter_list, filter_dict); + free(filter_str); + } + } + + // Denoise filter + int denoise; + hb_value_t *denoise_value = hb_dict_get(preset, "PictureDenoiseFilter"); + denoise = hb_value_type(denoise_value) == HB_VALUE_TYPE_STRING ? ( + !strcasecmp(hb_value_get_string(denoise_value), "off") ? 0 : + !strcasecmp(hb_value_get_string(denoise_value), "nlmeans") ? 1 : 2) : + hb_value_get_int(denoise_value); + + if (denoise != 0) + { + int filter_id = denoise == 1 ? HB_FILTER_NLMEANS : HB_FILTER_HQDN3D; + const char *denoise_preset, *denoise_tune; + denoise_preset = hb_value_get_string( + hb_dict_get(preset, "PictureDenoisePreset")); + if (denoise_preset != NULL) + { + if (strcasecmp(denoise_preset, "custom")) + denoise_tune = hb_value_get_string( + hb_dict_get(preset, "PictureDenoiseTune")); + else + denoise_tune = hb_value_get_string( + hb_dict_get(preset, "PictureDeinterlaceCustom")); + + filter_str = hb_generate_filter_settings( + filter_id, denoise_preset, denoise_tune); + if (filter_str == NULL) + { + hb_error("Invalid denoise filter settings (%s%s%s)", + denoise_preset, + denoise_tune ? "," : "", + denoise_tune ? denoise_tune : ""); + goto fail; + } + else if (filter_str != hb_filter_off) + { + filter_dict = hb_dict_init(); + hb_dict_set(filter_dict, "ID", hb_value_int(filter_id)); + hb_dict_set(filter_dict, "Settings", + hb_value_string(filter_str)); + hb_value_array_append(filter_list, filter_dict); + free(filter_str); + } + } + } + + // Deblock filter + char *deblock = hb_value_get_string_xform( + hb_dict_get(preset, "PictureDeblock")); + if (deblock != NULL) + { + filter_str = hb_generate_filter_settings(HB_FILTER_DEBLOCK, + deblock, NULL); + if (filter_str == NULL) + { + hb_error("Invalid deblock filter settings (%s)", deblock); + goto fail; + } + else if (filter_str != hb_filter_off) + { + filter_dict = hb_dict_init(); + hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_DEBLOCK)); + hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str)); + hb_value_array_append(filter_list, filter_dict); + free(filter_str); + } + } + free(deblock); + + // Rotate filter + char *rotate = hb_value_get_string_xform( + hb_dict_get(preset, "PictureRotate")); + if (rotate != NULL) + { + filter_str = hb_generate_filter_settings(HB_FILTER_ROTATE, + rotate, NULL); + if (filter_str == NULL) + { + hb_error("Invalid rotate filter settings (%s)", rotate); + goto fail; + } + else if (filter_str != hb_filter_off) + { + filter_dict = hb_dict_init(); + hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_ROTATE)); + hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str)); + hb_value_array_append(filter_list, filter_dict); + free(filter_str); + } + } + free(rotate); + + hb_value_t *fr_value = hb_dict_get(preset, "VideoFramerate"); + int vrate_den = get_video_framerate(fr_value); + if (vrate_den < 0) + { + char *str = hb_value_get_string_xform(fr_value); + hb_error("Invalid video framerate (%s)", str); + free(str); + goto fail; + } + + int fr_mode; + hb_value_t *fr_mode_value = hb_dict_get(preset, "VideoFramerateMode"); + fr_mode = hb_value_type(fr_mode_value) == HB_VALUE_TYPE_STRING ? ( + !strcasecmp(hb_value_get_string(fr_mode_value), "cfr") ? 1 : + !strcasecmp(hb_value_get_string(fr_mode_value), "pfr") ? 2 : 0) : + hb_value_get_int(fr_mode_value); + + if (hb_value_get_bool(hb_dict_get(preset, "x264ZeroLatency"))) + fr_mode = 1; + + if (vrate_den == 0) + filter_str = hb_strdup_printf("%d", fr_mode); + else + filter_str = hb_strdup_printf("%d:%d:%d", fr_mode, 27000000, vrate_den); + + filter_dict = hb_dict_init(); + hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_VFR)); + hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str)); + hb_value_array_append(filter_list, filter_dict); + free(filter_str); + + // Video encoder settings + hb_dict_t *video_dict = hb_dict_get(job_dict, "Video"); + hb_value_t *color_value; + if ((color_value = hb_dict_get(preset, "VideoColorMatrixCode")) != NULL) + hb_dict_set(video_dict, "ColorMatrixCode", hb_value_dup(color_value)); + hb_dict_set(video_dict, "Encoder", hb_value_dup(vcodec_value)); + switch (vcodec) + { + case HB_VCODEC_X264: + { + if (hb_value_get_bool( + hb_dict_get(preset, "x264UseAdvancedOptions"))) + { + hb_dict_set(video_dict, "Options", + hb_value_dup(hb_dict_get(preset, "x264Option"))); + break; + } + } + // Falling through to next case... + + case HB_VCODEC_X265: + { + hb_value_t *value, *array; + if ((value = hb_dict_get(preset, "VideoPreset")) != NULL) + hb_dict_set(video_dict, "Preset", hb_value_dup(value)); + if ((value = hb_dict_get(preset, "VideoProfile")) != NULL) + hb_dict_set(video_dict, "Profile", hb_value_dup(value)); + if ((value = hb_dict_get(preset, "VideoLevel")) != NULL) + hb_dict_set(video_dict, "Level", hb_value_dup(value)); + if ((value = hb_dict_get(preset, "VideoOptionExtra")) != NULL) + hb_dict_set(video_dict, "Options", hb_value_dup(value)); + array = hb_value_array_init(); + if ((value = hb_dict_get(preset, "VideoTune")) != NULL) + hb_value_array_append(array, hb_value_dup(value)); + if (vcodec == HB_VCODEC_X264) + { + if (hb_value_get_bool(hb_dict_get(preset, "x264FastDecode"))) + hb_value_array_append(array, hb_value_string("fastdecode")); + if (hb_value_get_bool(hb_dict_get(preset, "x264ZeroLatency"))) + hb_value_array_append(array, hb_value_string("zerolatency")); + } + if (hb_value_array_len(array) > 0) + hb_dict_set(video_dict, "Tune", + hb_value_xform(array, HB_VALUE_TYPE_STRING)); + hb_value_decref(array); + } break; + + case HB_VCODEC_FFMPEG_MPEG2: + case HB_VCODEC_FFMPEG_MPEG4: + case HB_VCODEC_FFMPEG_VP8: + { + hb_value_t *value; + if ((value = hb_dict_get(preset, "VideoOptionExtra")) != NULL) + hb_dict_set(video_dict, "Options", hb_value_dup(value)); + } break; + + case HB_VCODEC_THEORA: + default: + { + } break; + } + int vqtype = hb_value_get_int(hb_dict_get(preset, "VideoQualityType")); + if (vqtype == 2) // Constant quality + { + hb_dict_set(video_dict, "Quality", + hb_value_xform(hb_dict_get(preset, "VideoQualitySlider"), + HB_VALUE_TYPE_DOUBLE)); + } + else if (vqtype == 1) // ABR + { + hb_dict_set(video_dict, "Bitrate", + hb_value_xform(hb_dict_get(preset, "VideoAvgBitrate"), + HB_VALUE_TYPE_INT)); + hb_dict_set(video_dict, "TwoPass", + hb_value_xform(hb_dict_get(preset, "VideoTwoPass"), + HB_VALUE_TYPE_BOOL)); + hb_dict_set(video_dict, "Turbo", + hb_value_xform(hb_dict_get(preset, "VideoTurboTwoPass"), + HB_VALUE_TYPE_BOOL)); + } + else + { + hb_value_t *value = hb_dict_get(preset, "VideoQualitySlider"); + if (value != NULL && hb_value_get_double(value) >= 0) + hb_dict_set(video_dict, "Quality", + hb_value_xform(value, HB_VALUE_TYPE_DOUBLE)); + else + { + hb_dict_set(video_dict, "Bitrate", + hb_value_xform(hb_dict_get(preset, "VideoAvgBitrate"), + HB_VALUE_TYPE_INT)); + hb_dict_set(video_dict, "TwoPass", + hb_value_xform(hb_dict_get(preset, "VideoTwoPass"), + HB_VALUE_TYPE_BOOL)); + hb_dict_set(video_dict, "Turbo", + hb_value_xform(hb_dict_get(preset, "VideoTurboTwoPass"), + HB_VALUE_TYPE_BOOL)); + } + } + hb_dict_t *qsv = hb_dict_get(video_dict, "QSV"); + if (qsv == NULL) + { + qsv = hb_dict_init(); + hb_dict_set(video_dict, "QSV", qsv); + } + hb_value_t *value; + if ((value = hb_dict_get(preset, "VideoQSVDecode")) != NULL) + { + hb_dict_set(qsv, "Decode", + hb_value_xform(value, HB_VALUE_TYPE_BOOL)); + } + if ((value = hb_dict_get(preset, "VideoQSVAsyncDepth")) != NULL) + { + hb_dict_set(qsv, "AsyncDepth", + hb_value_xform(value, HB_VALUE_TYPE_INT)); + } + + if ((value = hb_dict_get(preset, "VideoScaler")) != NULL) + { + const char *s = hb_value_get_string(value); + if (strcasecmp(s, "opencl")) + { + hb_dict_set(video_dict, "OpenCL", hb_value_bool(1)); + } + } + if ((value = hb_dict_get(preset, "VideoHWDecode")) != NULL) + { + hb_dict_set(video_dict, "HWDecode", + hb_value_xform(value, HB_VALUE_TYPE_BOOL)); + } + video_dict = NULL; + + // Audio settings + if (hb_preset_job_add_audio(h, title_index, preset, job_dict) != 0) + { + goto fail; + } + + // Subtitle settings + if (hb_preset_job_add_subtitles(h, title_index, preset, job_dict) != 0) + { + goto fail; + } + + return job_dict; + +fail: + hb_dict_free(&job_dict); + return NULL; +} + +// Clean a dictionary of unwanted keys +// Used to make sure only valid keys are in output presets +static void +dict_clean(hb_value_t *dict, hb_value_t *template) +{ + hb_value_t *tmp = hb_value_dup(dict); + hb_dict_iter_t iter; + const char *key; + hb_value_t *val; + hb_value_t *template_val; + hb_value_type_t template_type, val_type; + const char *preset_name = NULL; + + val = hb_dict_get(dict, "PresetName"); + if (val != NULL) + preset_name = hb_value_get_string(val); + + for (iter = hb_dict_iter_init(tmp); + iter != HB_DICT_ITER_DONE; + iter = hb_dict_iter_next(tmp, iter)) + { + key = hb_dict_iter_key(iter); + val = hb_dict_iter_value(iter); + val_type = hb_value_type(val); + + template_val = hb_dict_get(template, key); + template_type = hb_value_type(template_val); + if (template_val == NULL) + { + // Unknown key. These can be keys used privately by the + // frontend. So don't make noise about them. + hb_dict_remove(dict, key); + } + else if (val_type != template_type) + { + if (val_type == HB_VALUE_TYPE_DICT || + val_type == HB_VALUE_TYPE_ARRAY || + template_type == HB_VALUE_TYPE_DICT || + template_type == HB_VALUE_TYPE_ARRAY) + { + hb_error("Preset %s: Incompatible value types for key %s. " + "Dropping.", preset_name, key); + hb_dict_remove(dict, key); + } + else if (hb_value_is_number(val) && + hb_value_is_number(template_val)) + { + // Silently convert compatible numbers + hb_value_t *v; + v = hb_value_xform(val, template_type); + hb_dict_set(dict, key, v); + } + else + { + hb_value_t *v; + hb_error("Preset %s: Incorrect value type for key %s. " + "Converting.", preset_name, key); + v = hb_value_xform(val, template_type); + hb_dict_set(dict, key, v); + } + } + else if (val_type == HB_VALUE_TYPE_DICT && + template_type == HB_VALUE_TYPE_DICT) + { + val = hb_dict_get(dict, key); + dict_clean(val, template_val); + } + else if (val_type == HB_VALUE_TYPE_ARRAY && + template_type == HB_VALUE_TYPE_ARRAY && + hb_value_array_len(template_val) > 0) + { + template_val = hb_value_array_get(template_val, 0); + if (hb_value_type(template_val) == HB_VALUE_TYPE_DICT) + { + val = hb_dict_get(dict, key); + int count = hb_value_array_len(val); + int ii; + for (ii = 0; ii < count; ii++) + { + hb_value_t *array_val; + array_val = hb_value_array_get(val, ii); + if (hb_value_type(array_val) == HB_VALUE_TYPE_DICT) + { + dict_clean(array_val, template_val); + } + } + } + } + } + hb_value_free(&tmp); +} + +static void preset_clean(hb_value_t *preset, hb_value_t *template) +{ + dict_clean(preset, template); + + // Check for proper "short name" values. + // Convert as necessary. + hb_value_t *val; + const char *preset_name = NULL; + int muxer; + + val = hb_dict_get(preset, "PresetName"); + if (val != NULL) + preset_name = hb_value_get_string(val); + + val = hb_dict_get(preset, "FileFormat"); + if (val != NULL) + { + const char *s, *mux; + s = hb_value_get_string(val); + muxer = hb_container_get_from_name(s); + if (muxer == HB_MUX_INVALID) + { + const hb_container_t *c = hb_container_get_next(NULL); + muxer = c->format; + hb_error("Preset %s: Invalid container (%s)", preset_name, s); + } + mux = hb_container_get_short_name(muxer); + val = hb_value_string(mux); + hb_dict_set(preset, "FileFormat", val); + } + val = hb_dict_get(preset, "VideoEncoder"); + if (val != NULL) + { + const char *s, *enc; + int vcodec; + s = hb_value_get_string(val); + vcodec = hb_video_encoder_get_from_name(s); + if (vcodec == HB_VCODEC_INVALID) + { + vcodec = hb_video_encoder_get_default(muxer); + hb_error("Preset %s: Invalid video encoder (%s)", preset_name, s); + } + enc = hb_video_encoder_get_short_name(vcodec); + val = hb_value_string(enc); + hb_dict_set(preset, "VideoEncoder", val); + } + val = hb_dict_get(preset, "VideoFramerate"); + if (val != NULL) + { + const char *s; + s = hb_value_get_string(val); + if (strcasecmp(s, "auto")) + { + int fr = hb_video_framerate_get_from_name(s); + if (fr < 0) + { + val = hb_value_string("auto"); + hb_dict_set(preset, "VideoFramerate", val); + hb_error("Preset %s: Invalid video framerate (%s)", + preset_name, s); + } + } + } + val = hb_dict_get(preset, "AudioEncoderFallback"); + if (val != NULL) + { + const char *s, *enc; + int acodec; + s = hb_value_get_string(val); + acodec = hb_audio_encoder_get_from_name(s); + if (acodec == HB_ACODEC_INVALID) + { + acodec = hb_audio_encoder_get_default(muxer); + hb_error("Preset %s: Invalid audio fallback encoder (%s)", + preset_name, s); + } + enc = hb_audio_encoder_get_short_name(acodec); + val = hb_value_string(enc); + hb_dict_set(preset, "AudioEncoderFallback", val); + } + hb_value_t *alist = hb_dict_get(preset, "AudioList"); + int count = hb_value_array_len(alist); + int ii; + for (ii = 0; ii < count; ii++) + { + hb_value_t *adict = hb_value_array_get(alist, ii); + val = hb_dict_get(adict, "AudioEncoder"); + if (val != NULL) + { + const char *s, *enc; + int acodec; + s = hb_value_get_string(val); + acodec = hb_audio_encoder_get_from_name(s); + if (acodec == HB_ACODEC_INVALID) + { + acodec = hb_audio_encoder_get_default(muxer); + hb_error("Preset %s: Invalid audio encoder (%s)", + preset_name, s); + } + enc = hb_audio_encoder_get_short_name(acodec); + val = hb_value_string(enc); + hb_dict_set(preset, "AudioEncoder", val); + } + val = hb_dict_get(adict, "AudioSamplerate"); + if (val != NULL) + { + const char *s; + s = hb_value_get_string(val); + if (strcasecmp(s, "auto")) + { + int sr = hb_video_framerate_get_from_name(s); + if (sr < 0) + { + val = hb_value_string("auto"); + hb_dict_set(preset, "AudioSamplerate", val); + hb_error("Preset %s: Invalid audio samplerate (%s)", + preset_name, s); + } + } + } + val = hb_dict_get(adict, "AudioMixdown"); + if (val != NULL) + { + const char *s, *mix; + s = hb_value_get_string(val); + int mixdown = hb_mixdown_get_from_name(s); + if (mixdown == HB_INVALID_AMIXDOWN) + { + // work.c do_job() sanitizes NONE to default mixdown + mixdown = HB_AMIXDOWN_NONE; + hb_error("Preset %s: Invalid audio mixdown (%s)", + preset_name, s); + } + mix = hb_mixdown_get_short_name(mixdown); + val = hb_value_string(mix); + hb_dict_set(adict, "AudioMixdown", val); + } + } +} + +static void presets_clean(hb_value_t *presets, hb_value_t *template) +{ + if (hb_value_type(presets) == HB_VALUE_TYPE_ARRAY) + { + // An array of presets, clean each one + int ii, count; + count = hb_value_array_len(presets); + for (ii = 0; ii < count; ii++) + { + hb_value_t *preset = hb_value_array_get(presets, ii); + preset_clean(preset, template); + } + } + else if (hb_value_type(presets) == HB_VALUE_TYPE_DICT && + hb_dict_get(presets, "VersionMajor") != NULL) + { + // A packaged preset list + hb_value_t *list = hb_dict_get(presets, "PresetList"); + presets_clean(list, template); + } + else if (hb_value_type(presets) == HB_VALUE_TYPE_DICT && + hb_dict_get(presets, "PresetName") != NULL) + { + // An individual preset + preset_clean(presets, template); + } + else + { + hb_error("Error: invalid preset format in presets_clean()"); + } +} + +// Note that unpackage does not make any copies. +// In one increases the reference count. +static hb_value_t * preset_unpackage(hb_value_t *packaged_presets) +{ + // TODO: Verify compatible version number. + // Do any desired legacy translations. + if (hb_value_type(packaged_presets) == HB_VALUE_TYPE_ARRAY) + { + // Not packaged + hb_value_incref(packaged_presets); + return packaged_presets; + } + hb_value_t *presets = hb_dict_get(packaged_presets, "PresetList"); + hb_value_incref(presets); + return presets; +} + +static hb_value_t * preset_package(hb_value_t *presets) +{ + hb_dict_t *packaged_presets; + if (hb_dict_get(presets, "VersionMajor") == NULL) + { + // Preset is not packaged + packaged_presets = hb_dict_init(); + hb_dict_set(packaged_presets, "VersionMajor", + hb_value_int(hb_preset_version_major)); + hb_dict_set(packaged_presets, "VersionMinor", + hb_value_int(hb_preset_version_minor)); + hb_dict_set(packaged_presets, "VersionMicro", + hb_value_int(hb_preset_version_micro)); + + // TODO: What else to we want in the preset containers header? + if (hb_value_type(presets) == HB_VALUE_TYPE_DICT) + { + hb_value_array_t *tmp = hb_value_array_init(); + hb_value_array_append(tmp, presets); + presets = tmp; + } + hb_dict_t *tmp = hb_value_dup(presets); + presets_clean(tmp, hb_preset_template); + hb_dict_set(packaged_presets, "PresetList", tmp); + } + else + { + // Preset is already packaged + hb_dict_t *tmp = hb_value_dup(presets); + presets_clean(tmp, hb_preset_template); + packaged_presets = tmp; + } + return packaged_presets; +} + +void hb_presets_builtin_init(void) +{ + hb_value_t * dict = hb_value_json(hb_builtin_presets_json); + hb_value_t * template = hb_dict_get(dict, "PresetTemplate"); + hb_preset_version_major = hb_value_get_int( + hb_dict_get(template, "VersionMajor")); + hb_preset_version_minor = hb_value_get_int( + hb_dict_get(template, "VersionMinor")); + hb_preset_version_micro = hb_value_get_int( + hb_dict_get(template, "VersionMicro")); + hb_preset_template = hb_value_dup(hb_dict_get(template, "Preset")); + + hb_presets_builtin = hb_value_dup(hb_dict_get(dict, "PresetBuiltin")); + hb_value_free(&dict); + + // Make a dup, never change contents of hb_presets_builtin + hb_presets = hb_value_dup(hb_presets_builtin); + hb_presets_custom = hb_value_array_init(); + +} + +int hb_presets_gui_init(void) +{ + char path[1024]; + hb_value_t * dict = NULL; + +#if defined(HB_PRESET_JSON_FILE) + hb_get_user_config_filename(path, "%s", HB_PRESET_JSON_FILE); + dict = hb_value_read_json(path); + if (dict != NULL) + { + hb_value_t *preset = preset_unpackage(dict); + // Unpackaging does some validity checks and can fail + if (preset == NULL) + return -1; + int result = hb_presets_add(preset); + hb_value_free(&preset); + hb_value_free(&dict); + return result; + } +#endif +#if defined(HB_PRESET_PLIST_FILE) + if (dict == NULL) + { + hb_get_user_config_filename(path, "%s", HB_PRESET_PLIST_FILE); + dict = hb_plist_parse_file(path); + if (dict != NULL) + { + int result = hb_presets_add(dict); + hb_value_free(&dict); + return result; + } + } +#endif + if (dict == NULL) + { + hb_error("Failed to load GUI presets file"); +#if defined(HB_PRESET_JSON_FILE) + hb_error("Attempted: %s", HB_PRESET_JSON_FILE); +#endif +#if defined(HB_PRESET_PLIST_FILE) + hb_error("Attempted: %s", HB_PRESET_PLIST_FILE); +#endif + return -1; + } + return -1; +} + +hb_value_t * hb_presets_builtin_get(void) +{ + return hb_value_dup(hb_presets_builtin); +} + +char * hb_presets_builtin_get_json(void) +{ + char *json = hb_value_get_json(hb_presets_builtin); + return json; +} + +static hb_value_t * preset_lookup(hb_value_t *list, const char *name, + int def, int folder, int recurse) +{ + int count = hb_value_array_len(list); + int ii; + for (ii = 0; ii < count; ii++) + { + int d; + const char *n; + hb_dict_t *preset_dict = hb_value_array_get(list, ii); + n = hb_value_get_string(hb_dict_get(preset_dict, "PresetName")); + d = hb_value_get_bool(hb_dict_get(preset_dict, "Default")); + if (hb_value_get_bool(hb_dict_get(preset_dict, "Folder"))) + { + if (folder && !def && n != NULL && !strncmp(name, n, 80)) + return preset_dict; + + if (recurse) + { + hb_value_array_t *children; + children = hb_dict_get(preset_dict, "ChildrenArray"); + if (children == NULL) + continue; + preset_dict = preset_lookup(children, name, def, + folder, recurse); + if (preset_dict != NULL) + return preset_dict; + } + } + else if (!folder) + { + if (!def && n != NULL && !strncmp(n, name, 80)) + { + // preset is not a folder and we found a matching preset name + return preset_dict; + } + else if (def && d) + { + return preset_dict; + } + } + } + return NULL; +} + +// Lookup a preset in the preset list. The "name" may contain '/' +// separators to explicitely specify a preset within the preset lists +// folder structure. +// +// If 'recurse' is specified, a recursive search for the first component +// in the name will be performed. +// +// I assume that the actual preset name does not include any '/' +hb_value_t * hb_preset_get(const char *name, int recurse) +{ + if (name == NULL || name[0] == 0) + { + // bad input. + return NULL; + } + + char *tmp = strdup(name); + char *part, *next; + hb_value_t *list; + + list = hb_presets; + part = tmp; + next = strchr(name, '/'); + if (next == NULL) + { + // Only preset name was specified, so do a recursive search + hb_value_t *preset = preset_lookup(list, part, 0, 0, recurse); + free(tmp); + if (preset == NULL) + { + return NULL; + } + return hb_value_dup(preset); + } + // Found folder separator in name, do explicit path search + while (part) + { + *next = 0; + next++; + if (next[0] == 0) + { + // name ends in a folder separator '/'. Invalid input + free(tmp); + return NULL; + } + + // We have a folder part. Lookup the folder. + // First try non-recursive so that we match top level folders first + hb_dict_t *folder = preset_lookup(list, part, 0, 1, 0); + if (folder == NULL && recurse) + { + // Try a recursive search for the folder + folder = preset_lookup(list, part, 0, 1, recurse); + } + if (folder == NULL) + { + // Not found + free(tmp); + return NULL; + } + list = hb_dict_get(folder, "ChildrenArray"); + if (list == NULL) + { + // Folder found, but it has no children + free(tmp); + return NULL; + } + // Folder found, continue the search + part = next; + next = strchr(name, '/'); + if (next == NULL) + { + // We have reached the final component of the path + // which is the preset name. Do a non-recursive search. + // If the preset is not in the specified folder, will + // return NULL + hb_value_t *preset = preset_lookup(list, part, 0, 0, 0); + free(tmp); + if (preset == NULL) + { + return NULL; + } + return hb_value_dup(preset); + } + } + // This should never be reached, but some compilers might complain + // without a final return. + free(tmp); + return NULL; +} + +char * hb_preset_get_json(const char *name, int recurse) +{ + hb_value_t * preset; + char *json; + preset = hb_preset_get(name, recurse); + if (preset == NULL) + return NULL; + json = hb_value_get_json(preset); + hb_value_free(&preset); + return json; +} + +static hb_dict_t * find_first_preset(hb_value_array_t *list) +{ + int count, ii; + count = hb_value_array_len(list); + for (ii = 0; ii < count; ii++) + { + hb_dict_t *preset_dict = hb_value_array_get(list, ii); + if (hb_value_get_bool(hb_dict_get(preset_dict, "Folder"))) + { + hb_value_array_t *children; + children = hb_dict_get(preset_dict, "ChildrenArray"); + if (children == NULL) + continue; + preset_dict = find_first_preset(children); + if (preset_dict != NULL) + return preset_dict; + } + else + { + return preset_dict; + } + } + return NULL; +} + +hb_dict_t * hb_presets_get_default(void) +{ + // Look for preset with 'Default' flag set + hb_value_t *preset = preset_lookup(hb_presets, NULL, 1, 0, 1); + if (preset == NULL) + { + // Look for preset named 'Normal' flag set + preset = preset_lookup(hb_presets, "Normal", 0, 0, 1); + if (preset == NULL) + { + // Just grab the first preset available + preset = find_first_preset(hb_presets); + if (preset == NULL) + return NULL; + } + } + return hb_value_dup(preset); +} + +char * hb_presets_get_default_json(void) +{ + hb_value_t *preset = preset_lookup(hb_presets, NULL, 1, 0, 1); + if (preset == NULL) + return NULL; + return hb_value_get_json(preset); +} + +// Return: +// 0 upon success +// 1 if preset name could not be found +int hb_presets_set_default(const char *name, int recurse) +{ + hb_value_t *preset = preset_lookup(hb_presets, NULL, 1, 0, 1); + if (preset != NULL) + { + // Mark old defalt as not + hb_dict_set(preset, "Default", hb_value_bool(0)); + } + preset = preset_lookup(hb_presets, name, 0, 0, recurse); + if (preset == NULL) + return -1; + hb_dict_set(preset, "Default", hb_value_bool(1)); + return 0; +} + +int hb_presets_add(hb_value_t *preset) +{ + if (preset == NULL) + return -1; + // TODO: validity checking of input preset + if (hb_value_type(preset) == HB_VALUE_TYPE_DICT) + { + // A standalone preset or folder of presets. Add to preset array. + // Only allow custom presets to be added + if (hb_value_get_int(hb_dict_get(preset, "Type")) == 1) + hb_value_array_append(hb_presets_custom, hb_value_dup(preset)); + } + else if (hb_value_type(preset) == HB_VALUE_TYPE_ARRAY) + { + // An array of presets. Add each element. + int count = hb_value_array_len(preset); + int ii; + for (ii = 0; ii < count; ii++) + { + hb_value_t *value = hb_value_array_get(preset, ii); + hb_value_array_append(hb_presets_custom, hb_value_dup(value)); + } + } + + // Reconstruct global list + hb_value_decref(hb_presets); + hb_presets = hb_value_dup(hb_presets_builtin); + // Append custom presets + int count = hb_value_array_len(hb_presets_custom); + int ii; + for (ii = 0; ii < count; ii++) + { + hb_value_t *value = hb_value_array_get(hb_presets_custom, ii); + // Only allow custom presets to be added + if (hb_value_get_int(hb_dict_get(value, "Type")) == 1) + hb_value_array_append(hb_presets, hb_value_dup(value)); + } + return 0; +} + +int hb_presets_add_json(const char *json) +{ + hb_value_t *packaged_preset = hb_value_json(json); + hb_value_t *preset = preset_unpackage(packaged_preset); + if (preset == NULL) + return -1; + int result = hb_presets_add(preset); + hb_value_free(&preset); + hb_value_free(&packaged_preset); + return result; +} + +int hb_presets_add_file(const char *filename) +{ + hb_value_t *packaged_preset = hb_value_read_json(filename); + hb_value_t *preset = preset_unpackage(packaged_preset); + // Unpackaging does some validity checks and can fail + if (preset == NULL) + return -1; + int result = hb_presets_add(preset); + hb_value_free(&preset); + hb_value_free(&packaged_preset); + return result; +} + +static int compare_str(const void *a, const void *b) +{ + return strncmp(*(const char**)a, *(const char**)b, PATH_MAX); +} + +int hb_presets_add_path(char * path) +{ + hb_stat_t sb; + HB_DIR * dir; + struct dirent * entry; + char * filename; + int count, ii; + char ** files; + int result = -1; + + if (hb_stat(path, &sb)) + return -1; + + if (S_ISREG(sb.st_mode)) + { + return hb_presets_add_file(path); + } + + if (!S_ISDIR(sb.st_mode)) + return -1; + + dir = hb_opendir(path); + if ( dir == NULL ) + return -1; + + // Count the total number of entries + count = 0; + while ((entry = hb_readdir(dir))) + { + count++; + } + files = malloc(count * sizeof(char*)); + + // Find all regular files + ii = 0; + hb_rewinddir(dir); + while ((entry = hb_readdir(dir))) + { + filename = hb_strdup_printf("%s" DIR_SEP_STR "%s", path, entry->d_name); + if (hb_stat(filename, &sb)) + { + free(filename); + continue; + } + + // Only load regular files + if (!S_ISREG(sb.st_mode)) + { + free(filename); + continue; + } + // Only load files with .json extension + if (strcmp(".json", filename + strlen(filename) - 5)) + { + free(filename); + continue; + } + + files[ii++] = filename; + } + count = ii; + + // Sort the files so presets get added in a consistent order + qsort(files, count, sizeof(char*), compare_str); + + // Add preset files to preset list + for (ii = 0; ii < count; ii++) + { + int res = hb_presets_add_file(files[ii]); + // return success if any one of the files is successfully loaded + if (res == 0) + result = res; + } + hb_closedir( dir ); + free(files); + + return result; +} + +hb_value_t * hb_presets_get(void) +{ + return hb_value_dup(hb_presets); +} + +char * hb_presets_get_json(void) +{ + return hb_value_get_json(hb_presets); +} + +int hb_preset_write_json(hb_value_t *preset, const char *path) +{ + hb_value_t *packaged_preset = preset_package(preset); + // Packaging does some validity checks and can fail + if (packaged_preset == NULL) + return -1; + int result = hb_value_write_json(packaged_preset, path); + hb_value_free(&packaged_preset); + return result; +} + +char * hb_preset_package_json(hb_value_t *preset) +{ + hb_value_t *packaged_preset = preset_package(preset); + // Packaging does some validity checks and can fail + if (packaged_preset == NULL) + return NULL; + char *json = hb_value_get_json(packaged_preset); + hb_value_free(&packaged_preset); + return json; +} + +void hb_presets_free(void) +{ + hb_value_free(&hb_preset_template); + hb_value_free(&hb_presets); + hb_value_free(&hb_presets_custom); + hb_value_free(&hb_presets_builtin); +} diff --git a/libhb/preset.h b/libhb/preset.h new file mode 100644 index 000000000..5e7c460b6 --- /dev/null +++ b/libhb/preset.h @@ -0,0 +1,84 @@ +/* hb_preset.h + + Copyright (c) 2003-2015 HandBrake Team + This file is part of the HandBrake source code + Homepage: <http://handbrake.fr/>. + It may be used under the terms of the GNU General Public License v2. + For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html + */ +#if !defined(HB_PRESET_H) +#define HB_PRESET_H + +#include "common.h" +#include "hb_dict.h" + +// Initialize the hb_value_array_t that holds HandBrake builtin presets +void hb_presets_builtin_init(void); + +// Load presets from GUI presets file if possible +int hb_presets_gui_init(void); + +// Free all libhb presets +void hb_presets_free(void); + +// Get list of HandBrake builtin presets as hb_value_array_t +hb_value_t * hb_presets_builtin_get(void); + +// Get list of HandBrake builtin presets as json string +char * hb_presets_builtin_get_json(void); + +// Register new presets with libhb from +// hb_dict_t (single preset) or hb_value_array_t (list of presets) +int hb_presets_add(hb_value_t *preset); + +// Register new presets with libhb from json string +int hb_presets_add_json(const char *json); + +// Register new presets with libhb from json file +int hb_presets_add_file(const char *filename); + +// Register new presets with libhb from json file(s) +// path can be a directory, in which case all *.json files in the +// directory will be added +int hb_presets_add_path(char * path); + +// Get list of all presets registered with libhb as hb_value_array_t +hb_value_t * hb_presets_get(void); + +// Get list of all presets registered with libhb as json string +char * hb_presets_get_json(void); + +// Initialize a job from the given title and preset +hb_dict_t * hb_preset_job_init(hb_handle_t *h, int title_index, + hb_dict_t *preset); +int hb_preset_job_add_subtitles(hb_handle_t *h, int title_index, + hb_dict_t *preset, hb_dict_t *job_dict); +int hb_preset_job_add_audio(hb_handle_t *h, int title_index, + hb_dict_t *preset, hb_dict_t *job_dict); + +// Lookup a preset in the preset list. The "name" may contain '/' +// separators to explicitely specify a preset within the preset lists +// folder structure. +// +// If 'recurse' is specified, a recursive search for the first component +// in the name will be performed. +// +// I assume that the actual preset name does not include any '/' +hb_value_t * hb_preset_get(const char *name, int recurse); +char * hb_preset_get_json(const char *name, int recurse); + +// Recursively lookup the preset that is marked as 'Default' +hb_dict_t * hb_presets_get_default(void); +char * hb_presets_get_default_json(void); + +// Set the preset that is marked as 'Default' +int hb_presets_set_default(const char *name, int recurse); + +// Package the provided preset (wrap in dict and add version etc) +// and write to json file +int hb_preset_write_json(hb_value_t *preset, const char *path); +// Package the provided preset (wrap in dict and add version etc) +// and return as json string +char * hb_preset_package_json(hb_value_t *preset); + +#endif // HB_PRESET_H diff --git a/libhb/preset_builtin.json b/libhb/preset_builtin.json new file mode 100644 index 000000000..5bf5d4d8a --- /dev/null +++ b/libhb/preset_builtin.json @@ -0,0 +1,855 @@ + [ + { + "ChildrenArray": [ + { + "AudioAllowAACPass": 1, + "AudioAllowAC3Pass": 1, + "AudioAllowDTSHDPass": 1, + "AudioAllowDTSPass": 1, + "AudioAllowMP3Pass": 1, + "AudioEncoderFallback": "ac3", + "AudioList": [ + { + "AudioBitrate": "160", + "AudioEncoder": "aac", + "AudioMixdown": "dpl2", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + }, + { + "AudioBitrate": "160", + "AudioEncoder": "copy:ac3", + "AudioMixdown": "none", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + } + ], + "ChapterMarkers": 1, + "Default": 0, + "FileFormat": "mp4", + "Folder": false, + "Mp4HttpOptimize": 0, + "Mp4iPodCompatible": 0, + "PictureAutoCrop": 1, + "PictureBottomCrop": 0, + "PictureDeblock": 0, + "PictureDecomb": 0, + "PictureDecombCustom": "", + "PictureDecombDeinterlace": 1, + "PictureDeinterlace": 0, + "PictureDeinterlaceCustom": "", + "PictureDenoiseCustom": "", + "PictureDenoiseFilter": "off", + "PictureDetelecine": 0, + "PictureDetelecineCustom": "", + "PictureHeight": 576, + "PictureKeepRatio": 0, + "PictureLeftCrop": 0, + "PictureModulus": 2, + "PicturePAR": "loose", + "PictureRightCrop": 0, + "PictureTopCrop": 0, + "PictureWidth": 720, + "PresetDescription": "HandBrake's settings for compatibility with all Apple devices (including the iPod 6G and later). Includes Dolby Digital audio for surround sound.", + "PresetName": "Universal", + "Type": 0, + "UsesPictureFilters": 1, + "UsesPictureSettings": 1, + "VideoAvgBitrate": "2500", + "VideoEncoder": "x264", + "VideoFramerate": "30", + "VideoFramerateMode": "pfr", + "VideoGrayScale": 0, + "VideoLevel": "3.0", + "VideoOptionExtra": "", + "VideoPreset": "fast", + "VideoProfile": "baseline", + "VideoQualitySlider": 20.0, + "VideoQualityType": 2, + "VideoTune": "", + "VideoTurboTwoPass": 0, + "VideoTwoPass": 0, + "x264Option": "", + "x264UseAdvancedOptions": 0 + }, + { + "AudioAllowAACPass": 1, + "AudioAllowAC3Pass": 1, + "AudioAllowDTSHDPass": 1, + "AudioAllowDTSPass": 1, + "AudioAllowMP3Pass": 1, + "AudioEncoderFallback": "ac3", + "AudioList": [ + { + "AudioBitrate": "160", + "AudioEncoder": "aac", + "AudioMixdown": "dpl2", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + } + ], + "ChapterMarkers": 1, + "Default": 0, + "FileFormat": "mp4", + "Folder": false, + "Mp4HttpOptimize": 0, + "Mp4iPodCompatible": 1, + "PictureAutoCrop": 1, + "PictureBottomCrop": 0, + "PictureDeblock": 0, + "PictureDecomb": 0, + "PictureDecombCustom": "", + "PictureDecombDeinterlace": 1, + "PictureDeinterlace": 0, + "PictureDeinterlaceCustom": "", + "PictureDenoiseCustom": "", + "PictureDenoiseFilter": "off", + "PictureDetelecine": 0, + "PictureDetelecineCustom": "", + "PictureHeight": 240, + "PictureKeepRatio": 1, + "PictureLeftCrop": 0, + "PictureModulus": 2, + "PicturePAR": "off", + "PictureRightCrop": 0, + "PictureTopCrop": 0, + "PictureWidth": 320, + "PresetDescription": "HandBrake's settings for playback on the iPod with Video (all generations).", + "PresetName": "iPod", + "Type": 0, + "UsesPictureFilters": 1, + "UsesPictureSettings": 1, + "VideoAvgBitrate": "2500", + "VideoEncoder": "x264", + "VideoFramerate": "30", + "VideoFramerateMode": "pfr", + "VideoGrayScale": 0, + "VideoLevel": "1.3", + "VideoOptionExtra": "", + "VideoPreset": "medium", + "VideoProfile": "baseline", + "VideoQualitySlider": 22.0, + "VideoQualityType": 2, + "VideoTune": "", + "VideoTurboTwoPass": 0, + "VideoTwoPass": 0, + "x264Option": "", + "x264UseAdvancedOptions": 0 + }, + { + "AudioAllowAACPass": 1, + "AudioAllowAC3Pass": 1, + "AudioAllowDTSHDPass": 1, + "AudioAllowDTSPass": 1, + "AudioAllowMP3Pass": 1, + "AudioEncoderFallback": "ac3", + "AudioList": [ + { + "AudioBitrate": "160", + "AudioEncoder": "aac", + "AudioMixdown": "dpl2", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + } + ], + "ChapterMarkers": 1, + "Default": 0, + "FileFormat": "mp4", + "Folder": false, + "Mp4HttpOptimize": 0, + "Mp4iPodCompatible": 0, + "PictureAutoCrop": 1, + "PictureBottomCrop": 0, + "PictureDeblock": 0, + "PictureDecomb": 0, + "PictureDecombCustom": "", + "PictureDecombDeinterlace": 1, + "PictureDeinterlace": 0, + "PictureDeinterlaceCustom": "", + "PictureDenoiseCustom": "", + "PictureDenoiseFilter": "off", + "PictureDetelecine": 0, + "PictureDetelecineCustom": "", + "PictureHeight": 640, + "PictureKeepRatio": 0, + "PictureLeftCrop": 0, + "PictureModulus": 2, + "PicturePAR": "loose", + "PictureRightCrop": 0, + "PictureTopCrop": 0, + "PictureWidth": 960, + "PresetDescription": "HandBrake's settings for handheld iOS devices (iPhone 4, iPod touch 3G and later).", + "PresetName": "iPhone & iPod touch", + "Type": 0, + "UsesPictureFilters": 1, + "UsesPictureSettings": 1, + "VideoAvgBitrate": "2500", + "VideoEncoder": "x264", + "VideoFramerate": "30", + "VideoFramerateMode": "pfr", + "VideoGrayScale": 0, + "VideoLevel": "3.1", + "VideoOptionExtra": "", + "VideoPreset": "medium", + "VideoProfile": "high", + "VideoQualitySlider": 22.0, + "VideoQualityType": 2, + "VideoTune": "", + "VideoTurboTwoPass": 0, + "VideoTwoPass": 0, + "x264Option": "", + "x264UseAdvancedOptions": 0 + }, + { + "AudioAllowAACPass": 1, + "AudioAllowAC3Pass": 1, + "AudioAllowDTSHDPass": 1, + "AudioAllowDTSPass": 1, + "AudioAllowMP3Pass": 1, + "AudioEncoderFallback": "ac3", + "AudioList": [ + { + "AudioBitrate": "160", + "AudioEncoder": "aac", + "AudioMixdown": "dpl2", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + } + ], + "ChapterMarkers": 1, + "Default": 0, + "FileFormat": "mp4", + "Folder": false, + "Mp4HttpOptimize": 0, + "Mp4iPodCompatible": 0, + "PictureAutoCrop": 1, + "PictureBottomCrop": 0, + "PictureDeblock": 0, + "PictureDecomb": 0, + "PictureDecombCustom": "", + "PictureDecombDeinterlace": 1, + "PictureDeinterlace": 0, + "PictureDeinterlaceCustom": "", + "PictureDenoiseCustom": "", + "PictureDenoiseFilter": "off", + "PictureDetelecine": 0, + "PictureDetelecineCustom": "", + "PictureHeight": 720, + "PictureKeepRatio": 0, + "PictureLeftCrop": 0, + "PictureModulus": 2, + "PicturePAR": "loose", + "PictureRightCrop": 0, + "PictureTopCrop": 0, + "PictureWidth": 1280, + "PresetDescription": "HandBrake's settings for playback on the iPad (all generations).", + "PresetName": "iPad", + "Type": 0, + "UsesPictureFilters": 1, + "UsesPictureSettings": 1, + "VideoAvgBitrate": "2500", + "VideoEncoder": "x264", + "VideoFramerate": "30", + "VideoFramerateMode": "pfr", + "VideoGrayScale": 0, + "VideoLevel": "3.1", + "VideoOptionExtra": "", + "VideoPreset": "medium", + "VideoProfile": "high", + "VideoQualitySlider": 20.0, + "VideoQualityType": 2, + "VideoTune": "", + "VideoTurboTwoPass": 0, + "VideoTwoPass": 0, + "x264Option": "", + "x264UseAdvancedOptions": 0 + }, + { + "AudioAllowAACPass": 1, + "AudioAllowAC3Pass": 1, + "AudioAllowDTSHDPass": 1, + "AudioAllowDTSPass": 1, + "AudioAllowMP3Pass": 1, + "AudioEncoderFallback": "ac3", + "AudioList": [ + { + "AudioBitrate": "160", + "AudioEncoder": "aac", + "AudioMixdown": "dpl2", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + }, + { + "AudioBitrate": "160", + "AudioEncoder": "copy:ac3", + "AudioMixdown": "none", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + } + ], + "ChapterMarkers": 1, + "Default": 0, + "FileFormat": "mp4", + "Folder": false, + "Mp4HttpOptimize": 0, + "Mp4iPodCompatible": 0, + "PictureAutoCrop": 1, + "PictureBottomCrop": 0, + "PictureDeblock": 0, + "PictureDecomb": 0, + "PictureDecombCustom": "", + "PictureDecombDeinterlace": 1, + "PictureDeinterlace": 0, + "PictureDeinterlaceCustom": "", + "PictureDenoiseCustom": "", + "PictureDenoiseFilter": "off", + "PictureDetelecine": 0, + "PictureDetelecineCustom": "", + "PictureHeight": 720, + "PictureKeepRatio": 0, + "PictureLeftCrop": 0, + "PictureModulus": 2, + "PicturePAR": "loose", + "PictureRightCrop": 0, + "PictureTopCrop": 0, + "PictureWidth": 960, + "PresetDescription": "HandBrake's settings for the original AppleTV. Includes Dolby Digital audio for surround sound. Also compatible with iOS devices released since 2009.", + "PresetName": "AppleTV", + "Type": 0, + "UsesPictureFilters": 1, + "UsesPictureSettings": 1, + "VideoAvgBitrate": "2500", + "VideoEncoder": "x264", + "VideoFramerate": "30", + "VideoFramerateMode": "pfr", + "VideoGrayScale": 0, + "VideoLevel": "3.1", + "VideoOptionExtra": "qpmin=4:cabac=0:ref=2:b-pyramid=none:weightb=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500", + "VideoPreset": "medium", + "VideoProfile": "high", + "VideoQualitySlider": 20.0, + "VideoQualityType": 2, + "VideoTune": "", + "VideoTurboTwoPass": 0, + "VideoTwoPass": 0, + "x264Option": "", + "x264UseAdvancedOptions": 0 + }, + { + "AudioAllowAACPass": 1, + "AudioAllowAC3Pass": 1, + "AudioAllowDTSHDPass": 1, + "AudioAllowDTSPass": 1, + "AudioAllowMP3Pass": 1, + "AudioEncoderFallback": "ac3", + "AudioList": [ + { + "AudioBitrate": "160", + "AudioEncoder": "aac", + "AudioMixdown": "dpl2", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + }, + { + "AudioBitrate": "160", + "AudioEncoder": "copy:ac3", + "AudioMixdown": "none", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + } + ], + "ChapterMarkers": 1, + "Default": 0, + "FileFormat": "mp4", + "Folder": false, + "Mp4HttpOptimize": 0, + "Mp4iPodCompatible": 0, + "PictureAutoCrop": 1, + "PictureBottomCrop": 0, + "PictureDeblock": 0, + "PictureDecomb": 0, + "PictureDecombCustom": "", + "PictureDecombDeinterlace": 1, + "PictureDeinterlace": 0, + "PictureDeinterlaceCustom": "", + "PictureDenoiseCustom": "", + "PictureDenoiseFilter": "off", + "PictureDetelecine": 0, + "PictureDetelecineCustom": "", + "PictureHeight": 720, + "PictureKeepRatio": 0, + "PictureLeftCrop": 0, + "PictureModulus": 2, + "PicturePAR": "loose", + "PictureRightCrop": 0, + "PictureTopCrop": 0, + "PictureWidth": 1280, + "PresetDescription": "HandBrake's settings for the second-generation AppleTV. Includes Dolby Digital audio for surround sound. NOT compatible with the original AppleTV.", + "PresetName": "AppleTV 2", + "Type": 0, + "UsesPictureFilters": 1, + "UsesPictureSettings": 1, + "VideoAvgBitrate": "2500", + "VideoEncoder": "x264", + "VideoFramerate": "30", + "VideoFramerateMode": "pfr", + "VideoGrayScale": 0, + "VideoLevel": "3.1", + "VideoOptionExtra": "", + "VideoPreset": "medium", + "VideoProfile": "high", + "VideoQualitySlider": 20.0, + "VideoQualityType": 2, + "VideoTune": "", + "VideoTurboTwoPass": 0, + "VideoTwoPass": 0, + "x264Option": "", + "x264UseAdvancedOptions": 0 + }, + { + "AudioAllowAACPass": 1, + "AudioAllowAC3Pass": 1, + "AudioAllowDTSHDPass": 1, + "AudioAllowDTSPass": 1, + "AudioAllowMP3Pass": 1, + "AudioEncoderFallback": "ac3", + "AudioList": [ + { + "AudioBitrate": "160", + "AudioEncoder": "aac", + "AudioMixdown": "dpl2", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + }, + { + "AudioBitrate": "160", + "AudioEncoder": "copy:ac3", + "AudioMixdown": "none", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + } + ], + "ChapterMarkers": 1, + "Default": 0, + "FileFormat": "mp4", + "Folder": false, + "Mp4HttpOptimize": 0, + "Mp4iPodCompatible": 0, + "PictureAutoCrop": 1, + "PictureBottomCrop": 0, + "PictureDeblock": 0, + "PictureDecomb": 3, + "PictureDecombCustom": "", + "PictureDecombDeinterlace": 1, + "PictureDeinterlace": 0, + "PictureDeinterlaceCustom": "", + "PictureDenoiseCustom": "", + "PictureDenoiseFilter": "off", + "PictureDetelecine": 0, + "PictureDetelecineCustom": "", + "PictureHeight": 1080, + "PictureKeepRatio": 0, + "PictureLeftCrop": 0, + "PictureModulus": 2, + "PicturePAR": "loose", + "PictureRightCrop": 0, + "PictureTopCrop": 0, + "PictureWidth": 1920, + "PresetDescription": "HandBrake's settings for the third-generation AppleTV. Includes Dolby Digital audio for surround sound. NOT compatible with the original AppleTV. May stutter on the second-generation AppleTV.", + "PresetName": "AppleTV 3", + "Type": 0, + "UsesPictureFilters": 1, + "UsesPictureSettings": 1, + "VideoAvgBitrate": "2500", + "VideoEncoder": "x264", + "VideoFramerate": "30", + "VideoFramerateMode": "pfr", + "VideoGrayScale": 0, + "VideoLevel": "4.0", + "VideoOptionExtra": "", + "VideoPreset": "medium", + "VideoProfile": "high", + "VideoQualitySlider": 20.0, + "VideoQualityType": 2, + "VideoTune": "", + "VideoTurboTwoPass": 0, + "VideoTwoPass": 0, + "x264Option": "", + "x264UseAdvancedOptions": 0 + }, + { + "AudioAllowAACPass": 1, + "AudioAllowAC3Pass": 1, + "AudioAllowDTSHDPass": 1, + "AudioAllowDTSPass": 1, + "AudioAllowMP3Pass": 1, + "AudioEncoderFallback": "ac3", + "AudioList": [ + { + "AudioBitrate": "128", + "AudioEncoder": "aac", + "AudioMixdown": "dpl2", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + } + ], + "ChapterMarkers": 0, + "Default": 0, + "FileFormat": "mp4", + "Folder": false, + "Mp4HttpOptimize": 0, + "Mp4iPodCompatible": 0, + "PictureAutoCrop": 1, + "PictureBottomCrop": 0, + "PictureDeblock": 0, + "PictureDecomb": 0, + "PictureDecombCustom": "", + "PictureDecombDeinterlace": 1, + "PictureDeinterlace": 0, + "PictureDeinterlaceCustom": "", + "PictureDenoiseCustom": "", + "PictureDenoiseFilter": "off", + "PictureDetelecine": 0, + "PictureDetelecineCustom": "", + "PictureHeight": 576, + "PictureKeepRatio": 0, + "PictureLeftCrop": 0, + "PictureModulus": 2, + "PicturePAR": "loose", + "PictureRightCrop": 0, + "PictureTopCrop": 0, + "PictureWidth": 720, + "PresetDescription": "HandBrake's settings for midrange devices running Android 2.3 or later.", + "PresetName": "Android", + "Type": 0, + "UsesPictureFilters": 1, + "UsesPictureSettings": 1, + "VideoAvgBitrate": "2500", + "VideoEncoder": "x264", + "VideoFramerate": "30", + "VideoFramerateMode": "pfr", + "VideoGrayScale": 0, + "VideoLevel": "3.0", + "VideoOptionExtra": "", + "VideoPreset": "medium", + "VideoProfile": "main", + "VideoQualitySlider": 22.0, + "VideoQualityType": 2, + "VideoTune": "", + "VideoTurboTwoPass": 0, + "VideoTwoPass": 0, + "x264Option": "", + "x264UseAdvancedOptions": 0 + }, + { + "AudioAllowAACPass": 1, + "AudioAllowAC3Pass": 1, + "AudioAllowDTSHDPass": 1, + "AudioAllowDTSPass": 1, + "AudioAllowMP3Pass": 1, + "AudioEncoderFallback": "ac3", + "AudioList": [ + { + "AudioBitrate": "128", + "AudioEncoder": "aac", + "AudioMixdown": "dpl2", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + } + ], + "ChapterMarkers": 0, + "Default": 0, + "FileFormat": "mp4", + "Folder": false, + "Mp4HttpOptimize": 0, + "Mp4iPodCompatible": 0, + "PictureAutoCrop": 1, + "PictureBottomCrop": 0, + "PictureDeblock": 0, + "PictureDecomb": 0, + "PictureDecombCustom": "", + "PictureDecombDeinterlace": 1, + "PictureDeinterlace": 0, + "PictureDeinterlaceCustom": "", + "PictureDenoiseCustom": "", + "PictureDenoiseFilter": "off", + "PictureDetelecine": 0, + "PictureDetelecineCustom": "", + "PictureHeight": 720, + "PictureKeepRatio": 0, + "PictureLeftCrop": 0, + "PictureModulus": 2, + "PicturePAR": "loose", + "PictureRightCrop": 0, + "PictureTopCrop": 0, + "PictureWidth": 1280, + "PresetDescription": "HandBrake's preset for tablets running Android 2.3 or later.", + "PresetName": "Android Tablet", + "Type": 0, + "UsesPictureFilters": 1, + "UsesPictureSettings": 1, + "VideoAvgBitrate": "2500", + "VideoEncoder": "x264", + "VideoFramerate": "30", + "VideoFramerateMode": "pfr", + "VideoGrayScale": 0, + "VideoLevel": "3.1", + "VideoOptionExtra": "", + "VideoPreset": "medium", + "VideoProfile": "main", + "VideoQualitySlider": 22.0, + "VideoQualityType": 2, + "VideoTune": "", + "VideoTurboTwoPass": 0, + "VideoTwoPass": 0, + "x264Option": "", + "x264UseAdvancedOptions": 0 + }, + { + "AudioAllowAACPass": 1, + "AudioAllowAC3Pass": 1, + "AudioAllowDTSHDPass": 1, + "AudioAllowDTSPass": 1, + "AudioAllowMP3Pass": 1, + "AudioEncoderFallback": "ac3", + "AudioList": [ + { + "AudioBitrate": "128", + "AudioEncoder": "aac", + "AudioMixdown": "dpl2", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + } + ], + "ChapterMarkers": 0, + "Default": 0, + "FileFormat": "mp4", + "Folder": false, + "Mp4HttpOptimize": 0, + "Mp4iPodCompatible": 0, + "PictureAutoCrop": 1, + "PictureBottomCrop": 0, + "PictureDeblock": 0, + "PictureDecomb": 0, + "PictureDecombCustom": "", + "PictureDecombDeinterlace": 1, + "PictureDeinterlace": 0, + "PictureDeinterlaceCustom": "", + "PictureDenoiseCustom": "", + "PictureDenoiseFilter": "off", + "PictureDetelecine": 0, + "PictureDetelecineCustom": "", + "PictureHeight": 720, + "PictureKeepRatio": 1, + "PictureLeftCrop": 0, + "PictureModulus": 2, + "PicturePAR": "off", + "PictureRightCrop": 0, + "PictureTopCrop": 0, + "PictureWidth": 1280, + "PresetDescription": "HandBrake's preset for Windows Phone 8 devices", + "PresetName": "Windows Phone 8", + "Type": 0, + "UsesPictureFilters": 1, + "UsesPictureSettings": 1, + "VideoAvgBitrate": "2500", + "VideoEncoder": "x264", + "VideoFramerate": "30", + "VideoFramerateMode": "pfr", + "VideoGrayScale": 0, + "VideoLevel": "3.1", + "VideoOptionExtra": "", + "VideoPreset": "medium", + "VideoProfile": "main", + "VideoQualitySlider": 22.0, + "VideoQualityType": 2, + "VideoTune": "", + "VideoTurboTwoPass": 0, + "VideoTwoPass": 0, + "x264Option": "", + "x264UseAdvancedOptions": 0 + } + ], + "Default": 0, + "Folder": true, + "PresetName": "Devices", + "Type": 0 + }, + { + "ChildrenArray": [ + { + "AudioAllowAACPass": 1, + "AudioAllowAC3Pass": 1, + "AudioAllowDTSHDPass": 1, + "AudioAllowDTSPass": 1, + "AudioAllowMP3Pass": 1, + "AudioEncoderFallback": "ac3", + "AudioList": [ + { + "AudioBitrate": "160", + "AudioEncoder": "aac", + "AudioMixdown": "dpl2", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + } + ], + "ChapterMarkers": 1, + "Default": 1, + "FileFormat": "mp4", + "Folder": false, + "Mp4HttpOptimize": 0, + "Mp4iPodCompatible": 0, + "PictureAutoCrop": 1, + "PictureBottomCrop": 0, + "PictureDeblock": 0, + "PictureDecomb": 0, + "PictureDecombCustom": "", + "PictureDecombDeinterlace": 1, + "PictureDeinterlace": 0, + "PictureDeinterlaceCustom": "", + "PictureDenoiseCustom": "", + "PictureDenoiseFilter": "off", + "PictureDetelecine": 0, + "PictureDetelecineCustom": "", + "PictureHeight": 0, + "PictureKeepRatio": 0, + "PictureLeftCrop": 0, + "PictureModulus": 2, + "PicturePAR": "loose", + "PictureRightCrop": 0, + "PictureTopCrop": 0, + "PictureWidth": 0, + "PresetDescription": "HandBrake's normal, default settings.", + "PresetName": "Normal", + "Type": 0, + "UsesPictureFilters": 1, + "UsesPictureSettings": 1, + "VideoAvgBitrate": "2500", + "VideoEncoder": "x264", + "VideoFramerate": "auto", + "VideoFramerateMode": "vfr", + "VideoGrayScale": 0, + "VideoLevel": "4.0", + "VideoOptionExtra": "", + "VideoPreset": "veryfast", + "VideoProfile": "main", + "VideoQualitySlider": 20.0, + "VideoQualityType": 2, + "VideoTune": "", + "VideoTurboTwoPass": 0, + "VideoTwoPass": 0, + "x264Option": "", + "x264UseAdvancedOptions": 0 + }, + { + "AudioAllowAACPass": 1, + "AudioAllowAC3Pass": 1, + "AudioAllowDTSHDPass": 1, + "AudioAllowDTSPass": 1, + "AudioAllowMP3Pass": 1, + "AudioEncoderFallback": "ac3", + "AudioList": [ + { + "AudioBitrate": "160", + "AudioEncoder": "aac", + "AudioMixdown": "dpl2", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + }, + { + "AudioBitrate": "160", + "AudioEncoder": "copy:ac3", + "AudioMixdown": "none", + "AudioSamplerate": "auto", + "AudioTrack": 1, + "AudioTrackDRCSlider": 0.0, + "AudioTrackGainSlider": 0.0 + } + ], + "ChapterMarkers": 1, + "Default": 0, + "FileFormat": "mp4", + "Folder": false, + "Mp4HttpOptimize": 0, + "Mp4iPodCompatible": 0, + "PictureAutoCrop": 1, + "PictureBottomCrop": 0, + "PictureDeblock": 0, + "PictureDecomb": 2, + "PictureDecombCustom": "", + "PictureDecombDeinterlace": 1, + "PictureDeinterlace": 0, + "PictureDeinterlaceCustom": "", + "PictureDenoiseCustom": "", + "PictureDenoiseFilter": "off", + "PictureDetelecine": 0, + "PictureDetelecineCustom": "", + "PictureHeight": 0, + "PictureKeepRatio": 0, + "PictureLeftCrop": 0, + "PictureModulus": 2, + "PicturePAR": "loose", + "PictureRightCrop": 0, + "PictureTopCrop": 0, + "PictureWidth": 0, + "PresetDescription": "HandBrake's general-purpose preset for High Profile H.264 video.", + "PresetName": "High Profile", + "Type": 0, + "UsesPictureFilters": 1, + "UsesPictureSettings": 1, + "VideoAvgBitrate": "2500", + "VideoEncoder": "x264", + "VideoFramerate": "auto", + "VideoFramerateMode": "vfr", + "VideoGrayScale": 0, + "VideoLevel": "4.1", + "VideoOptionExtra": "", + "VideoPreset": "medium", + "VideoProfile": "high", + "VideoQualitySlider": 20.0, + "VideoQualityType": 2, + "VideoTune": "", + "VideoTurboTwoPass": 0, + "VideoTwoPass": 0, + "x264Option": "", + "x264UseAdvancedOptions": 0 + } + ], + "Default": 0, + "Folder": true, + "PresetName": "Regular", + "Type": 0 + } + ] diff --git a/libhb/preset_template.json b/libhb/preset_template.json new file mode 100644 index 000000000..30449e437 --- /dev/null +++ b/libhb/preset_template.json @@ -0,0 +1,104 @@ + { + "AudioAllowMP3Pass": false, + "AudioAllowAACPass": false, + "AudioAllowAC3Pass": true, + "AudioAllowDTSPass": false, + "AudioAllowDTSHDPass": false, + "AudioAllowEAC3Pass": false, + "AudioAllowFLACPass": false, + "AudioAllowTRUEHDPass": false, + "AudioCopyMask": [ + ], + "AudioEncoderFallback": "ac3", + "AudioLanguageList": [ + "und" + ], + "AudioList": [ + { + "AudioBitrate": "192", + "AudioCompressionLevel": -1.0, + "AudioDitherMethod": "auto", + "AudioEncoder": "copy:ac3", + "AudioMixdown": "dpl2", + "AudioNormalizeMixLevel": false, + "AudioSamplerate": "auto", + "AudioTrackQualityEnable": false, + "AudioTrackQuality": -1.0, + "AudioTrackGainSlider": 0.0, + "AudioTrackDRCSlider": 0.0 + } + ], + "AudioSecondaryEncoderMode": true, + "AudioTrackSelectionBehavior": "first", + "ChapterMarkers": true, + "Default": false, + "FileFormat": "mp4", + "Folder": false, + "Mp4HttpOptimize": false, + "Mp4iPodCompatible": false, + "PictureAutoCrop": true, + "PictureBottomCrop": 0, + "PictureLeftCrop": 0, + "PictureRightCrop": 0, + "PictureTopCrop": 0, + "PictureDARWidth": 0, + "PictureDeblock": 0, + "PictureDecomb": "off", + "PictureDecombCustom": "", + "PictureDecombDeinterlace": true, + "PictureDeinterlace": "off", + "PictureDeinterlaceCustom": "", + "PictureDenoiseCustom": "", + "PictureDenoiseFilter": "off", + "PictureDenoisePreset": "medium", + "PictureDenoiseTune": "none", + "PictureDetelecine": "off", + "PictureDetelecineCustom": "", + "PictureItuPAR": false, + "PictureKeepRatio": true, + "PictureLooseCrop": false, + "PictureModulus": 2, + "PicturePAR": "loose", + "PicturePARWidth": 853, + "PicturePARHeight": 720, + "PictureRotate": 0, + "PictureWidth": 0, + "PictureHeight": 0, + "PictureForceHeight": 0, + "PictureForceWidth": 0, + "PresetDescription": "", + "PresetName": "Name Missing", + "Type": 1, + "UsesPictureFilters": true, + "UsesPictureSettings": 2, + "SubtitleAddCC": false, + "SubtitleAddForeignAudioSearch": false, + "SubtitleAddForeignAudioSubtitle": false, + "SubtitleBurnBehavior": "none", + "SubtitleBurnBDSub": false, + "SubtitleBurnDVDSub": false, + "SubtitleLanguageList": [ + ], + "SubtitleTrackSelectionBehavior": "none", + "VideoAvgBitrate": 1800, + "VideoColorMatrixCode": 0, + "VideoEncoder": "x264", + "VideoFramerate": "auto", + "VideoFramerateMode": "vfr", + "VideoGrayScale": false, + "VideoHWDecode": false, + "VideoScaler": "swscale", + "VideoPreset": "medium", + "VideoTune": "none", + "VideoProfile": "auto", + "VideoLevel": "auto", + "VideoOptionExtra": "", + "VideoQualityType": 2, + "VideoQualitySlider": 20.0, + "VideoQSVDecode": false, + "VideoQSVAsyncDepth": 4, + "VideoTwoPass": false, + "VideoTurboTwoPass": false, + "x264Option": "", + "x264UseAdvancedOptions": false + } diff --git a/scripts/create_resources.py b/scripts/create_resources.py new file mode 100644 index 000000000..95921d317 --- /dev/null +++ b/scripts/create_resources.py @@ -0,0 +1,161 @@ +#! /bin/python +# + +import types +import os +import sys +import time +import datetime +import json +import plistlib +import getopt +from xml.parsers import expat + +resources = dict() +stack = list() +stack.append(resources) + +def top(ss): + return ss[len(ss)-1] + +def end_element_handler(tag): + global stack + + if tag == "section": + stack.pop() + +def start_element_handler(tag, attr): + global resources, stack + + current = top(stack) + key = val = None + if tag == "section": + key = attr["name"] + val = dict() + stack.append(val) + elif tag == "integer": + key = attr["name"] + val = int(attr["value"]) + elif tag == "json": + fbase = attr["file"] + fname = find_file(fbase) + key = attr["name"] + if fname != None and key != None: + try: + fp = open(fname) + except Exception, err: + print >> sys.stderr, ( "Error: %s" % str(err) ) + val = json.load(fp) + elif fname == None: + print >> sys.stderr, ( "Error: No such json file %s" % fbase ) + sys.exit(1) + elif tag == "plist": + fbase = attr["file"] + fname = find_file(fbase) + key = attr["name"] + if fname != None and key != None: + val = plistlib.readPlist(fname) + elif fname == None: + print >> sys.stderr, ( "Error: No such plist file %s" % fbase ) + sys.exit(1) + elif tag == "string": + fbase = attr["file"] + fname = find_file(fbase) + key = attr["name"] + if fname != None and key != None: + try: + fp = open(fname) + val = fp.read() + except Exception, err: + print >> sys.stderr, ( "Error: %s" % str(err) ) + sys.exit(1) + elif fname == None: + print >> sys.stderr, ( "Error: No such string file %s" % fbase ) + sys.exit(1) + + if val != None: + if type(current) == types.DictType: + current[key] = val + elif type(current) == types.TupleType: + current.append(val) + + +def cdata_handler(str): + return + +def resource_parse_file(infile): + parser = expat.ParserCreate() + parser.StartElementHandler = start_element_handler + parser.EndElementHandler = end_element_handler + parser.CharacterDataHandler = cdata_handler + parser.ParseFile(infile) + +def usage(): + print >> sys.stderr, ( + "Usage: %s [-I <inc path>] <resource list> [resource json]\n" + "Summary:\n" + " Creates a resource json from a resource list\n\n" + "Options:\n" + " I - Include path to search for files\n" + " <resource list> Input resources file\n" + " <resource json> Output resources json file\n" + % sys.argv[0] + ) + +inc_list = list() + +def find_file(name): + global inc_list + + for inc_dir in inc_list: + inc = "%s/%s" % (inc_dir, name) + if os.path.isfile(inc): + return inc + + if os.path.isfile(name): + return name + + return None + +def main(): + global inc_list + + OPTS = "I:" + try: + opts, args = getopt.gnu_getopt(sys.argv[1:], OPTS) + except getopt.GetoptError, err: + print >> sys.stderr, str(err) + usage() + sys.exit(2) + + for o, a in opts: + if o == "-I": + # add to include list + inc_list.append(a) + else: + assert False, "unhandled option" + + if len(args) > 2 or len(args) < 1: + usage() + sys.exit(2) + + try: + infile = open(args[0]) + except Exception, err: + print >> sys.stderr, ( "Error: %s" % str(err) ) + sys.exit(1) + + if len(args) > 1: + try: + outfile = open(args[1], "w") + except Exception, err: + print >> sys.stderr, ( "Error: %s" % str(err)) + sys.exit(1) + else: + outfile = sys.stdout + + resource_parse_file(infile) + json.dump(resources, outfile, indent=4, sort_keys=True) + +main() + diff --git a/scripts/quotestring.py b/scripts/quotestring.py new file mode 100644 index 000000000..1038719e7 --- /dev/null +++ b/scripts/quotestring.py @@ -0,0 +1,62 @@ +#! /usr/bin/python + +import re +import getopt +import sys + +def usage(): + print >> sys.stderr, ( + "Usage: %s <input> [output]\n" + "Summary:\n" + " Creates a quoted string suitable for inclusion in a C char*\n\n" + "Options:\n" + " <input> Input file to quote\n" + " <output> Output quoted string [stdout]\n" + % sys.argv[0] + ) + +def main(): + global inc_list + + OPTS = "" + try: + opts, args = getopt.gnu_getopt(sys.argv[1:], OPTS) + except getopt.GetoptError, err: + print >> sys.stderr, str(err) + usage() + sys.exit(2) + + for o, a in opts: + usage() + assert False, "unhandled option" + + if len(args) > 2 or len(args) < 1: + usage() + sys.exit(2) + + try: + infile = open(args[0]) + except Exception, err: + print >> sys.stderr, ( "Error: %s" % str(err) ) + sys.exit(1) + + if len(args) > 1: + try: + outfile = open(args[1], "w") + except Exception, err: + print >> sys.stderr, ( "Error: %s" % str(err)) + sys.exit(1) + else: + outfile = sys.stdout + + ss = infile.read() + ss = re.sub(r'\\', r'\\\\', ss) + ss = re.sub(r'"', r'\\"', ss) + pattern = re.compile("$", re.M) + ss = re.sub(pattern, r'\\n"', ss) + pattern = re.compile("^", re.M) + ss = re.sub(pattern, "\"", ss) + outfile.write(ss) + +main() + diff --git a/test/test.c b/test/test.c index a3b18a227..d489ffd08 100644 --- a/test/test.c +++ b/test/test.c @@ -42,112 +42,117 @@ #endif /* Options */ -static int debug = HB_DEBUG_ALL; -static int update = 0; -static int dvdnav = 1; -static char * input = NULL; -static char * output = NULL; -static char * format = NULL; -static int titleindex = 1; -static int titlescan = 0; -static int main_feature = 0; -static char * native_language = NULL; -static int native_dub = 0; -static int twoPass = 0; -static int deinterlace = 0; -static char * deinterlace_opt = 0; -static int deblock = 0; -static char * deblock_opt = 0; -static int denoise = 0; -static char * denoise_opt = 0; -static int nlmeans = 0; -static char * nlmeans_opt = NULL; -static char * nlmeans_tune_opt = NULL; -static int detelecine = 0; -static char * detelecine_opt = 0; -static int decomb = 0; -static char * decomb_opt = 0; -static int rotate = 0; -static char * rotate_opt = 0; -static int rotate_val = 0; -static int grayscale = 0; -static int vcodec = HB_VCODEC_FFMPEG_MPEG4; -static hb_list_t * audios = NULL; -static hb_audio_config_t * audio = NULL; -static int num_audio_tracks = 0; -static int allowed_audio_copy = -1; -static char * mixdowns = NULL; -static char * dynamic_range_compression = NULL; -static char * audio_gain = NULL; -static char ** audio_dither = NULL; -static char ** normalize_mix_level = NULL; -static char * atracks = NULL; -static char * arates = NULL; -static char ** abitrates = NULL; -static char ** aqualities = NULL; -static char ** acompressions = NULL; -static char * acodec_fallback = NULL; -static char * acodecs = NULL; -static char ** anames = NULL; -static int audio_explicit = 0; -static char ** subtracks = NULL; -static char ** subforce = NULL; -static char * subburn = NULL; -static char * subdefault = NULL; -static char ** srtfile = NULL; -static char ** srtcodeset = NULL; -static char ** srtoffset = NULL; -static char ** srtlang = NULL; -static int srtdefault = -1; -static int srtburn = -1; -static int subtitle_scan = 0; -static int width = 0; -static int height = 0; -static int crop[4] = { -1,-1,-1,-1 }; -static int loose_crop = -1; -static int vrate = 0; -static float vquality = -1.0; -static int vbitrate = 0; -static int mux = 0; -static int anamorphic_mode = 0; -static int modulus = 0; -static int par_height = 0; -static int par_width = 0; -static int display_width = 0; -static int keep_display_aspect = 0; -static int itu_par = 0; -static int angle = 0; -static int chapter_start = 0; -static int chapter_end = 0; -static int chapter_markers = 0; -static char * marker_file = NULL; -static char * x264_preset = NULL; -static char * x264_tune = NULL; -static char * advanced_opts = NULL; -static char * h264_profile = NULL; -static char * h264_level = NULL; -static int maxHeight = 0; -static int maxWidth = 0; -static int fastfirstpass = 0; -static int preset = 0; -static char * preset_name = 0; -static int cfr = 0; -static int mp4_optimize = 0; -static int ipod_atom = 0; -static int color_matrix_code = 0; -static int preview_count = 10; -static int store_previews = 0; -static int start_at_preview = 0; -static int64_t start_at_pts = 0; -static int start_at_frame = 0; -static int64_t stop_at_pts = 0; -static int stop_at_frame = 0; +static int debug = HB_DEBUG_ALL; +static int update = 0; +static int dvdnav = 1; +static char * input = NULL; +static char * output = NULL; +static char * format = NULL; +static int titleindex = 1; +static int titlescan = 0; +static int main_feature = 0; +static char * native_language = NULL; +static int native_dub = 0; +static int twoPass = 0; +static int deinterlace_disable = 0; +static int deinterlace_custom = 0; +static char * deinterlace = NULL; +static int deblock_disable = 0; +static char * deblock = NULL; +static int hqdn3d_disable = 0; +static int hqdn3d_custom = 0; +static char * hqdn3d = NULL; +static int nlmeans_disable = 0; +static int nlmeans_custom = 0; +static char * nlmeans = NULL; +static char * nlmeans_tune = NULL; +static int detelecine_disable = 0; +static int detelecine_custom = 0; +static char * detelecine = NULL; +static int decomb_disable = 0; +static int decomb_custom = 0; +static char * decomb = NULL; +static char * rotate = NULL; +static int grayscale = -1; +static char * vcodec = NULL; +static int audio_all = -1; +static char ** audio_copy_list = NULL; +static char ** audio_lang_list = NULL; +static char ** atracks = NULL; +static char ** acodecs = NULL; +static char ** abitrates = NULL; +static char ** aqualities = NULL; +static char ** arates = NULL; +static char ** mixdowns = NULL; +static char ** normalize_mix_level = NULL; +static char ** audio_dither = NULL; +static char ** dynamic_range_compression = NULL; +static char ** audio_gain = NULL; +static char ** acompressions = NULL; +static char * acodec_fallback = NULL; +static char ** anames = NULL; +static char ** subtitle_lang_list = NULL; +static char ** subtracks = NULL; +static char ** subforce = NULL; +static int subtitle_all = -1; +static int subburn = 0; +static int subburn_native = 0; +static int subdefault = 0; +static char ** srtfile = NULL; +static char ** srtcodeset = NULL; +static char ** srtoffset = NULL; +static char ** srtlang = NULL; +static int srtdefault = -1; +static int srtburn = -1; +static int width = 0; +static int height = 0; +static int crop[4] = { -1,-1,-1,-1 }; +static int loose_crop = -1; +static char * vrate = NULL; +static float vquality = -1.0; +static int vbitrate = 0; +static int mux = 0; +static int anamorphic_mode = -1; +static int modulus = 0; +static int par_height = -1; +static int par_width = -1; +static int display_width = -1; +static int keep_display_aspect = 0; +static int itu_par = -1; +static int angle = 0; +static int chapter_start = 0; +static int chapter_end = 0; +static int chapter_markers = -1; +static char * marker_file = NULL; +static char * encoder_preset = NULL; +static char * encoder_tune = NULL; +static char * encoder_profile = NULL; +static char * encoder_level = NULL; +static char * advanced_opts = NULL; +static int maxHeight = 0; +static int maxWidth = 0; +static int fastfirstpass = 0; +static char * preset_export_name = NULL; +static char * preset_export_desc = NULL; +static char * preset_export_file = NULL; +static char * preset_name = NULL; +static int cfr = -1; +static int mp4_optimize = -1; +static int ipod_atom = -1; +static int color_matrix_code = -1; +static int preview_count = 10; +static int store_previews = 0; +static int start_at_preview = 0; +static int64_t start_at_pts = 0; +static int start_at_frame = 0; +static int64_t stop_at_pts = 0; +static int stop_at_frame = 0; static uint64_t min_title_duration = 10; -static int use_opencl = 0; -static int use_hwd = 0; +static int use_opencl = -1; +static int use_hwd = -1; #ifdef USE_QSV -static int qsv_async_depth = -1; -static int qsv_decode = 1; +static int qsv_async_depth = -1; +static int qsv_decode = -1; #endif /* Exit cleanly on Ctrl-C */ @@ -157,7 +162,6 @@ static void SigHandler( int ); /* Utils */ static void ShowHelp(); -static void ShowPresets(); static void ShowCommands() { fprintf(stdout, "\nCommands:\n"); @@ -167,10 +171,14 @@ static void ShowCommands() fprintf(stdout, " [r]esume Resume encoding\n"); } -static int ParseOptions( int argc, char ** argv ); -static int CheckOptions( int argc, char ** argv ); -static int HandleEvents( hb_handle_t * h ); +static int ParseOptions( int argc, char ** argv ); +static int CheckOptions( int argc, char ** argv ); +static int HandleEvents( hb_handle_t * h, hb_dict_t *preset_dict ); +static hb_dict_t * PreparePreset( const char *preset_name ); +static hb_dict_t * PrepareJob( hb_handle_t *h, hb_title_t *title, + hb_dict_t *preset_dict ); +static int str_vlen(char **strv); static void str_vfree( char **strv ); static char** str_split( char *str, char delem ); @@ -216,7 +224,7 @@ static int get_argv_utf8(int *argc_ptr, char ***argv_ptr) size += WideCharToMultiByte(CP_UTF8, 0, argv_utf16[i], -1, NULL, 0, NULL, NULL ); argv = malloc(size); - if (argv) + if (argv != NULL) { for (i = 0; i < argc; i++) { @@ -248,7 +256,6 @@ int main( int argc, char ** argv ) hb_global_init(); - audios = hb_list_init(); // Get utf8 command line if windows get_argv_utf8(&argc, &argv); @@ -297,6 +304,45 @@ int main( int argc, char ** argv ) /* Exit ASAP on Ctrl-C */ signal( SIGINT, SigHandler ); + // Apply all command line overrides to the preset that are possible. + // Some command line options are applied later to the job + // (e.g. chapter names, explicit audio & subtitle tracks). + hb_dict_t *preset_dict = PreparePreset(preset_name); + if (preset_dict == NULL) + { + // An appropriate error message should have already + // been spilled by PreparePreset. + return 1; + } + + if (preset_export_name != NULL) + { + hb_dict_set(preset_dict, "PresetName", + hb_value_string(preset_export_name)); + if (preset_export_desc != NULL) + { + hb_dict_set(preset_dict, "PresetDescription", + hb_value_string(preset_export_desc)); + } + if (preset_export_file != NULL) + { + hb_preset_write_json(preset_dict, preset_export_file); + } + else + { + char *json; + json = hb_preset_package_json(preset_dict); + fprintf(stdout, "%s\n", json); + } + // If the user requested to export a preset, but not to + // transcode or scan a file, exit here. + if (input == NULL || (!titlescan && titleindex != 0 && output == NULL)) + { + hb_value_free(&preset_dict); + return 0; + } + } + /* Feed libhb with a DVD to scan */ fprintf( stderr, "Opening %s...\n", input ); @@ -307,15 +353,16 @@ int main( int argc, char ** argv ) titleindex = 0; } - hb_system_sleep_prevent(h); // FIXME: When hardware decode is enabled, the scan must be performed // with hardware decode enabled because the decoder context used during // encoding phase comes from the context used during scan. This is // broken by design and I would very much like to fix this someday. - hb_hwd_set_enable(h, use_hwd); - hb_scan( h, input, titleindex, preview_count, store_previews, min_title_duration * 90000LL ); + hb_hwd_set_enable(h, hb_value_get_bool( + hb_dict_get(preset_dict, "VideoHWDecode"))); + hb_scan(h, input, titleindex, preview_count, store_previews, + min_title_duration * 90000LL); /* Wait... */ while( !die ) @@ -403,47 +450,49 @@ int main( int argc, char ** argv ) hb_snooze( 200 ); #endif - HandleEvents( h ); + HandleEvents( h, preset_dict ); } /* Clean up */ + hb_value_free(&preset_dict); hb_close(&h); hb_global_close(); - if (audios != NULL) - { - while ((audio = hb_list_item(audios, 0)) != NULL) - { - hb_list_rem(audios, audio); - if (audio->out.name != NULL) - { - free(audio->out.name); - } - free(audio); - } - hb_list_close(&audios); - } + str_vfree(audio_copy_list); str_vfree(abitrates); str_vfree(acompressions); str_vfree(aqualities); str_vfree(audio_dither); - free(acodecs); - free(arates); - free(atracks); - free(audio_gain); - free(dynamic_range_compression); - free(mixdowns); + str_vfree(acodecs); + str_vfree(arates); + str_vfree(atracks); + str_vfree(audio_lang_list); + str_vfree(audio_gain); + str_vfree(dynamic_range_compression); + str_vfree(mixdowns); + str_vfree(subtitle_lang_list); + str_vfree(subtracks); + free(acodec_fallback); free(native_language); free(format); free(input); free(output); free(preset_name); - free(x264_preset); - free(x264_tune); + free(encoder_preset); + free(encoder_tune); free(advanced_opts); - free(h264_profile); - free(h264_level); - free(nlmeans_opt); - free(nlmeans_tune_opt); + free(encoder_profile); + free(encoder_level); + free(rotate); + free(deblock); + free(detelecine); + free(deinterlace); + free(decomb); + free(hqdn3d); + free(nlmeans); + free(nlmeans_tune); + free(preset_export_name); + free(preset_export_desc); + free(preset_export_file); // write a carriage return to stdout // avoids overlap / line wrapping when stderr is redirected @@ -455,8 +504,6 @@ int main( int argc, char ** argv ) static void PrintTitleInfo( hb_title_t * title, int feature ) { - hb_chapter_t * chapter; - hb_subtitle_t * subtitle; int i; fprintf( stderr, "+ title %d:\n", title->index ); @@ -500,6 +547,7 @@ static void PrintTitleInfo( hb_title_t * title, int feature ) fprintf( stderr, " + chapters:\n" ); for( i = 0; i < hb_list_count( title->list_chapter ); i++ ) { + hb_chapter_t * chapter; chapter = hb_list_item( title->list_chapter, i ); fprintf( stderr, " + %d: cells %d->%d, %"PRIu64" blocks, duration " "%02d:%02d:%02d\n", chapter->index, @@ -510,20 +558,21 @@ static void PrintTitleInfo( hb_title_t * title, int feature ) fprintf( stderr, " + audio tracks:\n" ); for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { + hb_audio_config_t *audio; audio = hb_list_audio_config_item( title->list_audio, i ); if( ( audio->in.codec == HB_ACODEC_AC3 ) || ( audio->in.codec == HB_ACODEC_DCA) ) { - fprintf( stderr, " + %d, %s (iso639-2: %s), %dHz, %dbps\n", + fprintf( stderr, " + %d, %s (iso639-2: %s), %dHz, %dbps\n", i + 1, - audio->lang.description, + audio->lang.description, audio->lang.iso639_2, - audio->in.samplerate, + audio->in.samplerate, audio->in.bitrate ); } else { - fprintf( stderr, " + %d, %s (iso639-2: %s)\n", - i + 1, + fprintf( stderr, " + %d, %s (iso639-2: %s)\n", + i + 1, audio->lang.description, audio->lang.iso639_2 ); } @@ -531,8 +580,9 @@ static void PrintTitleInfo( hb_title_t * title, int feature ) fprintf( stderr, " + subtitle tracks:\n" ); for( i = 0; i < hb_list_count( title->list_subtitle ); i++ ) { + hb_subtitle_t *subtitle; subtitle = hb_list_item( title->list_subtitle, i ); - fprintf( stderr, " + %d, %s (iso639-2: %s) (%s)(%s)\n", + fprintf( stderr, " + %d, %s (iso639-2: %s) (%s)(%s)\n", i + 1, subtitle->lang, subtitle->iso639_2, (subtitle->format == TEXTSUB) ? "Text" : "Bitmap", @@ -578,80 +628,69 @@ static int test_sub_list( char ** list, int pos ) return 0; } -static int cmp_lang( char * lang, const char * code ) +void write_chapter_names(hb_dict_t *job_dict, const char *marker_file) { - iso639_lang_t * iso639; + if (marker_file == NULL) + return; - iso639 = lang_for_code2( code ); + hb_csv_file_t * file = hb_open_csv_file(marker_file); + hb_csv_cell_t * cell; + int row = 0; - if ( iso639 == NULL ) - return 0; - if ( iso639->eng_name && !strcasecmp( lang, iso639->eng_name ) ) - return 1; - if ( iso639->native_name && !strcasecmp( lang, iso639->native_name ) ) - return 1; - if ( iso639->iso639_1 && !strcasecmp( lang, iso639->iso639_1 ) ) - return 1; - if ( iso639->iso639_2 && !strcasecmp( lang, iso639->iso639_2 ) ) - return 1; - if ( iso639->iso639_2b && !strcasecmp( lang, iso639->iso639_2b ) ) - return 1; - return 0; -} + if (file == NULL) + { + fprintf(stderr, "Cannot open chapter marker file, using defaults\n"); + return; + } + fprintf(stderr, "Reading chapter markers from file %s\n", marker_file); -static void apply_loose_crop(int total, int * v1, int * v2, int mod, int max) -{ - /* number of extra pixels which must be cropped to reach next modulus */ - int add = (total - *v1 - *v2) % mod; + hb_value_array_t *chapter_array; + chapter_array = hb_dict_get(hb_dict_get(job_dict, "Destination"), + "ChapterList"); - if (add) - { - /* number of pixels which must be uncropped to reach previous modulus */ - int sub = mod - add; + if (chapter_array == NULL) + return; - /* less than maximum (or can't reduce), increase the crop size */ - if (add <= max || sub > (*v1 + *v2)) + /* Parse the cells */ + while (NULL != (cell = hb_read_next_cell(file))) + { + /* We have a chapter number */ + if (cell->cell_col == 0) { - int add1 = add / 2; - if ((*v1 + add1) & 1) // avoid odd crop if possible - ++add1; - int add2 = (add - add1); - - *v1 += add1; - *v2 += add2; + row = cell->cell_row; } - /* more than maximum, reduce the crop size instead */ - else + /* We have a chapter name */ + if (cell->cell_col == 1 && row == cell->cell_row) { - int sub1 = sub / 2; - if (sub1 > *v1) - sub1 = *v1; - else if ((*v1 - sub1) & 1) // avoid odd crop if possible - ++sub1; - - int sub2 = sub - sub1; - if (sub2 > *v2) + /* If we have a valid chapter, add chapter entry */ + hb_dict_t *chapter_dict = hb_value_array_get(chapter_array, row); + if (chapter_dict != NULL) { - sub1 += (sub2 - *v2); - if ((*v1 - sub1) & 1) // avoid odd crop if possible - ++sub1; - sub2 = sub - sub1; + hb_dict_set(chapter_dict, "Name", + hb_value_string(cell->cell_text)); } - - *v1 -= sub1; - *v2 -= sub2; } + hb_dispose_cell( cell ); + } + hb_close_csv_file( file ); +} + +static void lang_list_remove(hb_value_array_t *list, const char *lang) +{ + int count = hb_value_array_len(list); + int ii; + for (ii = count - 1; ii >= 0; ii--) + { + const char *tmp = hb_value_get_string(hb_value_array_get(list, ii)); + if (!strncmp(lang, tmp, 4)) + hb_value_array_remove(list, ii); } } -static int HandleEvents( hb_handle_t * h ) +static int HandleEvents(hb_handle_t * h, hb_dict_t *preset_dict) { hb_state_t s; - const hb_encoder_t *encoder; - int tmp_num_audio_tracks; - int filter_cfr; - hb_rational_t filter_vrate; hb_get_state( h, &s ); switch( s.state ) @@ -681,23 +720,8 @@ static int HandleEvents( hb_handle_t * h ) { hb_title_set_t * title_set; hb_title_t * title; - hb_job_t * job; - int i; - int sub_burned = 0; - - /* Audio argument string parsing variables */ - int acodec = 0; - int abitrate = 0; - float aquality = 0; - float acompression = 0; - int arate = 0; - int mixdown = HB_AMIXDOWN_DOLBYPLII; - double d_r_c = 0; - double gain = 0; - /* Audio argument string parsing variables */ title_set = hb_get_title_set( h ); - if( !title_set || !hb_list_count( title_set->list_title ) ) { /* No valid title, stop right there */ @@ -706,7 +730,7 @@ static int HandleEvents( hb_handle_t * h ) die = 1; break; } - if( main_feature ) + if (main_feature) { int i; int main_feature_idx=0; @@ -744,15 +768,15 @@ static int HandleEvents( hb_handle_t * h ) break; } titleindex = main_feature_idx; - fprintf( stderr, "Found main feature title, setting title to %d\n", - main_feature_idx); + fprintf(stderr, "Found main feature title %d\n", + main_feature_idx); - title = hb_list_item( title_set->list_title, main_feature_pos); + title = hb_list_item(title_set->list_title, main_feature_pos); } else { - title = hb_list_item( title_set->list_title, 0 ); + title = hb_list_item(title_set->list_title, 0); } - if( !titleindex || titlescan ) + if (!titleindex || titlescan) { /* Scan-only mode, print infos and exit */ PrintTitleSetInfo( title_set ); @@ -760,2063 +784,31 @@ static int HandleEvents( hb_handle_t * h ) break; } - PrintTitleInfo( title, title_set->feature ); - - /* Set job settings */ - job = hb_job_init(title); - filter_cfr = job->cfr; - filter_vrate = job->vrate; + fprintf( stderr, "+ Using preset: %s\n", + hb_value_get_string(hb_dict_get(preset_dict, "PresetName"))); + PrintTitleInfo(title, title_set->feature); - if( chapter_start && chapter_end && !stop_at_pts && !start_at_preview && !stop_at_frame && !start_at_pts && !start_at_frame ) - { - job->chapter_start = MAX( job->chapter_start, - chapter_start ); - job->chapter_end = MIN( job->chapter_end, - chapter_end ); - job->chapter_end = MAX( job->chapter_start, - job->chapter_end ); - } - - if ( angle ) + // All overrides to the preset are complete + // Initialize the job from preset + overrides + // and apply job specific command line overrides + hb_dict_t *job_dict = PrepareJob(h, title, preset_dict); + if (job_dict == NULL) { - job->angle = angle; - } - - if (preset) - { - fprintf( stderr, "+ Using preset: %s\n", preset_name); - - if (!strcasecmp(preset_name, "Universal")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1,1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac,copy:ac3"); - } - if( !abitrates ) - { - abitrates = str_split("160,160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2,none"); - } - if( !arates ) - { - arates = strdup("Auto,Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0,0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 720; - maxHeight = 576; - if (x264_preset == NULL) - { - x264_preset = strdup("fast"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("baseline"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.0"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "iPod")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - job->ipod_atom = 1; - vcodec = HB_VCODEC_X264; - job->vquality = 22.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 320; - maxHeight = 240; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("baseline"); - } - if (h264_level == NULL) - { - h264_level = strdup("1.3"); - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "iPhone & iPod touch")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 22.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 960; - maxHeight = 640; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("high"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.1"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "iPad")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 1280; - maxHeight = 720; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("high"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.1"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "AppleTV")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1,1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac,copy:ac3"); - } - if( !abitrates ) - { - abitrates = str_split("160,160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2,none"); - } - if( !arates ) - { - arates = strdup("Auto,Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0,0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 960; - maxHeight = 720; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("high"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.1"); - } - if (advanced_opts == NULL) - { - advanced_opts = strdup("qpmin=4:cabac=0:ref=2:b-pyramid=none:weightb=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "AppleTV 2")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1,1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac,copy:ac3"); - } - if( !abitrates ) - { - abitrates = str_split("160,160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2,none"); - } - if( !arates ) - { - arates = strdup("Auto,Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0,0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 1280; - maxHeight = 720; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("high"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.1"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "AppleTV 3")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1,1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac,copy:ac3"); - } - if( !abitrates ) - { - abitrates = str_split("160,160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2,none"); - } - if( !arates ) - { - arates = strdup("Auto,Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0,0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 1920; - maxHeight = 1080; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("high"); - } - if (h264_level == NULL) - { - h264_level = strdup("4.0"); - } - decomb = 1; - decomb_opt = "7:2:6:9:1:80"; - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "Android")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 22.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("128", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 720; - maxHeight = 576; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("main"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.0"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - } - if (!strcasecmp(preset_name, "Android Tablet")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 22.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("128", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 1280; - maxHeight = 720; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("main"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.1"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - } - if (!strcasecmp(preset_name, "Windows Phone 8")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 22.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("128", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 1280; - maxHeight = 720; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("main"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.1"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 0; - } - modulus = 2; - } - if (!strcasecmp(preset_name, "Normal")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - if (x264_preset == NULL) - { - x264_preset = strdup("veryfast"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("main"); - } - if (h264_level == NULL) - { - h264_level = strdup("4.0"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "High Profile")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - if( !atracks ) - { - atracks = strdup("1,1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac,copy:ac3"); - } - if( !abitrates ) - { - abitrates = str_split("160,160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2,none"); - } - if( !arates ) - { - arates = strdup("Auto,Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0,0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("high"); - } - if (h264_level == NULL) - { - h264_level = strdup("4.1"); - } - decomb = 1; - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; - } - } - - if ( chapter_markers ) - { - job->chapter_markers = chapter_markers; - - if( marker_file != NULL ) - { - hb_csv_file_t * file = hb_open_csv_file( marker_file ); - hb_csv_cell_t * cell; - int row = 0; - int chapter = 0; - - fprintf( stderr, "Reading chapter markers from file %s\n", marker_file ); - - if( file == NULL ) - { - fprintf( stderr, "Cannot open chapter marker file, using defaults\n" ); - } - else - { - /* Parse the cells */ - while( NULL != ( cell = hb_read_next_cell( file ) ) ) - { - /* We have a chapter number */ - if( cell->cell_col == 0 ) - { - row = cell->cell_row; - chapter = atoi( cell->cell_text ); - } - - /* We have a chapter name */ - if( cell->cell_col == 1 && row == cell->cell_row ) - { - /* If we have a valid chapter, copy the string an terminate it */ - if( chapter >= job->chapter_start && chapter <= job->chapter_end ) - { - hb_chapter_t * chapter_s; - - chapter_s = hb_list_item( job->list_chapter, chapter - 1); - hb_chapter_set_title(chapter_s, cell->cell_text); - } - } - - - hb_dispose_cell( cell ); - } - hb_close_csv_file( file ); - } - } - } - - if (crop[0] < 0 || crop[1] < 0 || crop[2] < 0 || crop[3] < 0) - { - memcpy(crop, title->crop, sizeof(int[4])); - } - - if (loose_crop >= 0) - { - int mod = modulus > 0 ? modulus : 2; - apply_loose_crop(title->geometry.height, - &crop[0], &crop[1], mod, loose_crop); - apply_loose_crop(title->geometry.width, - &crop[2], &crop[3], mod, loose_crop); - } - - job->grayscale = grayscale; - - hb_filter_object_t * filter; - - /* Add selected filters */ - if( detelecine ) - { - filter = hb_filter_init( HB_FILTER_DETELECINE ); - hb_add_filter( job, filter, detelecine_opt ); - } - if( decomb ) - { - filter = hb_filter_init( HB_FILTER_DECOMB ); - hb_add_filter( job, filter, decomb_opt ); - } - if( deinterlace ) - { - filter = hb_filter_init( HB_FILTER_DEINTERLACE ); - hb_add_filter( job, filter, deinterlace_opt ); - } - if( deblock ) - { - filter = hb_filter_init( HB_FILTER_DEBLOCK ); - hb_add_filter( job, filter, deblock_opt ); - } - if( denoise ) - { - filter = hb_filter_init( HB_FILTER_DENOISE ); - hb_add_filter( job, filter, denoise_opt ); - } - if( nlmeans ) - { - filter = hb_filter_init( HB_FILTER_NLMEANS ); - hb_add_filter( job, filter, nlmeans_opt ); - } - if( rotate ) - { - filter = hb_filter_init( HB_FILTER_ROTATE ); - hb_add_filter( job, filter, rotate_opt); - } - - if (use_hwd) - { - job->use_hwd = use_hwd; - } - - hb_geometry_t srcGeo, resultGeo; - hb_geometry_settings_t uiGeo; - - srcGeo = title->geometry; - - keep_display_aspect |= anamorphic_mode != HB_ANAMORPHIC_CUSTOM; - uiGeo.mode = anamorphic_mode; - if (width != 0 && height != 0) - { - if (anamorphic_mode == HB_ANAMORPHIC_NONE) - { - keep_display_aspect = 0; - } - else - { - uiGeo.mode = HB_ANAMORPHIC_CUSTOM; - } - } - uiGeo.keep = !!keep_display_aspect * HB_KEEP_DISPLAY_ASPECT; - uiGeo.itu_par = itu_par; - uiGeo.modulus = modulus; - memcpy(uiGeo.crop, crop, sizeof(int[4])); - if (width == 0) - { - uiGeo.geometry.width = title->geometry.width - crop[2] - crop[3]; - } - else - { - uiGeo.keep |= HB_KEEP_WIDTH; - uiGeo.geometry.width = width; - } - if (height == 0) - { - uiGeo.geometry.height = title->geometry.height - crop[0] - crop[1]; - } - else - { - uiGeo.keep |= HB_KEEP_HEIGHT; - uiGeo.geometry.height = height; - } - uiGeo.maxWidth = maxWidth; - uiGeo.maxHeight = maxHeight; - if( par_width && par_height ) - { - uiGeo.geometry.par.num = par_width; - uiGeo.geometry.par.den = par_height; - } - else if (display_width != 0 && width != 0) - { - if (height != 0) - { - fprintf(stderr, "display_width (%d), width (%d), and height (%d) can not all be specified, ignoring height", display_width, width, height); - } - uiGeo.geometry.par.num = display_width; - uiGeo.geometry.par.den = width; - } - else if (display_width != 0) - { - uiGeo.geometry.par.num = display_width; - uiGeo.geometry.par.den = uiGeo.geometry.width; - } - else - { - uiGeo.geometry.par = srcGeo.par; - } - - hb_set_anamorphic_size2(&srcGeo, &uiGeo, &resultGeo); - job->par = resultGeo.par; - - // Add filter that does cropping and scaling - char * filter_str; - filter_str = hb_strdup_printf("%d:%d:%d:%d:%d:%d", - resultGeo.width, resultGeo.height, - crop[0], crop[1], crop[2], crop[3] ); - - filter = hb_filter_init( HB_FILTER_CROP_SCALE ); - hb_add_filter( job, filter, filter_str ); - free( filter_str ); - - // Add framerate shaping filter - if (vrate) - { - filter_cfr = cfr; - filter_vrate.num = 27000000; - filter_vrate.den = vrate; - } - else if (cfr) - { - // cfr or pfr flag with no rate specified implies - // use the title rate. - filter_cfr = cfr; - filter_vrate = title->vrate; - } - filter = hb_filter_init(HB_FILTER_VFR); - filter_str = hb_strdup_printf("%d:%d:%d", filter_cfr, - filter_vrate.num, filter_vrate.den); - hb_add_filter(job, filter, filter_str); - free(filter_str); - - // hb_job_init() will set a default muxer for us - // only override it if a specific muxer has been set - // note: the muxer must be set after presets, but before encoders - if (mux) - { - job->mux = mux; - } - // then, muxer options - if (mp4_optimize) - { - job->mp4_optimize = 1; - } - if (ipod_atom) - { - job->ipod_atom = 1; - } - - if( vquality >= 0.0 ) - { - job->vquality = vquality; - job->vbitrate = 0; - } - else if( vbitrate ) - { - job->vquality = -1.0; - job->vbitrate = vbitrate; - } - - /* Set video encoder and check muxer compatibility */ - if (vcodec) - { - job->vcodec = vcodec; - } - encoder = NULL; - while ((encoder = hb_video_encoder_get_next(encoder)) != NULL) - { - if ((encoder->codec == job->vcodec) && - (encoder->muxers & job->mux) == 0) - { - hb_error("incompatible video encoder '%s' for muxer '%s'", - hb_video_encoder_get_short_name(job->vcodec), - hb_container_get_short_name (job->mux)); - done_error = HB_ERROR_INIT; - die = 1; - return -1; - } - } - -#ifdef USE_QSV - if (qsv_async_depth >= 0) - { - job->qsv.async_depth = qsv_async_depth; - } - job->qsv.decode = qsv_decode; -#endif - - /* Grab audio tracks */ - if( atracks ) - { - char * token = strtok( atracks, "," ); - if( token == NULL ) - token = optarg; - int track_start, track_end; - for( ; token != NULL; token = strtok( NULL, "," ) ) - { - if( strlen( token ) >= 3 ) - { - if( sscanf( token, "%d-%d", &track_start, &track_end ) == 2 ) - { - int i; - for( i = track_start - 1; i < track_end; i++ ) - { - if( hb_list_item( title->list_audio, i ) == NULL ) - { - fprintf( stderr, "Warning: Could not find audio track %d, skipped\n", i + 1 ); - continue; - } - audio = calloc( 1, sizeof( *audio ) ); - hb_audio_config_init( audio ); - audio->in.track = i; - audio->out.track = num_audio_tracks++; - hb_list_add( audios, audio ); - } - } - else if( !strcasecmp(token, "none" ) ) - { - audio = calloc( 1, sizeof( *audio ) ); - hb_audio_config_init( audio ); - audio->in.track = audio->out.track = -1; - audio->out.codec = 0; - hb_list_add( audios, audio ); - break; - } - else - { - fprintf( stderr, "ERROR: unable to parse audio input \"%s\", skipping\n", - token); - } - } - else - { - int i = atoi( token ) - 1; - if( hb_list_item( title->list_audio, i ) == NULL ) - { - fprintf(stderr, - "Warning: Could not find audio track '%s', skipped\n", - token); - continue; - } - audio = calloc( 1, sizeof( *audio ) ); - hb_audio_config_init( audio ); - audio->in.track = i; - audio->out.track = num_audio_tracks++; - hb_list_add( audios, audio ); - } - } - } - - /* Parse audio tracks */ - if( native_language && native_dub ) - { - if( hb_list_count( audios ) == 0 || !audio_explicit ) - { - for( i = 0; i < hb_list_count( title->list_audio ); i++ ) - { - int track = i; - - audio = hb_list_audio_config_item( title->list_audio, i ); - - if( cmp_lang( native_language, audio->lang.iso639_2 ) && - audio->lang.type != 3 && // Directors 1 - audio->lang.type != 4) // Directors 2 - { - /* - * Matched an audio to our native language - use it. - * Replace any existing audio tracks that a preset may - * have put here. - */ - if( hb_list_count( audios ) == 0 ) - { - audio = calloc( 1, sizeof( *audio ) ); - hb_audio_config_init( audio ); - audio->in.track = track; - audio->out.track = num_audio_tracks++; - /* Add it to our audios */ - hb_list_add( audios, audio ); - } - else - { - /* - * Update the track numbers on what is already in - * there. - */ - for( i = 0; i < hb_list_count( audios ); i++ ) - { - audio = hb_list_item( audios, i ); - audio->in.track = track; - } - } - break; - } - } - } - else - { - fprintf( stderr, "Warning: Native language (dubbing) selection ignored since an audio track has already been selected\n" ); - } - } - - if( hb_list_count(audios) == 0 && - hb_list_count(title->list_audio) > 0 ) - { - /* Create a new audio track with default settings */ - audio = calloc( 1, sizeof( *audio ) ); - hb_audio_config_init( audio ); - /* Add it to our audios */ - hb_list_add( audios, audio ); - } - tmp_num_audio_tracks = num_audio_tracks = hb_list_count( audios ); - for( i = 0; i < tmp_num_audio_tracks; i++ ) - { - audio = hb_list_item( audios, 0 ); - if( audio == NULL || - audio->in.track == -1 || - audio->out.track == -1 || - audio->out.codec == 0 || - hb_audio_add( job, audio ) == 0 ) - { - num_audio_tracks--; - } - if( audio != NULL ) - { - hb_list_rem( audios, audio ); - if( audio->out.name ) - { - free( audio->out.name ); - } - free( audio ); - } - } - - /* Audio Codecs */ - i = 0; - if( acodecs ) - { - char * token = strtok(acodecs, ","); - if( token == NULL ) - token = acodecs; - while ( token != NULL ) - { - if ((acodec = hb_audio_encoder_get_from_name(token)) <= 0) - { - fprintf(stderr, "Invalid codec %s, using default for container.\n", token); - acodec = hb_audio_encoder_get_default(job->mux); - } - if( i < num_audio_tracks ) - { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.codec = acodec; - } - else - { - hb_audio_config_t * last_audio = hb_list_audio_config_item( job->list_audio, i - 1 ); - hb_audio_config_t audio; - - if( last_audio ) - { - fprintf(stderr, "More audio codecs than audio tracks, copying track %i and using encoder %s\n", - i, token); - hb_audio_config_init(&audio); - audio.in.track = last_audio->in.track; - audio.out.track = num_audio_tracks++; - audio.out.codec = acodec; - hb_audio_add(job, &audio); - } - else - { - fprintf(stderr, "Audio codecs and no valid audio tracks, skipping codec %s\n", token); - } - } - token = strtok(NULL, ","); - i++; - } - } - if( i < num_audio_tracks ) - { - /* We have fewer inputs than audio tracks, use the default codec for - * this container for the remaining tracks. Unless we only have one input - * then use that codec instead. - */ - if (i != 1) - acodec = hb_audio_encoder_get_default(job->mux); - for ( ; i < num_audio_tracks; i++) - { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.codec = acodec; - } - } - // sanity check muxer compatibility - for (i = 0; i < num_audio_tracks; i++) - { - encoder = NULL; - audio = hb_list_audio_config_item(job->list_audio, i); - if (audio != NULL) - { - while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) - { - if ((encoder->codec == audio->out.codec) && - (encoder->muxers & job->mux) == 0) - { - hb_error("audio track %d: incompatible encoder '%s' for muxer '%s'", i + 1, - hb_audio_encoder_get_short_name(audio->out.codec), - hb_container_get_short_name (job->mux)); - done_error = HB_ERROR_INIT; - die = 1; - return -1; - } - } - } - } - /* Audio Codecs */ - - /* Sample Rate */ - int auto_sample_rate = 0; - i = 0; - if( arates ) - { - char * token = strtok(arates, ","); - if (token == NULL) - token = arates; - while ( token != NULL ) - { - audio = hb_list_audio_config_item(job->list_audio, i); - - if( audio != NULL ) - { - if ( !strcasecmp( token, "auto" ) ) - { - arate = audio->in.samplerate; - auto_sample_rate = 1; - } - else - { - arate = hb_audio_samplerate_get_from_name(token); - } - if (arate <= 0) - { - fprintf(stderr, - "Invalid sample rate %s, using input rate %d\n", - token, audio->in.samplerate); - arate = audio->in.samplerate; - } - - audio->out.samplerate = arate; - if( (++i) >= num_audio_tracks ) - break; /* We have more inputs than audio tracks, oops */ - } - else - { - fprintf(stderr, "Ignoring sample rate %d, no audio tracks\n", arate); - } - token = strtok(NULL, ","); - } - } - if (i < num_audio_tracks) - { - /* We have fewer inputs than audio tracks, use default sample rate. - * Unless we only have one input, then use that for all tracks. - */ - int use_default = 0; - if( i != 1 || auto_sample_rate ) - use_default = 1; - - for ( ; i < num_audio_tracks; i++) - { - audio = hb_list_audio_config_item(job->list_audio, i); - if( use_default ) - arate = audio->in.samplerate; - audio->out.samplerate = arate; - } - } - /* Sample Rate */ - - /* Audio Mixdown */ - i = 0; - if ( mixdowns ) - { - char * token = strtok(mixdowns, ","); - if (token == NULL) - token = mixdowns; - while ( token != NULL ) - { - mixdown = hb_mixdown_get_from_name(token); - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio != NULL ) - { - audio->out.mixdown = mixdown; - if( (++i) >= num_audio_tracks ) - break; /* We have more inputs than audio tracks, oops */ - } - else - { - fprintf(stderr, "Ignoring mixdown, no audio tracks\n"); - } - token = strtok(NULL, ","); - } - } - if (i < num_audio_tracks && i == 1) - { - /* We have fewer inputs than audio tracks - * and we only have one input, then use that. - */ - for (; i < num_audio_tracks; i++) - { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.mixdown = mixdown; - } - } - /* Audio Mixdown */ - - /* Audio Bitrate */ - i = 0; - if( abitrates ) - { - for ( i = 0; abitrates[i] != NULL && i < num_audio_tracks; i++ ) - { - char * token = abitrates[i]; - abitrate = atoi(token); - audio = hb_list_audio_config_item(job->list_audio, i); - - if( audio != NULL ) - { - audio->out.bitrate = abitrate; - } - else - { - fprintf(stderr, "Ignoring bitrate %d, no audio tracks\n", abitrate); - } - } - } - if (i < num_audio_tracks && i == 1) - { - /* We have fewer inputs than audio tracks, - * and we only have one input, use - * that for all tracks. - */ - for (; i < num_audio_tracks; i++) - { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.bitrate = abitrate; - } - } - /* Audio Bitrate */ - - /* Audio Quality */ - i = 0; - if( aqualities ) - { - for ( i = 0; aqualities[i] != NULL && i < num_audio_tracks; i++ ) - { - char * token = aqualities[i]; - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio == NULL ) - { - fprintf(stderr, "Ignoring quality %.3f, no audio tracks\n", aquality); - } - else if( *token != 0 ) - { - aquality = atof(token); - - audio->out.quality = aquality; - audio->out.bitrate = -1; - } - } - } - if (i < num_audio_tracks && i == 1) - { - /* We have fewer inputs than audio tracks, - * and we only have one input, use - * that for all tracks. - */ - for (; i < num_audio_tracks; i++) - { - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio->out.bitrate <= 0 ) - audio->out.quality = aquality; - } - } - /* Audio Quality */ - - /* Audio Compression Level */ - i = 0; - if( acompressions ) - { - for ( i = 0; acompressions[i] != NULL && i < num_audio_tracks; i++ ) - { - char * token = acompressions[i]; - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio == NULL ) - { - fprintf(stderr, "Ignoring compression level %.2f, no audio tracks\n", acompression); - } - else if( *token != 0 ) - { - acompression = atof(token); - - audio->out.compression_level = acompression; - } - } - } - // Compression levels are codec specific values. So don't - // try to apply to other tracks. - /* Audio Compression Level */ - - /* Audio DRC */ - i = 0; - if ( dynamic_range_compression ) - { - char * token = strtok(dynamic_range_compression, ","); - if (token == NULL) - token = dynamic_range_compression; - while ( token != NULL ) - { - d_r_c = atof(token); - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio != NULL ) - { - audio->out.dynamic_range_compression = d_r_c; - if( (++i) >= num_audio_tracks ) - break; /* We have more inputs than audio tracks, oops */ - } - else - { - fprintf(stderr, "Ignoring drc, no audio tracks\n"); - } - token = strtok(NULL, ","); - } - } - if (i < num_audio_tracks) - { - /* We have fewer inputs than audio tracks, use no DRC for the remaining - * tracks. Unless we only have one input, then use the same DRC for all - * tracks. - */ - if (i != 1) - d_r_c = 0; - for (; i < num_audio_tracks; i++) - { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.dynamic_range_compression = d_r_c; - } - } - /* Audio DRC */ - - /* Audio Gain */ - i = 0; - if ( audio_gain ) - { - char * token = strtok(audio_gain, ","); - if (token == NULL) - token = audio_gain; - while ( token != NULL ) - { - gain = atof(token); - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio != NULL ) - { - audio->out.gain = gain; - if( (++i) >= num_audio_tracks ) - break; /* We have more inputs than audio tracks, oops */ - } - else - { - fprintf(stderr, "Ignoring gain, no audio tracks\n"); - } - token = strtok(NULL, ","); - } - } - if (i < num_audio_tracks) - { - /* We have fewer inputs than audio tracks, use no gain for the remaining - * tracks. Unless we only have one input, then use the same gain for all - * tracks. - */ - if (i != 1) - gain = 0; - for (; i < num_audio_tracks; i++) - { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.gain = gain; - } - } - /* Audio Gain */ - - /* Audio Dither */ - if (audio_dither != NULL) - { - int dither_method = hb_audio_dither_get_default(); - for (i = 0; audio_dither[i] != NULL; i++) - { - dither_method = hb_audio_dither_get_from_name(audio_dither[i]); - audio = hb_list_audio_config_item(job->list_audio, i); - if (audio != NULL) - { - if (hb_audio_dither_is_supported(audio->out.codec)) - { - audio->out.dither_method = dither_method; - } - else if (dither_method != hb_audio_dither_get_default()) - { - fprintf(stderr, - "Ignoring dither %s, not supported by codec\n", - audio_dither[i]); - } - } - else - { - fprintf(stderr, "Ignoring dither %s, no audio tracks\n", - audio_dither[i]); - } - } - if (i < num_audio_tracks && i == 1) - { - /* - * We have fewer inputs than audio tracks, and we only have - * one input: use that for all tracks. - */ - while (i < num_audio_tracks) - { - audio = hb_list_audio_config_item(job->list_audio, i); - if (hb_audio_dither_is_supported(audio->out.codec)) - { - audio->out.dither_method = dither_method; - } - else if (dither_method != hb_audio_dither_get_default()) - { - fprintf(stderr, - "Ignoring dither %s, not supported by codec\n", - audio_dither[0]); - } - i++; - } - } - } - /* Audio Dither */ - - /* Audio Mix Normalization */ - i = 0; - int norm = 0; - if( normalize_mix_level ) - { - for ( i = 0; normalize_mix_level[i] != NULL && i < num_audio_tracks; i++ ) - { - char * token = normalize_mix_level[i]; - norm = atoi(token); - audio = hb_list_audio_config_item(job->list_audio, i); - - if( audio != NULL ) - { - audio->out.normalize_mix_level = norm; - } - else - { - fprintf(stderr, "Ignoring normalization %d, no audio tracks\n", norm); - } - } - } - if (i < num_audio_tracks && i == 1) - { - /* We have fewer inputs than audio tracks, - * and we only have one input, use - * that for all tracks. - */ - for (; i < num_audio_tracks; i++) - { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.normalize_mix_level = norm; - } - } - /* Audio Mix Normalization */ - - /* Audio Track Names */ - if ( anames ) - { - char * token; - for ( i = 0; anames[i] != NULL && i < num_audio_tracks; i++ ) - { - token = anames[i]; - if ( *token ) - { - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio != NULL ) - { - audio->out.name = strdup(token); - } - else - { - fprintf(stderr, "Ignoring aname '%s', no audio track\n", - token); - } - } - } - } - if( i < num_audio_tracks && i == 1 ) - { - /* We have exactly one name and more than one audio track. Use the same - * name for all tracks. */ - for ( ; i < num_audio_tracks; i++) - { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.name = strdup(anames[0]); - } - } - /* Audio Track Names */ - - /* Sanitize passthru (drop/fallback if necessary) */ - for( i = 0; i < hb_list_count( job->list_audio ); ) - { - audio = hb_list_audio_config_item( job->list_audio, i ); - if( audio->out.codec == HB_ACODEC_AUTO_PASS ) - { - // Auto Passthru - job->acodec_copy_mask = allowed_audio_copy == -1 ? HB_ACODEC_PASS_MASK : allowed_audio_copy; - job->acodec_fallback = hb_audio_encoder_get_from_name(acodec_fallback); - } - else if( ( audio->out.codec & HB_ACODEC_PASS_FLAG ) && - !( audio->out.codec & audio->in.codec & HB_ACODEC_PASS_MASK ) ) - { - // passthru fallbacks - int requested_passthru = audio->out.codec; - audio->out.codec = - hb_audio_encoder_get_fallback_for_passthru(requested_passthru); - if (!(audio->out.codec & HB_ACODEC_MASK)) - { - // Passthru not possible, drop audio. - fprintf(stderr, - "Passthru requested and input codec is not the same as output codec for track %d, dropping track\n", - audio->out.track); - hb_audio_t *item = hb_list_item(job->list_audio, i); - hb_list_rem(job->list_audio, item); - hb_audio_close(&item); - continue; - } - fprintf(stderr, - "%s requested and input codec is not compatible for track %d, using %s encoder\n", - hb_audio_encoder_get_name(requested_passthru), audio->out.track, - hb_audio_encoder_get_name(audio->out.codec)); - } - // we didn't drop the track - i++; - } - - if( subtracks ) - { - char * token; - int i; - int burnpos = 0, defaultpos = 0; - - if ( subburn ) - burnpos = strtol( subburn, NULL, 0 ); - if ( subdefault ) - defaultpos = strtol( subdefault, NULL, 0 ); - for ( i = 0; subtracks[i] != NULL; i++ ) - { - token = subtracks[i]; - if( strcasecmp(token, "scan" ) == 0 ) - { - int burn = 0, force = 0, def = 0; - - if ( subburn != NULL ) - { - burn = ( i == 0 && subburn[0] == 0 ) || - ( burnpos == i+1 ); - } - if ( subdefault != NULL ) - { - def = ( i == 0 && subdefault[0] == 0 ) || - ( defaultpos == i+1 ); - } - force = test_sub_list( subforce, i+1 ); - - if ( !burn ) - { - job->select_subtitle_config.dest = PASSTHRUSUB; - } - else - { - if ( sub_burned ) - { - continue; - } - sub_burned = 1; - } - job->select_subtitle_config.force = force; - job->select_subtitle_config.default_track = def; - subtitle_scan = 1; - } - else - { - hb_subtitle_t * subtitle; - hb_subtitle_config_t sub_config; - int track; - int burn = 0, force = 0, def = 0; - - track = atoi(token) - 1; - subtitle = hb_list_item(title->list_subtitle, track); - if( subtitle == NULL ) - { - fprintf(stderr, - "Warning: Could not find subtitle track '%s', skipped\n", - token); - continue; - } - sub_config = subtitle->config; - - if ( subburn != NULL ) - { - burn = ( i == 0 && subburn[0] == 0 ) || - ( burnpos == i+1 ); - } - if ( subdefault != NULL ) - { - def = ( i == 0 && subdefault[0] == 0 ) || - ( defaultpos == i+1 ); - } - - force = test_sub_list(subforce, i+1); - - int supports_burn = hb_subtitle_can_burn( subtitle->source ); - - if ( ( burn && supports_burn ) || - !hb_subtitle_can_pass( subtitle->source, mux ) ) - { - // Only allow one subtitle to be burned into video - if ( sub_burned ) - { - fprintf( stderr, "Warning: Skipping subtitle track %d, can't have more than one track burnt in\n", track+1 ); - continue; - } - sub_burned = 1; - - // Mark as burn-in - sub_config.dest = RENDERSUB; - } - else - { - sub_config.dest = PASSTHRUSUB; - } - sub_config.force = force; - sub_config.default_track = def; - hb_subtitle_add( job, &sub_config, track ); - } - } - } - - if( srtfile ) - { - int i; - hb_subtitle_config_t sub_config; - - for( i=0; srtfile[i] != NULL; i++ ) - { - char *codeset = "L1"; - int64_t offset = 0; - char *lang = "und"; - - if (srtburn == i + 1 && hb_subtitle_can_burn(SRTSUB)) - { - // Only allow one subtitle to be burned into video - if ( sub_burned ) - { - fprintf( stderr, "Warning: Skipping SRT track %d, can't have more than one track burnt in\n", i+1 ); - continue; - } - sub_burned = 1; - - // Mark as burn-in - sub_config.dest = RENDERSUB; - } - else - { - sub_config.dest = PASSTHRUSUB; - } - if( srtcodeset && srtcodeset[i] ) - { - codeset = srtcodeset[i]; - } - if( srtoffset && srtoffset[i] ) - { - offset = strtoll( srtoffset[i], &srtoffset[i], 0 ); - } - if ( srtlang && srtlang[i] ) - { - lang = srtlang[i]; - } - sub_config.default_track = srtdefault == i + 1; - sub_config.force = 0; - strncpy( sub_config.src_filename, srtfile[i], 255); - sub_config.src_filename[255] = 0; - strncpy( sub_config.src_codeset, codeset, 39); - sub_config.src_codeset[39] = 0; - sub_config.offset = offset; - - hb_srt_add( job, &sub_config, lang); - } - } - - if ( sub_burned ) - { - char * filter_str; - filter_str = hb_strdup_printf("%d:%d:%d:%d", - crop[0], crop[1], crop[2], crop[3] ); - filter = hb_filter_init( HB_FILTER_RENDER_SUB ); - hb_add_filter( job, filter, filter_str); - free( filter_str ); - } - - if( native_language ) - { - audio = hb_list_audio_config_item(job->list_audio, 0); - - if( audio ) - { - if( !cmp_lang( native_language, audio->lang.iso639_2 ) ) - { - /* - * Audio language is not the same as our native language. - * If we have any subtitles in our native language they - * should be selected here if they haven't already been. - */ - hb_subtitle_t *subtitle, *subtitle2 = NULL; - int matched_track = 0; - - for( i = 0; i < hb_list_count( title->list_subtitle ); i++ ) - { - subtitle = hb_list_item( title->list_subtitle, i ); - matched_track = i; - if (cmp_lang(native_language, subtitle->iso639_2)) - { - /* - * Found the first matching subtitle in our - * native language. Is it already selected? - */ - for( i = 0; i < hb_list_count( job->list_subtitle ); i++ ) - { - subtitle2 = hb_list_item( job->list_subtitle, i ); - - if( subtitle2->track == subtitle->track) { - /* - * Already selected - */ - break; - } - subtitle2 = NULL; - } - - if( subtitle2 == NULL ) - { - /* - * Not already selected, so select it. - */ - hb_subtitle_config_t sub_config; - - if( native_dub ) - { - fprintf( stderr, "Warning: no matching audio for native language - using subtitles instead.\n"); - } - sub_config = subtitle->config; - - if ((mux & HB_MUX_MASK_MKV) || subtitle->format == TEXTSUB) - { - sub_config.dest = PASSTHRUSUB; - } - - sub_config.force = 0; - sub_config.default_track = 1; - hb_subtitle_add( job, &sub_config, matched_track); - } - /* - * Stop searching. - */ - break; - } - } - } - } + die = 1; + return -1; } - hb_job_set_file( job, output ); - - if( color_matrix_code ) + hb_job_t * job = NULL; + job = hb_dict_to_job(h, job_dict); + hb_value_free(&job_dict); + if (job == NULL) { - job->color_matrix_code = color_matrix_code; - } - - hb_job_set_encoder_preset (job, x264_preset); - hb_job_set_encoder_tune (job, x264_tune); - hb_job_set_encoder_profile(job, h264_profile); - hb_job_set_encoder_level (job, h264_level); - - if( start_at_preview ) - { - job->start_at_preview = start_at_preview - 1; - job->seek_points = preview_count; - } - - if( stop_at_pts ) - { - job->pts_to_stop = stop_at_pts; - subtitle_scan = 0; - } - - if( stop_at_frame ) - { - job->frame_to_stop = stop_at_frame; - subtitle_scan = 0; - } - - if( start_at_pts ) - { - job->pts_to_start = start_at_pts; - subtitle_scan = 0; - } - - if( start_at_frame ) - { - job->frame_to_start = start_at_frame; - subtitle_scan = 0; + fprintf(stderr, "Error in setting up job! Aborting.\n"); + die = 1; + return -1; } - /* OpenCL */ - job->use_opencl = use_opencl; - - job->indepth_scan = subtitle_scan; - job->twopass = twoPass; - job->fastfirstpass = fastfirstpass; - hb_job_set_encoder_options(job, advanced_opts); - hb_add( h, job ); hb_job_close( &job ); hb_start( h ); @@ -2829,7 +821,7 @@ static int HandleEvents( hb_handle_t * h ) p.pass, p.pass_count, 100.0 * p.progress ); if( p.seconds > -1 ) { - fprintf( stdout, " (ETA %02dh%02dm%02ds)", + fprintf( stdout, " (ETA %02dh%02dm%02ds)", p.hours, p.minutes, p.seconds ); } fflush(stdout); @@ -2911,7 +903,6 @@ void SigHandler( int i_signal ) static void ShowHelp() { int i; - const char *name; const hb_rate_t *rate; const hb_dither_t *dither; const hb_mixdown_t *mixdown; @@ -2920,45 +911,57 @@ static void ShowHelp() FILE* const out = stdout; fprintf( out, - "Syntax: HandBrakeCLI [options] -i <device> -o <file>\n" - "\n" - "### General Handbrake Options------------------------------------------------\n\n" - " -h, --help Print help\n" - " -u, --update Check for updates and exit\n" - " -v, --verbose <#> Be verbose (optional argument: logging level)\n" - " -Z. --preset <string> Use a built-in preset. Capitalization matters, and\n" - " if the preset name has spaces, surround it with\n" - " double quotation marks\n" - " -z, --preset-list See a list of available built-in presets\n" - " --no-dvdnav Do not use dvdnav for reading DVDs\n" - " --no-opencl Disable use of OpenCL\n" - "\n" - - "### Source Options-----------------------------------------------------------\n\n" - " -i, --input <string> Set input device\n" - " -t, --title <number> Select a title to encode (0 to scan all titles only,\n" - " default: 1)\n" - " --min-duration Set the minimum title duration (in seconds). Shorter\n" - " titles will not be scanned (default: 10).\n" - " --scan Scan selected title only.\n" - " --main-feature Detect and select the main feature title.\n" - " -c, --chapters <string> Select chapters (e.g. \"1-3\" for chapters\n" - " 1 to 3, or \"3\" for chapter 3 only,\n" - " default: all chapters)\n" - " --angle <number> Select the video angle (DVD or Blu-ray only)\n" - " --previews <#:B> Select how many preview images are generated,\n" - " and whether or not they're stored to disk (0 or 1).\n" - " (default: 10:0)\n" - " --start-at-preview <#> Start encoding at a given preview.\n" - " --start-at <unit:#> Start encoding at a given frame, duration (in seconds),\n" - " or pts (on a 90kHz clock)\n" - " --stop-at <unit:#> Stop encoding at a given frame, duration (in seconds),\n" - " or pts (on a 90kHz clock)" - "\n" - - "### Destination Options------------------------------------------------------\n\n" - " -o, --output <string> Set output file name\n" - " -f, --format <string> Set output container format ("); +"Syntax: HandBrakeCLI [options] -i <device> -o <file>\n" +"\n" +"### General Handbrake Options---------------------------------------------\n\n" +" -h, --help Print help\n" +" -u, --update Check for updates and exit\n" +" -v, --verbose <#> Be verbose (optional argument: logging level)\n" +" -Z. --preset <string> Use a built-in preset. Capitalization matters,\n" +" and if the preset name has spaces, surround it\n" +" with double quotation marks\n" +" -z, --preset-list See a list of available built-in presets\n" +" --preset-import-file Import presets from a json preset file.\n" +" <filespec> 'filespec' may be a list of files separated\n" +" by spaces, or it may use shell wildcards.\n" +" --preset-import-gui Import presets from GUI config preset file.\n" +" --preset-export Create a new preset from command line options and\n" +" <name> write a json representation of the preset to the\n" +" console or a file if '--preset-export-file' is\n" +" specified. The required 'name' argument will be\n" +" the new preset's name.\n" +" --preset-export-file Write new preset generated by '--preset-export'\n" +" <filename> to file 'filename'.\n" +" --preset-export-description\n" +" <description> Add a description to the new preset created with\n" +" '--preset-export'\n" +" --no-dvdnav Do not use dvdnav for reading DVDs\n" +" --no-opencl Disable use of OpenCL\n" +"\n" +"### Source Options--------------------------------------------------------\n\n" +" -i, --input <string> Set input device\n" +" -t, --title <number> Select a title to encode (0 to scan all titles\n" +" only, default: 1)\n" +" --min-duration Set the minimum title duration (in seconds).\n" +" Shorter titles will be ignored (default: 10).\n" +" --scan Scan selected title only.\n" +" --main-feature Detect and select the main feature title.\n" +" -c, --chapters <string> Select chapters (e.g. \"1-3\" for chapters\n" +" 1 to 3, or \"3\" for chapter 3 only,\n" +" default: all chapters)\n" +" --angle <number> Select the video angle (DVD or Blu-ray only)\n" +" --previews <#:B> Select how many preview images are generated,\n" +" and whether to store to disk (0 or 1).\n" +" (default: 10:0)\n" +" --start-at-preview <#> Start encoding at a given preview.\n" +" --start-at <unit:#> Start encoding at a given frame, duration\n" +" (in seconds), or pts (on a 90kHz clock)\n" +" --stop-at <unit:#> Stop encoding at a given frame, duration\n" +" (in seconds), or pts (on a 90kHz clock)" +"\n" +"### Destination Options---------------------------------------------------\n\n" +" -o, --output <string> Set output file name\n" +" -f, --format <string> Set output container format ("); container = NULL; while ((container = hb_container_get_next(container)) != NULL) { @@ -2973,19 +976,23 @@ static void ShowHelp() } } fprintf(out, - " (default: autodetected from file name)\n" - " -m, --markers Add chapter markers\n" - " -O, --optimize Optimize mp4 files for HTTP streaming (\"fast start\")\n" - " -I, --ipod-atom Mark mp4 files so 5.5G iPods will accept them\n" - " -P, --use-opencl Use OpenCL where applicable\n" - " -U, --use-hwd Use DXVA2 hardware decoding\n" - "\n" - - - "### Video Options------------------------------------------------------------\n\n" - " -e, --encoder <string> Set video library encoder\n" - " Options: " ); - name = NULL; +" (default: autodetected from file name)\n" +" -m, --markers Add chapter markers\n" +" --no-markers Disable preset chapter markers\n" +" -O, --optimize Optimize mp4 files for HTTP streaming\n" +" (\"fast start\")\n" +" --no-optimize Disable preset 'optimize'\n" +" -I, --ipod-atom Mark mp4 files so 5.5G iPods will accept them\n" +" --no-ipod-atom Disable 5.5G iPod tag\n" +" -P, --use-opencl Use OpenCL where applicable\n" +" -U, --use-hwd Use DXVA2 hardware decoding\n" +" --no-hwd Disable DXVA2 hardware decoding\n" +"\n" + + +"### Video Options------------------------------------------------------------\n\n" +" -e, --encoder <string> Set video library encoder\n" +" Options: " ); encoder = NULL; while ((encoder = hb_video_encoder_get_next(encoder)) != NULL) { @@ -2998,38 +1005,35 @@ static void ShowHelp() { fprintf(out, "\n"); } - if (encoder->codec == vcodec) - { - name = encoder->short_name; - } } - fprintf(out, " (default: %s)\n", name); fprintf(out, - " --encoder-preset Adjust video encoding settings for a particular\n" - " <string> speed/efficiency tradeoff (encoder-specific)\n" - " --encoder-preset-list List supported --encoder-preset values for the\n" - " <string> specified video encoder\n" - " --encoder-tune Adjust video encoding settings for a particular\n" - " <string> type of souce or situation (encoder-specific)\n" - " --encoder-tune-list List supported --encoder-tune values for the\n" - " <string> specified video encoder\n" - " -x, --encopts <string> Specify advanced encoding options in the same\n" - " style as mencoder (all encoders except theora):\n" - " option1=value1:option2=value2\n" - " --encoder-profile Ensures compliance with the requested codec\n" - " <string> profile (encoder-specific)\n" - " --encoder-profile-list List supported --encoder-profile values for the\n" - " <string> specified video encoder\n" - " --encoder-level Ensures compliance with the requested codec\n" - " <string> level (encoder-specific)\n" - " --encoder-level-list List supported --encoder-level values for the\n" - " <string> specified video encoder\n" - " -q, --quality <number> Set video quality\n" - " -b, --vb <kb/s> Set video bitrate (default: 1000)\n" - " -2, --two-pass Use two-pass mode\n" - " -T, --turbo When using 2-pass use \"turbo\" options on the\n" - " 1st pass to improve speed (only works with x264)\n" - " -r, --rate Set video framerate (" ); +" --encoder-preset Adjust video encoding settings for a particular\n" +" <string> speed/efficiency tradeoff (encoder-specific)\n" +" --encoder-preset-list List supported --encoder-preset values for the\n" +" <string> specified video encoder\n" +" --encoder-tune Adjust video encoding settings for a particular\n" +" <string> type of souce or situation (encoder-specific)\n" +" --encoder-tune-list List supported --encoder-tune values for the\n" +" <string> specified video encoder\n" +" -x, --encopts <string> Specify advanced encoding options in the same\n" +" style as mencoder (all encoders except theora):\n" +" option1=value1:option2=value2\n" +" --encoder-profile Ensures compliance with the requested codec\n" +" <string> profile (encoder-specific)\n" +" --encoder-profile-list List supported --encoder-profile values for the\n" +" <string> specified video encoder\n" +" --encoder-level Ensures compliance with the requested codec\n" +" <string> level (encoder-specific)\n" +" --encoder-level-list List supported --encoder-level values for the\n" +" <string> specified video encoder\n" +" -q, --quality <number> Set video quality\n" +" -b, --vb <kb/s> Set video bitrate (default: 1000)\n" +" -2, --two-pass Use two-pass mode\n" +" -T, --turbo When using 2-pass use \"turbo\" options on the\n" +" 1st pass to improve speed\n" +" (works with x264 and x265)\n" +" -r, --rate Set video framerate\n" +" (" ); rate = NULL; while ((rate = hb_video_framerate_get_next(rate)) != NULL) { @@ -3040,27 +1044,39 @@ static void ShowHelp() } } fprintf( out, ")\n" - " Be aware that not specifying a framerate lets\n" - " HandBrake preserve a source's time stamps,\n" - " potentially creating variable framerate video\n" - " --vfr, --cfr, --pfr Select variable, constant or peak-limited\n" - " frame rate control. VFR preserves the source\n" - " timing. CFR makes the output constant rate at\n" - " the rate given by the -r flag (or the source's\n" - " average rate if no -r is given). PFR doesn't\n" - " allow the rate to go over the rate specified\n" - " with the -r flag but won't change the source\n" - " timing if it's below that rate.\n" - " If none of these flags are given, the default\n" - " is --cfr when -r is given and --vfr otherwise\n" - - "\n" - "### Audio Options-----------------------------------------------------------\n\n" - " -a, --audio <string> Select audio track(s), separated by commas\n" - " (\"none\" for no audio, \"1,2,3\" for multiple\n" - " tracks, default: first one).\n" - " Multiple output tracks can be used for one input.\n" - " -E, --aencoder <string> Audio encoder(s):\n" ); +" Be aware that not specifying a framerate lets\n" +" HandBrake preserve a source's time stamps,\n" +" potentially creating variable framerate video\n" +" --vfr, --cfr, --pfr Select variable, constant or peak-limited\n" +" frame rate control. VFR preserves the source\n" +" timing. CFR makes the output constant rate at\n" +" the rate given by the -r flag (or the source's\n" +" average rate if no -r is given). PFR doesn't\n" +" allow the rate to go over the rate specified\n" +" with the -r flag but won't change the source\n" +" timing if it's below that rate.\n" +" If none of these flags are given, the default\n" +" is --cfr when -r is given and --vfr otherwise\n" +"\n" +"### Audio Options---------------------------------------------------------\n\n" +" --audio-lang-list Specifiy a comma separated list of audio\n" +" <string> languages you would like to select from the\n" +" source title. By default, the first audio\n" +" matching each language will be added to your\n" +" output. Provide the language's iso639-2 code\n" +" (fre, eng, spa, dut, et cetera)\n" +" Use code 'und' (Unknown) to match all languages.\n" +" --all-audio Select all audio tracks matching languages in\n" +" the specified language list (--audio-lang-list).\n" +" Any language if list is not specified.\n" +" --first-audio Select first audio track matching languages in\n" +" the specified language list (--audio-lang-list).\n" +" Any language if list is not specified.\n" +" -a, --audio <string> Select audio track(s), separated by commas\n" +" (\"none\" for no audio, \"1,2,3\" for multiple\n" +" tracks, default: first one).\n" +" Multiple output tracks can be used for one input.\n" +" -E, --aencoder <string> Audio encoder(s):\n" ); encoder = NULL; while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) { @@ -3068,11 +1084,11 @@ static void ShowHelp() encoder->short_name); } fprintf(out, - " copy:* will passthrough the corresponding\n" - " audio unmodified to the muxer if it is a\n" - " supported passthrough audio type.\n" - " Separated by commas for more than one audio track.\n" - " Defaults:\n"); +" copy:* will passthrough the corresponding\n" +" audio unmodified to the muxer if it is a\n" +" supported passthrough audio type.\n" +" Separate tracks by commas.\n" +" Defaults:\n"); container = NULL; while ((container = hb_container_get_next(container)) != NULL) { @@ -3082,9 +1098,9 @@ static void ShowHelp() hb_audio_encoder_get_short_name(audio_encoder)); } fprintf(out, - " --audio-copy-mask Set audio codecs that are permitted when the\n" - " <string> \"copy\" audio encoder option is specified\n" - " (" ); +" --audio-copy-mask Set audio codecs that are permitted when the\n" +" <string> \"copy\" audio encoder option is specified\n" +" (" ); i = 0; encoder = NULL; while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) @@ -3101,20 +1117,19 @@ static void ShowHelp() fprintf(out, "%s", encoder->short_name + 5); } } - fprintf(out, ", default: all).\n" - " Separated by commas for multiple allowed options.\n" - " --audio-fallback Set audio codec to use when it is not possible\n" - " <string> to copy an audio track without re-encoding.\n" - " -B, --ab <kb/s> Set audio bitrate(s) (default: depends on the\n" - " selected codec, mixdown and samplerate)\n" - " Separated by commas for more than one audio track.\n" - " -Q, --aq <quality> Set audio quality metric (default: depends on the\n" - " selected codec)\n" - " Separated by commas for more than one audio track.\n" - " -C, --ac <compression> Set audio compression metric (default: depends on the\n" - " selected codec)\n" - " Separated by commas for more than one audio track.\n" - " -6, --mixdown <string> Format(s) for audio downmixing/upmixing:\n"); + fprintf(out, ")\n" +" Separated by commas for multiple allowed options.\n" +" --audio-fallback Set audio codec to use when it is not possible\n" +" <string> to copy an audio track without re-encoding.\n" +" -B, --ab <kb/s> Set audio bitrate(s) (default: depends on the\n" +" selected codec, mixdown and samplerate)\n" +" Separate tracks by commas.\n" +" -Q, --aq <quality> Set audio quality metric.\n" +" Separate tracks by commas.\n" +" -C, --ac <compression> Set audio compression metric.\n" +" selected codec)\n" +" Separate tracks by commas.\n" +" -6, --mixdown <string> Format(s) for audio downmixing/upmixing:\n"); // skip HB_AMIXDOWN_NONE mixdown = hb_mixdown_get_next(NULL); while((mixdown = hb_mixdown_get_next(mixdown)) != NULL) @@ -3123,8 +1138,8 @@ static void ShowHelp() mixdown->short_name); } fprintf(out, - " Separated by commas for more than one audio track.\n" - " Defaults:\n"); +" Separate tracks by commas.\n" +" Defaults:\n"); encoder = NULL; while((encoder = hb_audio_encoder_get_next(encoder)) != NULL) { @@ -3138,11 +1153,12 @@ static void ShowHelp() } } fprintf(out, - " --normalize-mix Normalize audio mix levels to prevent clipping.\n" - " <string> Separated by commas for more than one audio track.\n" - " 0 = Disable Normalization (default)\n" - " 1 = Enable Normalization\n" - " -R, --arate Set audio samplerate(s) (" ); +" --normalize-mix Normalize audio mix levels to prevent clipping.\n" +" <string> Separate tracks by commas.\n" +" 0 = Disable Normalization (default)\n" +" 1 = Enable Normalization\n" +" -R, --arate Set audio samplerate(s)\n" +" (" ); rate = NULL; while ((rate = hb_audio_samplerate_get_next(rate)) != NULL) { @@ -3153,18 +1169,21 @@ static void ShowHelp() } } fprintf( out, " kHz)\n" - " Separated by commas for more than one audio track.\n" - " -D, --drc <float> Apply extra dynamic range compression to the audio,\n" - " making soft sounds louder. Range is 1.0 to 4.0\n" - " (too loud), with 1.5 - 2.5 being a useful range.\n" - " Separated by commas for more than one audio track.\n" - " --gain <float> Amplify or attenuate audio before encoding. Does\n" - " NOT work with audio passthru (copy). Values are in\n" - " dB. Negative values attenuate, positive values\n" - " amplify. A 1 dB difference is barely audible.\n" - " --adither <string> Apply dithering to the audio before encoding.\n" - " Separated by commas for more than one audio track.\n" - " Only supported by some encoders ("); +" Separate tracks by commas.\n" +" -D, --drc <float> Apply extra dynamic range compression to the\n" +" audio, making soft sounds louder. Range is 1.0\n" +" to 4.0 (too loud), with 1.5 - 2.5 being a useful\n" +" range.\n" +" Separate tracks by commas.\n" +" --gain <float> Amplify or attenuate audio before encoding. Does\n" +" NOT work with audio passthru (copy). Values are\n" +" in dB. Negative values attenuate, positive\n" +" values amplify. A 1 dB difference is barely\n" +" audible.\n" +" --adither <string> Apply dithering to the audio before encoding.\n" +" Separate tracks by commas.\n" +" Only supported by some encoders\n" +" ("); i = 0; encoder = NULL; while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) @@ -3197,45 +1216,46 @@ static void ShowHelp() } } fprintf(out, - " -A, --aname <string> Audio track name(s),\n" - " Separated by commas for more than one audio track.\n" - "\n" - - "### Picture Settings---------------------------------------------------------\n\n" - " -w, --width <number> Set picture width\n" - " -l, --height <number> Set picture height\n" - " --crop <T:B:L:R> Set cropping values (default: autocrop)\n" - " --loose-crop <#> Always crop to a multiple of the modulus\n" - " Specifies the maximum number of extra pixels\n" - " which may be cropped (default: 15)\n" - " -Y, --maxHeight <#> Set maximum height\n" - " -X, --maxWidth <#> Set maximum width\n" - " --strict-anamorphic Store pixel aspect ratio in video stream\n" - " --loose-anamorphic Store pixel aspect ratio with specified width\n" - " --custom-anamorphic Store pixel aspect ratio in video stream and\n" - " directly control all parameters.\n" - " --display-width Set the width to scale the actual pixels to\n" - " <number> at playback, for custom anamorphic.\n" - " --keep-display-aspect Preserve the source's display aspect ratio\n" - " when using custom anamorphic\n" - " --pixel-aspect Set a custom pixel aspect for custom anamorphic\n" - " <PARX:PARY>\n" - " (--display-width and --pixel-aspect are mutually\n" - " exclusive and the former will override the latter)\n" - " --itu-par Use wider, ITU pixel aspect values for loose and\n" - " custom anamorphic, useful with underscanned sources\n" - " --modulus Set the number you want the scaled pixel dimensions\n" - " <number> to divide cleanly by. Does not affect strict\n" - " anamorphic mode, which is always mod 2 (default: 16)\n" - " -M, --color-matrix Set the color space signaled by the output\n" - " Values: 709, pal, ntsc, 601 (same as ntsc)\n" - " (default: detected from source)\n" - "\n" - - "### Filters---------------------------------------------------------\n\n" - - " -d, --deinterlace Unconditionally deinterlaces all frames\n" - " <fast/slow/slower/bob"); +" -A, --aname <string> Audio track name(s),\n" +" Separate tracks by commas.\n" +"\n" +"### Picture Settings------------------------------------------------------\n\n" +" -w, --width <number> Set picture width\n" +" -l, --height <number> Set picture height\n" +" --crop <T:B:L:R> Set cropping values (default: autocrop)\n" +" --loose-crop Always crop to a multiple of the modulus\n" +" --no-loose-crop Disable preset 'loose-crop'\n" +" -Y, --maxHeight <#> Set maximum height\n" +" -X, --maxWidth <#> Set maximum width\n" +" --non-anamorphic Set pixel aspect ratio to 1:1\n" +" --strict-anamorphic Store pixel aspect ratio in video stream\n" +" --loose-anamorphic Store pixel aspect ratio with specified width\n" +" --custom-anamorphic Store pixel aspect ratio in video stream and\n" +" directly control all parameters.\n" +" --display-width Set the width to scale the actual pixels to\n" +" <number> at playback, for custom anamorphic.\n" +" --keep-display-aspect Preserve the source's display aspect ratio\n" +" when using custom anamorphic\n" +" --no-keep-display-aspect Disable preset 'keep-display-aspect'\n" +" --pixel-aspect Set a custom pixel aspect for custom anamorphic\n" +" <PARX:PARY> (--display-width and --pixel-aspect are mutually\n" +" exclusive.\n" +" --itu-par Use wider, ITU pixel aspect values for loose and\n" +" custom anamorphic, useful with underscanned\n" +" sources\n" +" --no-itu-par Disable preset 'itu-par'\n" +" --modulus Set the number you want the scaled pixel\n" +" dimensions\n" +" <number> to divide cleanly by. Does not affect strict\n" +" anamorphic mode, which is always mod 2\n" +" (default: 16)\n" +" -M, --color-matrix Set the color space signaled by the output\n" +" Values: 709, pal, ntsc, 601 (same as ntsc)\n" +" (default: detected from source)\n" +"\n" +"### Filters---------------------------------------------------------------\n\n" +" -d, --deinterlace Unconditionally deinterlaces all frames\n" +" <fast/slow/slower/bob"); #ifdef USE_QSV if (hb_qsv_available()) { @@ -3244,116 +1264,149 @@ if (hb_qsv_available()) #endif fprintf( out, "> or omitted (default settings)\n" " or\n" - " <YM:FD> (default 0:-1)\n" - " -5, --decomb Selectively deinterlaces when it detects combing\n" - " <fast/bob> or omitted (default settings)\n" - " or\n" - " <MO:ME:MT:ST:BT:BX:BY:MG:VA:LA:DI:ER:NO:MD:PP:FD>\n" - " (default: 7:2:6:9:80:16:16:10:20:20:4:2:50:24:1:-1)\n" - " -9, --detelecine Detelecine (ivtc) video with pullup filter\n" - " Note: this filter drops duplicate frames to\n" - " restore the pre-telecine framerate, unless you\n" - " specify a constant framerate (--rate 29.97)\n" - " <L:R:T:B:SB:MP:FD> (default 1:1:4:4:0:0:-1)\n" - " -8, --denoise Denoise video with hqdn3d filter\n" - " <ultralight/light/medium/strong> or omitted (default settings)\n" - " or\n" - " <SL:SCb:SCr:TL:TCb:TCr>\n" - " (default: 4:3:3:6:4.5:4.5)\n" - " --nlmeans Denoise video with nlmeans filter\n" - " <ultralight/light/medium/strong> or omitted\n" - " or\n" - " <SY:OTY:PSY:RY:FY:PY:Sb:OTb:PSb:Rb:Fb:Pb:Sr:OTr:PSr:Rr:Fr:Pr>\n" - " (default 8:1:7:3:2:0)\n" - " --nlmeans-tune Tune nlmeans filter to content type\n" - " Note: only works in conjunction with presets\n" - " ultralight/light/medium/strong.\n" - " <none/film/grain/highmotion/animation> or omitted (default none)\n" - " -7, --deblock Deblock video with pp7 filter\n" - " <QP:M> (default 5:2)\n" - " --rotate <mode> Rotate image or flip its axes.\n" - " Modes: (can be combined)\n" - " 1 vertical flip\n" - " 2 horizontal flip\n" - " 4 rotate clockwise 90 degrees\n" - " Default: 3 (vertical and horizontal flip)\n" - " -g, --grayscale Grayscale encoding\n" - "\n" - - "### Subtitle Options------------------------------------------------------------\n\n" - " -s, --subtitle <string> Select subtitle track(s), separated by commas\n" - " More than one output track can be used for one\n" - " input.\n" - " Example: \"1,2,3\" for multiple tracks.\n" - " A special track name \"scan\" adds an extra 1st pass.\n" - " This extra pass scans subtitles matching the\n" - " language of the first audio or the language \n" - " selected by --native-language.\n" - " The one that's only used 10 percent of the time\n" - " or less is selected. This should locate subtitles\n" - " for short foreign language segments. Best used in\n" - " conjunction with --subtitle-forced.\n" - " -F, --subtitle-forced Only display subtitles from the selected stream if\n" - " <string> the subtitle has the forced flag set. The values in\n" - " \"string\" are indexes into the subtitle list\n" - " specified with '--subtitle'.\n" - " Separated by commas for more than one subtitle track.\n" - " Example: \"1,2,3\" for multiple tracks.\n" - " If \"string\" is omitted, the first track is forced.\n" - " --subtitle-burned \"Burn\" the selected subtitle into the video track\n" - " <number> If \"number\" is omitted, the first track is burned.\n" - " \"number\" is an index into the subtitle list\n" - " specified with '--subtitle'.\n" - " --subtitle-default Flag the selected subtitle as the default subtitle\n" - " <number> to be displayed upon playback. Setting no default\n" - " means no subtitle will be automatically displayed\n" - " If \"number\" is omitted, the first track is default.\n" - " \"number\" is an index into the subtitle list\n" - " specified with '--subtitle'.\n" - " -N, --native-language Specifiy your language preference. When the first\n" - " <string> audio track does not match your native language then\n" - " select the first subtitle that does. When used in\n" - " conjunction with --native-dub the audio track is\n" - " changed in preference to subtitles. Provide the\n" - " language's iso639-2 code (fre, eng, spa, dut, et cetera)\n" - " --native-dub Used in conjunction with --native-language\n" - " requests that if no audio tracks are selected the\n" - " default selected audio track will be the first one\n" - " that matches the --native-language. If there are no\n" - " matching audio tracks then the first matching\n" - " subtitle track is used instead.\n" - " --srt-file <string> SubRip SRT filename(s), separated by commas.\n" - " --srt-codeset Character codeset(s) that the SRT file(s) are\n" - " <string> encoded in, separated by commas.\n" - " Use 'iconv -l' for a list of valid\n" - " codesets. If not specified, 'latin1' is assumed\n" - " --srt-offset Offset (in milliseconds) to apply to the SRT file(s),\n" - " <string> separated by commas. If not specified, zero is assumed.\n" - " Offsets may be negative.\n" - " --srt-lang <string> Language as an iso639-2 code fra, eng, spa et cetera)\n" - " for the SRT file(s), separated by commas. If not specified,\n" - " then 'und' is used.\n" - " --srt-default Flag the selected srt as the default subtitle\n" - " <number> to be displayed upon playback. Setting no default\n" - " means no subtitle will be automatically displayed\n" - " If \"number\" is omitted, the first srt is default.\n" - " \"number\" is an 1 based index into the srt-file list\n" - " --srt-burn \"Burn\" the selected srt subtitle into the video track\n" - " <number> If \"number\" is omitted, the first srt is burned.\n" - " \"number\" is an 1 based index into the srt-file list\n" - "\n" +" <YM:FD> (default 0:-1)\n" +" --no-deinterlace Disable preset deinterlace filter\n" +" -5, --decomb Selectively deinterlaces when it detects combing\n" +" <fast/bob> or omitted (default settings)\n" +" or\n" +" <MO:ME:MT:ST:BT:BX:BY:MG:VA:LA:DI:ER:NO:MD:PP:FD>\n" +" (default: 7:2:6:9:80:16:16:10:20:20:4:2:50:24:1:-1)\n" +" --no-decomb Disable preset decomb filter\n" +" -9, --detelecine Detelecine (ivtc) video with pullup filter\n" +" Note: this filter drops duplicate frames to\n" +" restore the pre-telecine framerate, unless you\n" +" specify a constant framerate (--rate 29.97)\n" +" <L:R:T:B:SB:MP:FD> (default 1:1:4:4:0:0:-1)\n" +" --no-detelecine Disable preset detelecine filter\n" +" -8, --hqdn3d Denoise video with hqdn3d filter\n" +" <ultralight/light/medium/strong> or omitted (default settings)\n" +" or\n" +" <SL:SCb:SCr:TL:TCb:TCr>\n" +" (default: 4:3:3:6:4.5:4.5)\n" +" --no-hqdn3d Disable preset hqdn3d filter\n" +" --denoise Legacy alias for '--hqdn3d'\n" +" --nlmeans Denoise video with nlmeans filter\n" +" <ultralight/light/medium/strong> or omitted\n" +" or\n" +" <SY:OTY:PSY:RY:FY:PY:Sb:OTb:PSb:Rb:Fb:Pb:Sr:OTr:PSr:Rr:Fr:Pr>\n" +" (default 8:1:7:3:2:0)\n" +" --no-nlmeans Disable preset nlmeans filter\n" +" --nlmeans-tune Tune nlmeans filter to content type\n" +" Note: only works in conjunction with presets\n" +" ultralight/light/medium/strong.\n" +" <none/film/grain/highmotion/animation> or omitted (default none)\n" +" -7, --deblock Deblock video with pp7 filter\n" +" <QP:M> (default 5:2)\n" +" --no-deblock Disable preset deblock filter\n" +" --rotate <mode> Rotate image or flip its axes.\n" +" Modes: (can be combined)\n" +" 0 disable rotate\n" +" 1 vertical flip\n" +" 2 horizontal flip\n" +" 4 rotate clockwise 90 degrees\n" +" More Examples:\n" +" 3 horiz + vert (aka rotate 180')\n" +" 7 horiz + vert + 90' (aka rotate 270')\n" +" Default: 3 (vertical and horizontal flip)\n" +" -g, --grayscale Grayscale encoding\n" +" --no-grayscale Disable preset 'grayscale'\n" +"\n" +"### Subtitle Options------------------------------------------------------\n\n" +" --subtitle-lang-list Specifiy a comma separated list of subtitle\n" +" <string> languages you would like to select from the\n" +" source title. By default, the first subtitle\n" +" matching each language will be added to your\n" +" output. Provide the language's iso639-2 code\n" +" (fre, eng, spa, dut, et cetera)\n" +" --all-subtitles Select all subtitle tracks matching languages in\n" +" the specified language list\n" +" (--subtitle-lang-list).\n" +" Any language if list is not specified.\n" +" --first-subtitle Select first subtitle track matching languages in\n" +" the specified language list\n" +" (--subtitle-lang-list).\n" +" Any language if list is not specified.\n" +" -s, --subtitle <string> Select subtitle track(s), separated by commas\n" +" More than one output track can be used for one\n" +" input. \"none\" for no subtitles.\n" +" Example: \"1,2,3\" for multiple tracks.\n" +" A special track name \"scan\" adds an extra 1st\n" +" pass. This extra pass scans subtitles matching\n" +" the language of the first audio or the language \n" +" selected by --native-language.\n" +" The one that's only used 10 percent of the time\n" +" or less is selected. This should locate subtitles\n" +" for short foreign language segments. Best used in\n" +" conjunction with --subtitle-forced.\n" +" -F, --subtitle-forced Only display subtitles from the selected stream\n" +" <string> if the subtitle has the forced flag set. The\n" +" values in \"string\" are indexes into the\n" +" subtitle list specified with '--subtitle'.\n" +" Separate tracks by commas.\n" +" Example: \"1,2,3\" for multiple tracks.\n" +" If \"string\" is omitted, the first track is\n" +" forced.\n" +" --subtitle-burned \"Burn\" the selected subtitle into the video\n" +" <subtitle> track. If \"subtitle\" is omitted, the first\n" +" track is burned. \"subtitle\" is an index into\n" +" the subtitle list specified with '--subtitle'\n" +" or \"native\" to burn the subtitle track that may\n" +" be added by the 'native-language' option.\n" +" --subtitle-default Flag the selected subtitle as the default\n" +" <number> subtitle to be displayed upon playback. Setting\n" +" no default means no subtitle will be displayed\n" +" automatically. \"number\" is an index into the\n" +" subtitle list specified with '--subtitle'.\n" +" -N, --native-language Specifiy your language preference. When the first\n" +" <string> audio track does not match your native language\n" +" then select the first subtitle that does. When\n" +" used in conjunction with --native-dub the audio\n" +" track is changed in preference to subtitles.\n" +" Provide the language's iso639-2 code:\n" +" (fre, eng, spa, dut, et cetera)\n" +" --native-dub Used in conjunction with --native-language\n" +" requests that if no audio tracks are selected the\n" +" default selected audio track will be the first\n" +" one that matches the --native-language. If there\n" +" are no matching audio tracks then the first\n" +" matching subtitle track is used instead.\n" +" --srt-file <string> SubRip SRT filename(s), separated by commas.\n" +" --srt-codeset Character codeset(s) that the SRT file(s) are\n" +" <string> encoded in, separated by commas.\n" +" Use 'iconv -l' for a list of valid\n" +" codesets. If not specified, 'latin1' is assumed\n" +" --srt-offset Offset (in milliseconds) to apply to the SRT\n" +" <string> file(s), separated by commas. If not specified,\n" +" zero is assumed. Offsets may be negative.\n" +" --srt-lang <string> SRT track language as an iso639-2 code:\n" +" (fre, eng, spa, dut, et cetera)\n" +" Separated by commas. If not specified, then 'und'\n" +" is used.\n" +" --srt-default Flag the selected srt as the default subtitle\n" +" <number> to be displayed upon playback. Setting no default\n" +" means no subtitle will be automatically displayed\n" +" If \"number\" is omitted, the first SRT is the\n" +" default. \"number\" is an 1 based index into the\n" +" 'srt-file' list\n" +" --srt-burn \"Burn\" the selected SRT subtitle into the\n" +" <number> video track. If \"number\" is omitted, the first\n" +" SRT is burned. \"number\" is an 1 based index\n" +" into the 'srt-file' list\n" +"\n" ); #ifdef USE_QSV if (hb_qsv_available()) { fprintf( out, - "### Intel Quick Sync Video------------------------------------------------------\n\n" - " --disable-qsv-decoding Force software decoding of the video track.\n" - " --qsv-async-depth Specifies how many asynchronous operations should be\n" - " performed before the result is explicitly synchronized.\n" - " Default: 4. If zero, the value is not specified.\n" - "\n" +"### Intel Quick Sync Video------------------------------------------------\n\n" +" --disable-qsv-decoding Force software decoding of the video track.\n" +" --enable-qsv-decoding Allow QSV hardware decoding of the video track.\n" +" --qsv-async-depth Specifies how many asynchronous operations\n" +" should be performed before the result is\n" +" explicitly synchronized.\n" +" Default: 4. If zero, the value is not specified.\n" +"\n" ); } #endif @@ -3362,26 +1415,141 @@ if (hb_qsv_available()) /**************************************************************************** * ShowPresets: ****************************************************************************/ -static void ShowPresets() +static const char * +reverse_search_char(const char *front, const char *back, char delim) { - fprintf( stderr, "%s - %s - %s\n", HB_PROJECT_TITLE, HB_PROJECT_BUILD_TITLE, HB_PROJECT_URL_WEBSITE ); - - printf("\n< Devices\n"); - printf("\n + Universal: -e x264 -q 20.0 -r 30 --pfr -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 720 -Y 576 --loose-anamorphic --modulus 2 -m --x264-preset fast --h264-profile baseline --h264-level 3.0\n"); - printf("\n + iPod: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -I -X 320 -Y 240 --modulus 2 -m --x264-preset medium --h264-profile baseline --h264-level 1.3\n"); - printf("\n + iPhone & iPod touch: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 960 -Y 640 --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 3.1\n"); - printf("\n + iPad: -e x264 -q 20.0 -r 30 --pfr -a 1 -E ffaac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1280 -Y 720 --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 3.1\n"); - printf("\n + AppleTV: -e x264 -q 20.0 -r 30 --pfr -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 960 -Y 720 --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 3.1 -x qpmin=4:cabac=0:ref=2:b-pyramid=none:weightb=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500\n"); - printf("\n + AppleTV 2: -e x264 -q 20.0 -r 30 --pfr -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1280 -Y 720 --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 3.1\n"); - printf("\n + AppleTV 3: -e x264 -q 20.0 -r 30 --pfr -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1920 -Y 1080 --decomb=fast --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 4.0\n"); - printf("\n + Android: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 128 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 720 -Y 576 --loose-anamorphic --modulus 2 --x264-preset medium --h264-profile main --h264-level 3.0\n"); - printf("\n + Android Tablet: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 128 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1280 -Y 720 --loose-anamorphic --modulus 2 --x264-preset medium --h264-profile main --h264-level 3.1\n"); - printf("\n + Windows Phone 8: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 128 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1280 -Y 720 --modulus 2 --x264-preset medium --h264-profile main --h264-level 3.1\n"); - printf("\n>\n"); - printf("\n< Regular\n"); - printf("\n + Normal: -e x264 -q 20.0 -a 1 -E ffaac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 --loose-anamorphic --modulus 2 -m --x264-preset veryfast --h264-profile main --h264-level 4.0\n"); - printf("\n + High Profile: -e x264 -q 20.0 -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 --decomb --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 4.1\n"); - printf("\n>\n"); + while (back != front && *back != delim) + back--; + return back; +} + +#if defined( __MINGW32__ ) +static char * my_strndup(const char *src, int len) +{ + int src_len = strlen(src); + int alloc = src_len < len ? src_len + 1 : len + 1; + char *result = malloc(alloc); + strncpy(result, src, alloc - 1); + result[alloc - 1] = 0; + return result; +} +#else +#define my_strndup strndup +#endif + +static char** str_width_split( const char *str, int width ) +{ + const char * pos; + const char * end; + char ** ret; + int count, ii; + int len; + char delem = ' '; + + if ( str == NULL || str[0] == 0 ) + { + ret = malloc( sizeof(char*) ); + *ret = NULL; + return ret; + } + + len = strlen(str); + + // Find number of elements in the string + count = 1; + pos = str; + end = pos + width; + while (end < str + len) + { + end = reverse_search_char(pos, end, delem); + if (end == pos) + { + // Shouldn't happen for reasonable input + break; + } + count++; + pos = end + 1; + end = pos + width; + } + count++; + ret = calloc( ( count + 1 ), sizeof(char*) ); + + pos = str; + end = pos + width; + for (ii = 0; ii < count - 1 && end < str + len; ii++) + { + end = reverse_search_char(pos, end, delem); + if (end == pos) + { + break; + } + ret[ii] = my_strndup(pos, end - pos); + pos = end + 1; + end = pos + width; + } + if (*pos != 0 && ii < count - 1) + { + ret[ii] = my_strndup(pos, width); + } + + return ret; +} + +static void Indent(FILE *f, char *whitespace, int indent) +{ + int ii; + for (ii = 0; ii < indent; ii++) + { + fprintf(f, "%s", whitespace); + } +} + +static void ShowPresets(hb_value_array_t *presets, int indent, int descriptions) +{ + if (presets == NULL) + presets = hb_presets_get(); + + int count = hb_value_array_len(presets); + int ii; + for (ii = 0; ii < count; ii++) + { + const char *name; + hb_dict_t *preset_dict = hb_value_array_get(presets, ii); + name = hb_value_get_string(hb_dict_get(preset_dict, "PresetName")); + Indent(stderr, " ", indent); + if (hb_value_get_bool(hb_dict_get(preset_dict, "Folder"))) + { + indent++; + fprintf(stderr, "%s/\n", name); + hb_value_array_t *children; + children = hb_dict_get(preset_dict, "ChildrenArray"); + if (children == NULL) + continue; + ShowPresets(children, indent, descriptions); + indent--; + } + else + { + fprintf(stderr, "%s\n", name); + if (descriptions) + { + const char *desc; + desc = hb_value_get_string(hb_dict_get(preset_dict, + "PresetDescription")); + if (desc != NULL && desc[0] != 0) + { + int ii; + char **split = str_width_split(desc, 60); + for (ii = 0; split[ii] != NULL; ii++) + { + Indent(stderr, " ", indent+1); + fprintf(stderr, "%s\n", split[ii]); + } + str_vfree(split); + } + } + } + } } static char* strchr_quote(char *pos, char c, char q) @@ -3434,6 +1602,15 @@ static char *strndup_quote(char *str, char q, int len) return res; } +static int str_vlen(char **strv) +{ + int i; + if (strv == NULL) + return 0; + for (i = 0; strv[i]; i++); + return i; +} + static char** str_split( char *str, char delem ) { char * pos; @@ -3510,7 +1687,7 @@ static double parse_hhmmss_strtok() ****************************************************************************/ static int ParseOptions( int argc, char ** argv ) { - + #define PREVIEWS 257 #define START_AT_PREVIEW 258 #define START_AT 259 @@ -3546,7 +1723,6 @@ static int ParseOptions( int argc, char ** argv ) #define ENCODER_PROFILE_LIST 289 #define ENCODER_LEVEL 290 #define ENCODER_LEVEL_LIST 291 - #define NO_OPENCL 292 #define NORMALIZE_MIX 293 #define AUDIO_DITHER 294 #define QSV_BASELINE 295 @@ -3554,6 +1730,13 @@ static int ParseOptions( int argc, char ** argv ) #define QSV_IMPLEMENTATION 297 #define FILTER_NLMEANS 298 #define FILTER_NLMEANS_TUNE 299 + #define AUDIO_LANG_LIST 300 + #define SUBTITLE_LANG_LIST 301 + #define PRESET_EXPORT 302 + #define PRESET_EXPORT_DESC 303 + #define PRESET_EXPORT_FILE 304 + #define PRESET_IMPORT 305 + #define PRESET_IMPORT_GUI 306 for( ;; ) { @@ -3563,22 +1746,26 @@ static int ParseOptions( int argc, char ** argv ) { "update", no_argument, NULL, 'u' }, { "verbose", optional_argument, NULL, 'v' }, { "no-dvdnav", no_argument, NULL, DVDNAV }, - { "no-opencl", no_argument, NULL, NO_OPENCL }, + { "no-opencl", no_argument, &use_opencl, 0 }, #ifdef USE_QSV { "qsv-baseline", no_argument, NULL, QSV_BASELINE, }, { "qsv-async-depth", required_argument, NULL, QSV_ASYNC_DEPTH, }, { "qsv-implementation", required_argument, NULL, QSV_IMPLEMENTATION, }, { "disable-qsv-decoding", no_argument, &qsv_decode, 0, }, + { "enable-qsv-decoding", no_argument, &qsv_decode, 1, }, #endif { "format", required_argument, NULL, 'f' }, { "input", required_argument, NULL, 'i' }, { "output", required_argument, NULL, 'o' }, - { "optimize", no_argument, NULL, 'O' }, - { "ipod-atom", no_argument, NULL, 'I' }, - { "use-opencl", no_argument, NULL, 'P' }, - { "use-hwd", no_argument, NULL, 'U' }, + { "optimize", no_argument, &mp4_optimize, 1 }, + { "no-optimize", no_argument, &mp4_optimize, 0 }, + { "ipod-atom", no_argument, &ipod_atom, 1 }, + { "no-ipod-atom",no_argument, &ipod_atom, 0 }, + { "use-opencl", no_argument, &use_opencl, 1 }, + { "use-hwd", no_argument, &use_hwd, 1 }, + { "no-hwd", no_argument, &use_hwd, 0 }, { "title", required_argument, NULL, 't' }, { "min-duration",required_argument, NULL, MIN_DURATION }, @@ -3587,12 +1774,19 @@ static int ParseOptions( int argc, char ** argv ) { "chapters", required_argument, NULL, 'c' }, { "angle", required_argument, NULL, ANGLE }, { "markers", optional_argument, NULL, 'm' }, + { "no-markers", no_argument, &chapter_markers, 0 }, + { "audio-lang-list", required_argument, NULL, AUDIO_LANG_LIST }, + { "all-audio", no_argument, &audio_all, 1 }, + { "first-audio", no_argument, &audio_all, 0 }, { "audio", required_argument, NULL, 'a' }, { "mixdown", required_argument, NULL, '6' }, { "normalize-mix", required_argument, NULL, NORMALIZE_MIX }, { "drc", required_argument, NULL, 'D' }, { "gain", required_argument, NULL, AUDIO_GAIN }, { "adither", required_argument, NULL, AUDIO_DITHER }, + { "subtitle-lang-list", required_argument, NULL, SUBTITLE_LANG_LIST }, + { "all-subtitles", no_argument, &subtitle_all, 1 }, + { "first-subtitle", no_argument, &subtitle_all, 0 }, { "subtitle", required_argument, NULL, 's' }, { "subtitle-forced", optional_argument, NULL, 'F' }, { "subtitle-burned", optional_argument, NULL, SUB_BURNED }, @@ -3609,26 +1803,38 @@ static int ParseOptions( int argc, char ** argv ) { "aencoder", required_argument, NULL, 'E' }, { "two-pass", no_argument, NULL, '2' }, { "deinterlace", optional_argument, NULL, 'd' }, + { "no-deinterlace", no_argument, &deinterlace_disable, 1 }, { "deblock", optional_argument, NULL, '7' }, + { "no-deblock", no_argument, &deblock_disable, 1 }, { "denoise", optional_argument, NULL, '8' }, + { "hqdn3d", optional_argument, NULL, '8' }, + { "no-hqdn3d", no_argument, &hqdn3d_disable, 1 }, { "nlmeans", optional_argument, NULL, FILTER_NLMEANS }, + { "no-nlmeans", no_argument, &nlmeans_disable, 1 }, { "nlmeans-tune",required_argument, NULL, FILTER_NLMEANS_TUNE }, { "detelecine", optional_argument, NULL, '9' }, + { "no-detelecine", no_argument, &detelecine_disable, 1 }, { "decomb", optional_argument, NULL, '5' }, - { "grayscale", no_argument, NULL, 'g' }, + { "no-decomb", no_argument, &decomb_disable, 1 }, + { "grayscale", no_argument, &grayscale, 1 }, + { "no-grayscale",no_argument, &grayscale, 0 }, { "rotate", optional_argument, NULL, ROTATE_FILTER }, + { "non-anamorphic", no_argument, &anamorphic_mode, 0 }, { "strict-anamorphic", no_argument, &anamorphic_mode, 1 }, { "loose-anamorphic", no_argument, &anamorphic_mode, 2 }, { "custom-anamorphic", no_argument, &anamorphic_mode, 3 }, { "display-width", required_argument, NULL, DISPLAY_WIDTH }, - { "keep-display-aspect", no_argument, &keep_display_aspect, 1 }, + { "keep-display-aspect", optional_argument, NULL, KEEP_DISPLAY_ASPECT }, + { "no-keep-display-aspect", no_argument, &keep_display_aspect, 0 }, { "pixel-aspect", required_argument, NULL, PIXEL_ASPECT }, { "modulus", required_argument, NULL, MODULUS }, { "itu-par", no_argument, &itu_par, 1 }, + { "no-itu-par", no_argument, &itu_par, 0 }, { "width", required_argument, NULL, 'w' }, { "height", required_argument, NULL, 'l' }, { "crop", required_argument, NULL, 'n' }, { "loose-crop", optional_argument, NULL, LOOSE_CROP }, + { "no-loose-crop", no_argument, &loose_crop, 0 }, // mapping of legacy option names for backwards compatibility { "qsv-preset", required_argument, NULL, ENCODER_PRESET, }, @@ -3664,6 +1870,11 @@ static int ParseOptions( int argc, char ** argv ) { "maxWidth", required_argument, NULL, 'X' }, { "preset", required_argument, NULL, 'Z' }, { "preset-list", no_argument, NULL, 'z' }, + { "preset-import-file", no_argument, NULL, PRESET_IMPORT }, + { "preset-import-gui", no_argument, NULL, PRESET_IMPORT_GUI }, + { "preset-export", required_argument, NULL, PRESET_EXPORT }, + { "preset-export-file", required_argument, NULL, PRESET_EXPORT_FILE }, + { "preset-export-description", required_argument, NULL, PRESET_EXPORT_DESC }, { "aname", required_argument, NULL, 'A' }, { "color-matrix",required_argument, NULL, 'M' }, @@ -3715,12 +1926,37 @@ static int ParseOptions( int argc, char ** argv ) } break; case 'Z': - preset = 1; preset_name = strdup(optarg); break; case 'z': - ShowPresets(); + ShowPresets(NULL, 0, 1); exit ( 0 ); + case PRESET_EXPORT: + preset_export_name = strdup(optarg); + break; + case PRESET_EXPORT_DESC: + preset_export_desc = strdup(optarg); + break; + case PRESET_EXPORT_FILE: + preset_export_file = strdup(optarg); + break; + case PRESET_IMPORT: + { + // Import list of preset files + while (optind < argc && argv[optind][0] != '-') + { + int result = hb_presets_add_path(argv[optind]); + if (result != 0) + { + fprintf(stderr, "Preset import failed, file (%s)\n", + argv[optind]); + } + optind++; + } + } break; + case PRESET_IMPORT_GUI: + hb_presets_gui_init(); + break; case DVDNAV: dvdnav = 0; break; @@ -3732,7 +1968,7 @@ static int ParseOptions( int argc, char ** argv ) input = strdup( optarg ); #ifdef __APPLE_CC__ char *devName = bsd_name_for_path( input ); // alloc - if( devName ) + if( devName != NULL ) { if( device_is_dvd( devName )) { @@ -3747,19 +1983,6 @@ static int ParseOptions( int argc, char ** argv ) case 'o': output = strdup( optarg ); break; - case 'O': - mp4_optimize = 1; - break; - case 'I': - ipod_atom = 1; - break; - case 'P': - use_opencl = 1; - break; - case 'U': - use_hwd = 1; - break; - case 't': titleindex = atoi( optarg ); break; @@ -3784,15 +2007,12 @@ static int ParseOptions( int argc, char ** argv ) } else { - fprintf( stderr, "chapters: invalid syntax (%s)\n", + fprintf( stderr, "chapters: Invalid syntax (%s)\n", optarg ); return -1; } break; } - case NO_OPENCL: - use_opencl = 0; - break; case ANGLE: angle = atoi( optarg ); break; @@ -3803,33 +2023,38 @@ static int ParseOptions( int argc, char ** argv ) } chapter_markers = 1; break; + case AUDIO_LANG_LIST: + audio_lang_list = str_split(optarg, ','); + break; + case SUBTITLE_LANG_LIST: + subtitle_lang_list = str_split(optarg, ','); + break; case 'a': if( optarg != NULL ) { - atracks = strdup( optarg ); - audio_explicit = 1; + atracks = str_split(optarg, ','); } else { - atracks = "1" ; + atracks = str_split("1", ','); } break; case '6': if( optarg != NULL ) { - mixdowns = strdup( optarg ); + mixdowns = str_split(optarg, ','); } break; case 'D': if( optarg != NULL ) { - dynamic_range_compression = strdup( optarg ); + dynamic_range_compression = str_split(optarg, ','); } break; case AUDIO_GAIN: if( optarg != NULL ) { - audio_gain = strdup( optarg ); + audio_gain = str_split(optarg, ','); } break; case AUDIO_DITHER: @@ -3839,10 +2064,7 @@ static int ParseOptions( int argc, char ** argv ) } break; case NORMALIZE_MIX: - if( optarg != NULL ) - { - normalize_mix_level = str_split( optarg, ',' ); - } + normalize_mix_level = str_split(optarg, ','); break; case 's': subtracks = str_split( optarg, ',' ); @@ -3851,28 +2073,52 @@ static int ParseOptions( int argc, char ** argv ) subforce = str_split( optarg, ',' ); break; case SUB_BURNED: - if( optarg != NULL ) + if (optarg != NULL) { - subburn = strdup( optarg ); + if (!strcasecmp(optarg, "native") || + !strcasecmp(optarg, "scan")) + subburn_native = 1; + else + { + subburn = strtol(optarg, NULL, 0); + } } else { - subburn = "" ; + subburn = 1; + } + if (subburn > 0) + { + if (subtracks != NULL && str_vlen(subtracks) >= subburn && + !strcasecmp("scan", subtracks[subburn-1])) + { + subburn_native = 1; + } } break; case SUB_DEFAULT: - if( optarg != NULL ) + if (optarg != NULL) { - subdefault = strdup( optarg ); + subdefault = strtol(optarg, NULL, 0); } else { - subdefault = "" ; + subdefault = 1; } break; case 'N': - native_language = strdup( optarg ); - break; + { + const iso639_lang_t *lang = lang_lookup(optarg); + if (lang != NULL) + { + native_language = strdup(lang->iso639_2); + } + else + { + fprintf(stderr, "Invalid native language (%s)\n", optarg); + return -1; + } + } break; case NATIVE_DUB: native_dub = 1; break; @@ -3912,129 +2158,123 @@ static int ParseOptions( int argc, char ** argv ) twoPass = 1; break; case 'd': - if( optarg != NULL ) + free(deinterlace); + if (optarg != NULL) { - if (!( strcmp( optarg, "fast" ) )) - { - deinterlace_opt = "0"; - } - else if (!( strcmp( optarg, "slow" ) )) - { - deinterlace_opt = "1"; - } - else if (!( strcmp( optarg, "slower" ) )) - { - deinterlace_opt = "3"; - } - else if (!( strcmp( optarg, "bob" ) )) - { - deinterlace_opt = "15"; - } - else - { - deinterlace_opt = strdup( optarg ); - } + deinterlace = strdup(optarg); + } + else + { + deinterlace = strdup("default"); } - deinterlace = 1; break; case '7': + free(deblock); if( optarg != NULL ) { - deblock_opt = strdup( optarg ); + deblock = strdup(optarg); + } + else + { + deblock = strdup("5"); } - deblock = 1; break; case '8': - if( optarg != NULL ) + free(hqdn3d); + if (optarg != NULL) + { + hqdn3d = strdup(optarg); + } + else { - free(denoise_opt); - denoise_opt = strdup( optarg ); + hqdn3d = strdup("default"); } - denoise = 1; break; case FILTER_NLMEANS: + free(nlmeans); if (optarg != NULL) { - free(nlmeans_opt); - nlmeans_opt = strdup(optarg); + nlmeans = strdup(optarg); + } + else + { + nlmeans = strdup("light"); } - nlmeans = 1; break; case FILTER_NLMEANS_TUNE: + free(nlmeans_tune); + nlmeans_tune = strdup(optarg); + break; + case '9': + free(detelecine); if (optarg != NULL) { - free(nlmeans_tune_opt); - nlmeans_tune_opt = strdup(optarg); + detelecine = strdup(optarg); } - break; - case '9': - if( optarg != NULL ) + else { - detelecine_opt = strdup( optarg ); + detelecine = strdup("default"); } - detelecine = 1; break; case '5': - if( optarg != NULL ) + free(decomb); + if (optarg != NULL) { - if (!( strcmp( optarg, "fast" ) )) - { - decomb_opt = "7:2:6:9:1:80"; - } - else if (!( strcmp( optarg, "bob" ) )) - { - decomb_opt = "455"; - } - else - { - decomb_opt = strdup( optarg ); - } + decomb = strdup(optarg); + } + else + { + decomb = strdup("default"); } - decomb = 1; - break; - case 'g': - grayscale = 1; break; case ROTATE_FILTER: + free(rotate); + if (optarg != NULL) + { + rotate = strdup(optarg); + } + else + { + rotate = strdup("3"); + } + break; + case KEEP_DISPLAY_ASPECT: if( optarg != NULL ) { - rotate_opt = strdup( optarg ); - rotate_val = atoi( optarg ); + keep_display_aspect = atoi(optarg); + } + else + { + keep_display_aspect = 1; } - rotate = 1; break; case DISPLAY_WIDTH: if( optarg != NULL ) { - sscanf( optarg, "%i", &display_width ); + display_width = atoi(optarg); } break; case PIXEL_ASPECT: if( optarg != NULL ) { - sscanf( optarg, "%i:%i", &par_width, &par_height ); + sscanf(optarg, "%i:%i", &par_width, &par_height); } break; case MODULUS: if( optarg != NULL ) { - sscanf( optarg, "%i", &modulus ); + modulus = atoi(optarg); } break; case 'e': { - vcodec = hb_video_encoder_get_from_name(optarg); - if (vcodec <= 0) - { - fprintf(stderr, "invalid codec (%s)\n", optarg); - return -1; - } + vcodec = strdup(optarg); break; } case 'E': if( optarg != NULL ) { - acodecs = strdup( optarg ); + acodecs = str_split(optarg, ','); } break; case 'w': @@ -4057,27 +2297,22 @@ static int ParseOptions( int argc, char ** argv ) break; } case LOOSE_CROP: - loose_crop = optarg ? atoi(optarg) : 15; + if (optarg != NULL) + loose_crop = atoi(optarg); + else + loose_crop = 1; break; case 'r': { - vrate = hb_video_framerate_get_from_name(optarg); - if (vrate <= 0) - { - vrate = 0; - fprintf(stderr, "invalid framerate %s\n", optarg); - } - else if (!cfr) + vrate = strdup(optarg); + if (!cfr) { cfr = 1; } break; } case 'R': - if( optarg != NULL ) - { - arates = strdup( optarg ); - } + arates = str_split( optarg, ',' ); break; case 'b': vbitrate = atoi( optarg ); @@ -4095,19 +2330,19 @@ static int ParseOptions( int argc, char ** argv ) acompressions = str_split( optarg, ',' ); break; case ENCODER_PRESET: - x264_preset = strdup( optarg ); + encoder_preset = strdup( optarg ); break; case ENCODER_TUNE: - x264_tune = strdup( optarg ); + encoder_tune = strdup( optarg ); break; case 'x': advanced_opts = strdup( optarg ); break; case ENCODER_PROFILE: - h264_profile = strdup( optarg ); + encoder_profile = strdup( optarg ); break; case ENCODER_LEVEL: - h264_level = strdup( optarg ); + encoder_level = strdup( optarg ); break; case ENCODER_PRESET_LIST: fprintf(stderr, "Available --encoder-preset values for '%s' encoder:\n", @@ -4208,31 +2443,7 @@ static int ParseOptions( int argc, char ** argv ) } case ALLOWED_AUDIO_COPY: { - allowed_audio_copy = 0; - const hb_encoder_t *audio_encoder = NULL; - char **allowed = str_split(optarg, ','); - - while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) - { - if ((audio_encoder->codec & HB_ACODEC_PASS_FLAG) && - (audio_encoder->codec != HB_ACODEC_AUTO_PASS)) - { - int i = -1; - while(allowed[++i] != NULL) - { - // skip "copy:" - if (!strcasecmp(allowed[i], - audio_encoder->short_name + 5)) - { - allowed_audio_copy |= audio_encoder->codec; - break; - } - } - } - } - - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - str_vfree(allowed); + audio_copy_list = str_split(optarg, ','); break; } case AUDIO_FALLBACK: @@ -4270,37 +2481,144 @@ static int ParseOptions( int argc, char ** argv ) } - if (nlmeans) + if (deblock != NULL) { - char *opt = hb_generate_filter_settings(HB_FILTER_NLMEANS, - nlmeans_opt, nlmeans_tune_opt); - if (opt != NULL) + if (deblock_disable) { - free(nlmeans_opt); - nlmeans_opt = opt; + fprintf(stderr, + "Incompatible options --deblock and --no-deblock\n"); + return -1; } - else if (nlmeans_opt != NULL) + if (hb_validate_filter_settings(HB_FILTER_DEBLOCK, deblock)) + { + fprintf(stderr, "Invalid deblock option %s\n", deblock); + return -1; + } + } + + if (detelecine != NULL) + { + if (detelecine_disable) { - fprintf(stderr, "Invalid parameters for nlmeans (%s).", nlmeans_opt); + fprintf(stderr, + "Incompatible options --detelecine and --no-detelecine\n"); return -1; } - else if (nlmeans_tune_opt != NULL) + if (!hb_validate_filter_preset(HB_FILTER_DETELECINE, + detelecine, NULL)) { - fprintf(stdout, "Default nlmeans parameters specified; ignoring nlmeans tune (%s).\n", nlmeans_tune_opt); + // Nothing to do, but must validate preset before + // attempting to validate custom settings to prevent potential + // false positive + } + else if (!hb_validate_filter_settings(HB_FILTER_DETELECINE, detelecine)) + { + detelecine_custom = 1; + } + else + { + fprintf(stderr, "Invalid detelecine option %s\n", detelecine); + return -1; } } - if (denoise) + + if (deinterlace != NULL) { - char *opt = hb_generate_filter_settings(HB_FILTER_DENOISE, - denoise_opt, NULL); - if (opt != NULL) + if (deinterlace_disable) + { + fprintf(stderr, + "Incompatible options --deinterlace and --no-deinterlace\n"); + return -1; + } + if (!hb_validate_filter_preset(HB_FILTER_DEINTERLACE, + deinterlace, NULL)) { - free(denoise_opt); - denoise_opt = opt; + // Nothing to do, but must validate preset before + // attempting to validate custom settings to prevent potential + // false positive } - else if (denoise_opt != NULL) + else if (!hb_validate_filter_settings(HB_FILTER_HQDN3D, deinterlace)) { - fprintf(stderr, "Invalid parameters for hqdn3d (%s).", denoise_opt); + deinterlace_custom = 1; + } + else + { + fprintf(stderr, "Invalid deinterlace option %s\n", deinterlace); + return -1; + } + } + + if (decomb != NULL) + { + if (decomb_disable) + { + fprintf(stderr, + "Incompatible options --decomb and --no-decomb\n"); + return -1; + } + if (!hb_validate_filter_preset(HB_FILTER_DECOMB, decomb, NULL)) + { + // Nothing to do, but must validate preset before + // attempting to validate custom settings to prevent potential + // false positive + } + else if (!hb_validate_filter_settings(HB_FILTER_DECOMB, decomb)) + { + decomb_custom = 1; + } + else + { + fprintf(stderr, "Invalid decomb option %s\n", decomb); + return -1; + } + } + + if (hqdn3d != NULL) + { + if (hqdn3d_disable) + { + fprintf(stderr, + "Incompatible options --hqdn3d and --no-hqdn3d\n"); + return -1; + } + if (!hb_validate_filter_preset(HB_FILTER_HQDN3D, hqdn3d, NULL)) + { + // Nothing to do, but must validate preset before + // attempting to validate custom settings to prevent potential + // false positive + } + else if (!hb_validate_filter_settings(HB_FILTER_HQDN3D, hqdn3d)) + { + hqdn3d_custom = 1; + } + else + { + fprintf(stderr, "Invalid hqdn3d option %s\n", hqdn3d); + return -1; + } + } + + if (nlmeans != NULL) + { + if (nlmeans_disable) + { + fprintf(stderr, + "Incompatible options --nlmeans and --no-nlmeans\n"); + return -1; + } + if (!hb_validate_filter_preset(HB_FILTER_HQDN3D, nlmeans, nlmeans_tune)) + { + // Nothing to do, but must validate preset before + // attempting to validate custom settings to prevent potential + // false positive + } + else if (!hb_validate_filter_settings(HB_FILTER_HQDN3D, nlmeans)) + { + nlmeans_custom = 1; + } + else + { + fprintf(stderr, "Invalid hqdn3d option %s\n", nlmeans); return -1; } } @@ -4308,6 +2626,42 @@ static int ParseOptions( int argc, char ** argv ) return 0; } +static int foreign_audio_scan(char **subtracks) +{ + if (subtracks != NULL) + { + int count = str_vlen(subtracks); + int ii; + for (ii = 0; ii < count; ii++) + { + if (!strcasecmp(subtracks[0], "scan")) + { + return 1; + } + } + } + return 0; +} + +static int count_subtitles(char **subtracks) +{ + int subtitle_track_count = 0; + if (subtracks != NULL) + { + int count = str_vlen(subtracks); + int ii; + for (ii = 0; ii < count; ii++) + { + if (strcasecmp(subtracks[0], "scan") && + strcasecmp(subtracks[0], "none")) + { + subtitle_track_count++; + } + } + } + return subtitle_track_count; +} + static int CheckOptions( int argc, char ** argv ) { if( update ) @@ -4315,7 +2669,7 @@ static int CheckOptions( int argc, char ** argv ) return 0; } - if( input == NULL || *input == '\0' ) + if (preset_export_name == NULL && (input == NULL || *input == '\0')) { fprintf( stderr, "Missing input device. Run %s --help for " "syntax.\n", argv[0] ); @@ -4323,16 +2677,16 @@ static int CheckOptions( int argc, char ** argv ) } /* Parse format */ - if( titleindex > 0 && !titlescan ) + if (titleindex > 0 && !titlescan) { - if( output == NULL || *output == '\0' ) + if (preset_export_name == NULL && (output == NULL || *output == '\0')) { fprintf( stderr, "Missing output file name. Run %s --help " "for syntax.\n", argv[0] ); return 1; } - if (format == NULL) + if (format == NULL && output != NULL) { /* autodetect */ const char *extension = strrchr(output, '.'); @@ -4341,44 +2695,1399 @@ static int CheckOptions( int argc, char ** argv ) // skip '.' mux = hb_container_get_from_extension(extension + 1); } - if (mux <= 0) + hb_container_t * c = hb_container_get_from_format(mux); + if (c != NULL) + format = strdup(c->short_name); + } + } + + int subtitle_track_count = count_subtitles(subtracks); + if (subtitle_track_count > 0 && subtitle_lang_list != NULL) + { + fprintf(stderr, + "Incompatible options: --subtitle-lang-list and --subtitle\n"); + return 1; + } + + if (subtitle_track_count > 0 && subtitle_all != -1) + { + fprintf(stderr, + "Incompatible options: --all-subtitles/--first-subtitle and --subtitle\n"); + return 1; + } + + if (atracks != NULL && audio_lang_list != NULL) + { + fprintf(stderr, + "Incompatible options: --audio-lang-list and --audio\n"); + return 1; + } + + if (atracks != NULL && audio_all != -1) + { + fprintf(stderr, + "Incompatible options: --all-audio/--first-audio and --audio\n"); + return 1; + } + + if ((par_width > 0 && par_height > 0) && display_width > 0) + { + fprintf(stderr, + "Incompatible options: --display-width and --pixel-aspect\n"); + return 1; + } + + if (preset_export_file != NULL && preset_export_name == NULL) + { + fprintf(stderr, + "Error: --preset-export-file requires option --preset-export\n"); + return 1; + } + + if (preset_export_desc != NULL && preset_export_name == NULL) + { + fprintf(stderr, + "Error: --preset-export-desc requires option --preset-export\n"); + return 1; + } + + return 0; +} + +static hb_dict_t * PreparePreset(const char *preset_name) +{ + int ii; + hb_dict_t *preset; + + if (preset_name != NULL) + { + preset = hb_preset_get(preset_name, 1 /*recurse*/); + if (preset == NULL) + { + fprintf(stderr, "Invalid preset %s\n" + "Valid presets are:\n", preset_name); + ShowPresets(NULL, 1, 1); + return NULL; + } + } + else + { + preset = hb_presets_get_default(); + } + if (preset == NULL) + { + fprintf(stderr, "Error loading presets! Aborting.\n"); + return NULL; + } + + int subtitle_track_count = count_subtitles(subtracks); + // Apply any overrides that can be made directly to the preset + if (format != NULL) + { + hb_dict_set(preset, "FileFormat", hb_value_string(format)); + } + if (mp4_optimize != -1) + { + hb_dict_set(preset, "Mp4HttpOptimize", hb_value_bool(mp4_optimize)); + } + if (ipod_atom != -1) + { + hb_dict_set(preset, "Mp4iPodCompatible", hb_value_bool(ipod_atom)); + } + if (chapter_markers != -1) + { + hb_dict_set(preset, "ChapterMarkers", hb_value_bool(chapter_markers)); + } + hb_value_array_t *subtitle_lang_array; + subtitle_lang_array = hb_dict_get(preset, "SubtitleLanguageList"); + if (subtitle_lang_array == NULL) + { + subtitle_lang_array = hb_value_array_init(); + hb_dict_set(preset, "SubtitleLanguageList", subtitle_lang_array); + } + if (subtitle_lang_list != NULL) + { + hb_value_array_clear(subtitle_lang_array); + int count = str_vlen(subtitle_lang_list); + for (ii = 0; ii < count; ii++) + { + const iso639_lang_t *lang = lang_lookup(subtitle_lang_list[ii]); + if (lang != NULL) { - fprintf(stderr, - "Output format can't be guessed from file name (%s), " - "using default.\n", output); - // reset the muxer (use default) - mux = 0; - return 0; + hb_value_array_append(subtitle_lang_array, + hb_value_string(lang->iso639_2)); + } + else + { + fprintf(stderr, "Warning: Invalid subtitle language (%s)\n", + subtitle_lang_list[ii]); + return NULL; } } - else + hb_dict_set(preset, "SubtitleTrackSelectionBehavior", + hb_value_string("first")); + } + if (native_language != NULL) + { + // Add native language subtitles if audio is not native + lang_list_remove(subtitle_lang_array, native_language); + hb_value_array_insert(subtitle_lang_array, 0, + hb_value_string(native_language)); + hb_dict_set(preset, "SubtitleAddForeignAudioSubtitle", + hb_value_bool(1)); + } + if (foreign_audio_scan(subtracks)) + { + // Add foreign audio search + hb_dict_set(preset, "SubtitleAddForeignAudioSearch", hb_value_bool(1)); + } + // Subtitle burn behavior + const char *burn = "none"; + if (subtitle_track_count == 0) + { + if (subburn_native && subburn == 1) + { + burn = "foreign_first"; + } + else if (subburn_native) { - mux = hb_container_get_from_name(format); - if (mux <= 0) + burn = "foreign"; + } + else if (subburn == 1) + { + burn = "first"; + } + } + else + { + if (subburn_native) + { + burn = "foreign"; + } + } + hb_dict_set(preset, "SubtitleBurnBehavior", hb_value_string(burn)); + const char *selection = NULL; + if (subtitle_track_count == 0 && subtitle_all != -1) + { + selection = subtitle_all == 1 ? "all" : "first"; + } + else + { + selection = "none"; + } + if (selection != NULL) + { + hb_dict_set(preset, "SubtitleTrackSelectionBehavior", + hb_value_string(selection)); + } + + if (audio_copy_list != NULL) + { + // Create autopassthru copy mask + hb_value_array_t *array = hb_value_array_init(); + for (ii = 0; audio_copy_list[ii] != NULL; ii++) + { + hb_value_array_append(array, hb_value_string(audio_copy_list[ii])); + } + hb_dict_set(preset, "AudioCopyMask", array); + } + if (acodec_fallback != NULL) + { + hb_dict_set(preset, "AudioEncoderFallback", + hb_value_string(acodec_fallback)); + } + + hb_value_array_t *audio_lang_array; + audio_lang_array = hb_dict_get(preset, "AudioLanguageList"); + if (audio_lang_array == NULL) + { + audio_lang_array = hb_value_array_init(); + hb_dict_set(preset, "AudioLanguageList", audio_lang_array); + } + if (audio_lang_list != NULL) + { + hb_value_array_clear(audio_lang_array); + int count = str_vlen(audio_lang_list); + for (ii = 0; ii < count; ii++) + { + const iso639_lang_t *lang = lang_lookup(audio_lang_list[ii]); + if (lang != NULL) + { + hb_value_array_append(audio_lang_array, + hb_value_string(lang->iso639_2)); + } + else { - fprintf(stderr, "Invalid output format (%s).", format); - fprintf(stderr, "Possible choices are: "); - const hb_container_t *container = NULL; - while ((container = hb_container_get_next(container)) != NULL) + fprintf(stderr, "Warning: Invalid audio language (%s)\n", + audio_lang_list[ii]); + return NULL; + } + } + hb_dict_set(preset, "AudioTrackSelectionBehavior", + hb_value_string("first")); + } + if (native_dub && native_language) + { + // Add native language audio + lang_list_remove(audio_lang_array, native_language); + hb_value_array_insert(audio_lang_array, 0, + hb_value_string(native_language)); + } + if (audio_all != -1) + { + hb_dict_set(preset, "AudioTrackSelectionBehavior", + hb_value_string(audio_all == 1 ? "all" : "first")); + } + + // Audio overrides + if (atracks == NULL && ( + acodecs != NULL || + abitrates != NULL || + arates != NULL || + mixdowns != NULL || + normalize_mix_level != NULL || + audio_dither != NULL || + dynamic_range_compression != NULL || + audio_gain != NULL || + aqualities != NULL || + acompressions != NULL || + anames != NULL)) + { + // No explicit audio tracks, but track settings modified. + // Modify the presets audio settings. + hb_value_array_t *list; + list = hb_dict_get(preset, "AudioList"); + if (list == NULL) + { + list = hb_value_array_init(); + hb_dict_set(preset, "AudioList", list); + } + int count = MAX(str_vlen(mixdowns), + MAX(str_vlen(dynamic_range_compression), + MAX(str_vlen(audio_gain), + MAX(str_vlen(audio_dither), + MAX(str_vlen(normalize_mix_level), + MAX(str_vlen(arates), + MAX(str_vlen(abitrates), + MAX(str_vlen(aqualities), + MAX(str_vlen(acompressions), + MAX(str_vlen(acodecs), + str_vlen(anames))))))))))); + + hb_dict_t *audio_dict; + // Add audio dict entries to list if needed + for (ii = 0; ii < count; ii++) + { + audio_dict = hb_value_array_get(list, ii); + if (audio_dict == NULL) + { + audio_dict = hb_dict_init(); + hb_value_array_append(list, audio_dict); + } + } + + // Update codecs + if (str_vlen(acodecs) > 0) + { + for (ii = 0; acodecs[ii] != NULL; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioEncoder", + hb_value_string(acodecs[ii])); + } + // Apply last codec in list to all other entries + for (; ii < count; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioEncoder", + hb_value_string(acodecs[ii-1])); + } + } + + // Update qualities + if (str_vlen(aqualities) > 0) + { + for (ii = 0; aqualities[ii] != NULL && + aqualities[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioTrackQualityEnable", + hb_value_bool(1)); + hb_dict_set(audio_dict, "AudioTrackQuality", + hb_value_double(strtod(aqualities[ii], NULL))); + } + } + + // Update bitrates + if (str_vlen(abitrates) > 0) + { + for (ii = 0; abitrates[ii] != NULL && + abitrates[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + if (hb_value_get_bool(hb_dict_get(audio_dict, + "AudioTrackQualityEnable"))) { - fprintf(stderr, "%s", container->short_name); - if (hb_container_get_next(container) != NULL) + continue; + } + hb_dict_set(audio_dict, "AudioBitrate", + hb_value_int(atoi(abitrates[ii]))); + } + // Apply last bitrate in list to all other entries + if (abitrates[ii-1][0] != 0) + for (; ii < count; ii++) + { + audio_dict = hb_value_array_get(list, ii); + if (hb_value_get_bool(hb_dict_get(audio_dict, + "AudioTrackQualityEnable"))) { - fprintf(stderr, ", "); + continue; } - else + hb_dict_set(audio_dict, "AudioBitrate", + hb_value_int(atoi(abitrates[ii-1]))); + } + } + + // Update samplerates + if (str_vlen(arates) > 0) + { + for (ii = 0; arates[ii] != NULL && + arates[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioSamplerate", + hb_value_string(arates[ii])); + } + // Apply last samplerate in list to all other entries + if (arates[ii-1][0] != 0) + for (; ii < count; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioSamplerate", + hb_value_string(arates[ii-1])); + } + } + + // Update mixdowns + if (str_vlen(mixdowns) > 0) + { + for (ii = 0; mixdowns[ii] != NULL && + mixdowns[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioMixdown", + hb_value_string(mixdowns[ii])); + } + // Apply last codec in list to all other entries + if (mixdowns[ii-1][0] != 0) + for (; ii < count; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioMixdown", + hb_value_string(mixdowns[ii-1])); + } + } + + // Update mixdowns normalization + if (str_vlen(normalize_mix_level) > 0) + { + for (ii = 0; normalize_mix_level[ii] != NULL && + normalize_mix_level[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioNormalizeMixLevel", + hb_value_bool(atoi(normalize_mix_level[ii]))); + } + // Apply last mix norm in list to all other entries + if (normalize_mix_level[ii-1][0] != 0) + for (; ii < count; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, + "AudioNormalizeMixLevel", + hb_value_bool( + atoi(normalize_mix_level[ii-1]))); + } + } + + // Update DRC + if (str_vlen(dynamic_range_compression) > 0) + { + for (ii = 0;dynamic_range_compression[ii] != NULL && + dynamic_range_compression[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioTrackDRCSlider", + hb_value_double( + strtod(dynamic_range_compression[ii], NULL))); + } + // Apply last DRC in list to all other entries + if (dynamic_range_compression[ii-1][0] != 0) + for (; ii < count; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioTrackDRCSlider", + hb_value_double( + strtod(dynamic_range_compression[ii-1], + NULL))); + } + } + + // Update Gain + if (str_vlen(audio_gain) > 0) + { + for (ii = 0; audio_gain[ii] != NULL && + audio_gain[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioTrackGainSlider", + hb_value_double( + strtod(audio_gain[ii], NULL))); + } + // Apply last gain in list to all other entries + if (audio_gain[ii-1][0] != 0) + for (; ii < count; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioTrackGainSlider", + hb_value_double( + strtod(audio_gain[ii-1], NULL))); + } + } + + // Update dither method + if (str_vlen(audio_dither) > 0) + { + for (ii = 0; audio_dither[ii] != NULL && + audio_dither[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioDitherMethod", + hb_value_string(audio_dither[ii])); + } + // Apply last dither in list to all other entries + if (audio_dither[ii-1][0] != 0) + for (; ii < count; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioDitherMethod", + hb_value_string(audio_dither[ii-1])); + } + } + + // Update compression + if (str_vlen(acompressions) > 0) + { + for (ii = 0; acompressions[ii] != NULL && + acompressions[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioCompressionLevel", + hb_value_double( + strtod(acompressions[ii], NULL))); + } + // Apply last compression in list to all other entries + if (acompressions[ii-1][0] != 0) + for (; ii < count; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioCompressionLevel", + hb_value_double( + strtod(acompressions[ii-1], NULL))); + } + } + + // Update track names + if (str_vlen(anames) > 0) + { + for (ii = 0; anames[ii] != NULL && + anames[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioTrackName", + hb_value_string(acodecs[ii])); + } + } + } + + if (atracks != NULL) + { + // User has specified explicit audio tracks + // Disable preset's audio track selection + hb_dict_set(preset, "AudioTrackSelectionBehavior", + hb_value_string("none")); + } + + if (vcodec != NULL) + { + hb_dict_set(preset, "VideoEncoder", hb_value_string(vcodec)); + } + if (encoder_preset != NULL) + { + hb_dict_set(preset, "VideoPreset", hb_value_string(encoder_preset)); + } + if (encoder_tune != NULL) + { + hb_dict_set(preset, "VideoTune", hb_value_string(encoder_tune)); + } + if (encoder_profile != NULL) + { + hb_dict_set(preset, "VideoProfile", hb_value_string(encoder_profile)); + } + if (encoder_level != NULL) + { + hb_dict_set(preset, "VideoLevel", hb_value_string(encoder_level)); + } + if (advanced_opts != NULL) + { + hb_dict_set(preset, "VideoOptionExtra", hb_value_string(advanced_opts)); + } + if (vquality >= 0) + { + hb_dict_set(preset, "VideoQualityType", hb_value_int(2)); + hb_dict_set(preset, "VideoQualitySlider", hb_value_double(vquality)); + } + else if (vbitrate != 0) + { + hb_dict_set(preset, "VideoQualityType", hb_value_int(1)); + hb_dict_set(preset, "VideoAvgBitrate", hb_value_int(vbitrate)); + if (twoPass) + { + hb_dict_set(preset, "VideoTwoPass", hb_value_bool(1)); + } + if (fastfirstpass) + { + hb_dict_set(preset, "VideoTurboTwoPass", hb_value_bool(1)); + } + } + if (vrate != NULL) + { + hb_dict_set(preset, "VideoFramerate", hb_value_string(vrate)); + } + else + { + hb_dict_set(preset, "VideoFramerate", hb_value_string("auto")); + } + if (cfr != -1) + { + hb_dict_set(preset, "VideoFramerateMode", + hb_value_string(cfr == 0 ? "vfr" : + cfr == 1 ? "cfr" : "pfr")); + } + if (color_matrix_code > 0) + { + hb_dict_set(preset, "VideoColorMatrixCode", + hb_value_int(color_matrix_code)); + } +#ifdef USE_QSV + if (qsv_async_depth >= 0) + { + hb_dict_set(preset, "VideoQSVAsyncDepth", + hb_value_int(qsv_async_depth)); + } + if (qsv_decode != -1) + { + hb_dict_set(preset, "VideoQSVDecode", hb_value_int(qsv_decode)); + } +#endif + if (use_hwd != -1) + { + hb_dict_set(preset, "VideoHWDecode", hb_value_bool(use_hwd)); + } + if (use_opencl != -1) + { + hb_dict_set(preset, "VideoScaler", + hb_value_string(use_opencl ? "opencl" : "swscale")); + } + if (maxWidth > 0) + { + hb_dict_set(preset, "PictureWidth", hb_value_int(maxWidth)); + } + if (maxHeight > 0) + { + hb_dict_set(preset, "PictureHeight", hb_value_int(maxHeight)); + } + if (width > 0) + { + hb_dict_set(preset, "PictureForceWidth", hb_value_int(width)); + } + if (height > 0) + { + hb_dict_set(preset, "PictureForceHeight", hb_value_int(height)); + } + if (crop[0] >= 0) + { + hb_dict_set(preset, "PictureTopCrop", hb_value_int(crop[0])); + } + if (crop[1] >= 0) + { + hb_dict_set(preset, "PictureBottomCrop", hb_value_int(crop[1])); + } + if (crop[2] >= 0) + { + hb_dict_set(preset, "PictureLeftCrop", hb_value_int(crop[2])); + } + if (crop[3] >= 0) + { + hb_dict_set(preset, "PictureRightCrop", hb_value_int(crop[3])); + } + if (loose_crop != -1) + { + hb_dict_set(preset, "PictureLooseCrop", hb_value_bool(loose_crop)); + } + if (display_width > 0) + { + keep_display_aspect = 0; + anamorphic_mode = 3; + hb_dict_set(preset, "PictureDARWidth", hb_value_int(display_width)); + } + else if (par_width > 0 && par_height > 0) + { + keep_display_aspect = 0; + anamorphic_mode = 3; + hb_dict_set(preset, "PicturePARWidth", hb_value_int(par_width)); + hb_dict_set(preset, "PicturePARHeight", hb_value_int(par_height)); + } + if (anamorphic_mode != -1) + { + hb_dict_set(preset, "PicturePAR", hb_value_int(anamorphic_mode)); + } + if (keep_display_aspect != -1) + { + hb_dict_set(preset, "PictureKeepRatio", + hb_value_bool(keep_display_aspect)); + } + if (itu_par != -1) + { + hb_dict_set(preset, "PictureItuPAR", hb_value_bool(itu_par)); + } + if (modulus > 0) + { + hb_dict_set(preset, "PictureModulus", hb_value_int(modulus)); + } + if (grayscale != -1) + { + hb_dict_set(preset, "VideoGrayScale", hb_value_bool(grayscale)); + } + if (deinterlace_disable) + { + hb_dict_set(preset, "PictureDeinterlace", hb_value_string("off")); + } + if (deinterlace != NULL) + { + hb_dict_set(preset, "PictureDecombDeinterlace", hb_value_int(0)); + if (!deinterlace_custom) + { + hb_dict_set(preset, "PictureDeinterlace", + hb_value_string(deinterlace)); + } + else + { + hb_dict_set(preset, "PictureDeinterlace", + hb_value_string("custom")); + hb_dict_set(preset, "PictureDeinterlaceCustom", + hb_value_string(deinterlace)); + } + } + if (decomb_disable) + { + hb_dict_set(preset, "PictureDecomb", hb_value_string("off")); + } + if (decomb != NULL) + { + hb_dict_set(preset, "PictureDecombDeinterlace", hb_value_int(1)); + if (!decomb_custom) + { + hb_dict_set(preset, "PictureDecomb", hb_value_string(decomb)); + } + else + { + hb_dict_set(preset, "PictureDecomb", hb_value_string("custom")); + hb_dict_set(preset, "PictureDecombCustom", hb_value_string(decomb)); + } + } + if (detelecine_disable) + { + hb_dict_set(preset, "PictureDetelecine", hb_value_string("off")); + } + if (detelecine != NULL) + { + if (!detelecine_custom) + { + hb_dict_set(preset, "PictureDetelecine", + hb_value_string(detelecine)); + } + else + { + hb_dict_set(preset, "PictureDetelecine", hb_value_string("custom")); + hb_dict_set(preset, "PictureDetelecineCustom", + hb_value_string(detelecine)); + } + } + const char *s; + s = hb_value_get_string(hb_dict_get(preset, "PictureDenoiseFilter")); + if (hqdn3d_disable && !strcasecmp(s, "hqdn3d")) + { + hb_dict_set(preset, "PictureDenoiseFilter", hb_value_string("off")); + } + if (hqdn3d != NULL) + { + hb_dict_set(preset, "PictureDenoiseFilter", hb_value_string("hqdn3d")); + if (!hqdn3d_custom) + { + hb_dict_set(preset, "PictureDenoisePreset", + hb_value_string(hqdn3d)); + } + else + { + hb_dict_set(preset, "PictureDenoisePreset", + hb_value_string("custom")); + hb_dict_set(preset, "PictureDenoiseCustom", + hb_value_string(hqdn3d)); + } + } + if (nlmeans_disable && !strcasecmp(s, "nlmeans")) + { + hb_dict_set(preset, "PictureDenoiseFilter", hb_value_string("off")); + } + if (nlmeans != NULL) + { + hb_dict_set(preset, "PictureDenoiseFilter", hb_value_string("nlmeans")); + if (!nlmeans_custom) + { + hb_dict_set(preset, "PictureDenoisePreset", + hb_value_string(nlmeans)); + if (nlmeans_tune != NULL) + { + hb_dict_set(preset, "PictureDenoisePreset", + hb_value_string(nlmeans_tune)); + } + } + else + { + hb_dict_set(preset, "PictureDenoisePreset", + hb_value_string("custom")); + hb_dict_set(preset, "PictureDenoiseCustom", + hb_value_string(nlmeans)); + } + } + if (deblock_disable) + { + hb_dict_set(preset, "PictureDeblock", hb_value_string("0")); + } + if (deblock != NULL) + { + hb_dict_set(preset, "PictureDeblock", hb_value_string(deblock)); + } + if (rotate != NULL) + { + hb_dict_set(preset, "PictureRotate", hb_value_string(rotate)); + } + + return preset; +} + + +static int add_sub(hb_value_array_t *list, hb_title_t *title, int track, int *one_burned) +{ + hb_subtitle_t *subtitle; + // Check that the track exists + subtitle = hb_list_item(title->list_subtitle, track); + if (subtitle == NULL) + { + fprintf(stderr, "Warning: Could not find subtitle track %d, skipped\n", + track + 1); + return -1; + } + + int burn = !*one_burned && subburn == track + 1 && + hb_subtitle_can_burn(subtitle->source); + *one_burned |= burn; + int def = subdefault == track + 1; + int force = test_sub_list(subforce, track + 1); + + if (!burn && + !hb_subtitle_can_pass(subtitle->source, mux)) + { + // Only allow one subtitle to be burned into video + if (*one_burned) + { + fprintf(stderr, "Warning: Skipping subtitle track %d, can't have more than one track burnt in\n", track + 1); + return -1; + } + *one_burned = 1; + } + hb_dict_t *subtitle_dict = hb_dict_init(); + hb_dict_set(subtitle_dict, "Track", hb_value_int(track)); + hb_dict_set(subtitle_dict, "Default", hb_value_bool(def)); + hb_dict_set(subtitle_dict, "Forced", hb_value_bool(force)); + hb_dict_set(subtitle_dict, "Burn", hb_value_bool(burn)); + hb_value_array_append(list, subtitle_dict); + return 0; +} + +static int add_srt(hb_value_array_t *list, int track, int *one_burned) +{ + char *codeset = "ISO-8859-1"; + int64_t offset = 0; + char *iso639_2 = "und"; + int burn = !*one_burned && srtburn == track + 1 && + hb_subtitle_can_burn(SRTSUB); + *one_burned |= burn; + int def = srtdefault == track + 1; + + if (srtcodeset && track < str_vlen(srtcodeset) && srtcodeset[track]) + codeset = srtcodeset[track]; + if (srtoffset && track < str_vlen(srtoffset) && srtoffset[track]) + offset = strtoll(srtoffset[track], NULL, 0); + if (srtlang && track < str_vlen(srtlang) && srtlang[track]) + { + const iso639_lang_t *lang = lang_lookup(srtlang[track]); + if (lang != NULL) + { + iso639_2 = lang->iso639_2; + } + else + { + fprintf(stderr, "Warning: Invalid SRT language (%s)\n", + srtlang[track]); + } + } + + hb_dict_t *subtitle_dict = hb_dict_init(); + hb_dict_t *srt_dict = hb_dict_init(); + hb_dict_set(subtitle_dict, "SRT", srt_dict); + hb_dict_set(subtitle_dict, "Default", hb_value_bool(def)); + hb_dict_set(subtitle_dict, "Burn", hb_value_bool(burn)); + hb_dict_set(subtitle_dict, "Offset", hb_value_int(offset)); + hb_dict_set(srt_dict, "Filename", hb_value_string(srtfile[track])); + hb_dict_set(srt_dict, "Language", hb_value_string(iso639_2)); + hb_dict_set(srt_dict, "Codeset", hb_value_string(codeset)); + hb_value_array_append(list, subtitle_dict); + return 0; +} + +static int add_audio(hb_value_array_t *list, hb_title_t *title, int track) +{ + // Check that the track exists + if (hb_list_item(title->list_audio, track) == NULL) + { + fprintf(stderr, "Warning: Could not find audio track %d, skipped\n", + track + 1); + return -1; + } + hb_dict_t *audio_dict = hb_dict_init(); + hb_dict_set(audio_dict, "Track", hb_value_int(track)); + hb_value_array_append(list, audio_dict); + + return 0; +} + +static hb_dict_t* +PrepareJob(hb_handle_t *h, hb_title_t *title, hb_dict_t *preset_dict) +{ + hb_dict_t *job_dict; + job_dict = hb_preset_job_init(h, title->index, preset_dict); + if (job_dict == NULL) + { + fprintf(stderr, "Failed to initialize job\n"); + return NULL; + } + + if (hb_value_get_bool(hb_dict_get(job_dict, "ChapterMarkers"))) + { + write_chapter_names(job_dict, marker_file); + } + + hb_dict_t *dest_dict = hb_dict_get(job_dict, "Destination"); + hb_dict_set(dest_dict, "File", hb_value_string(output)); + + // Now that the job is initialized, we need to find out + // what muxer is being used. + mux = hb_container_get_from_name( + hb_value_get_string(hb_dict_get(dest_dict, "Mux"))); + + // Now set non-preset settings in the job dict + int range_start = 0, range_end = 0, range_seek_points = 0; + const char *range_type = "chapter"; + if (chapter_start && chapter_end && + !start_at_pts && !stop_at_pts && + !start_at_frame && !stop_at_frame && + !start_at_preview ) + { + range_type = "chapter"; + int chapter_count = hb_list_count(title->list_chapter); + range_start = MAX(1, chapter_start); + range_end = MIN(chapter_count, chapter_end); + range_end = MAX(chapter_start, chapter_end); + } + else if (start_at_preview) + { + range_type = "preview"; + range_start = start_at_preview -1; + range_end = stop_at_pts; + range_seek_points = preview_count; + } + else if (start_at_pts || stop_at_pts) + { + range_type = "time"; + range_start = start_at_pts; + range_end = stop_at_pts; + } + else if (start_at_frame || stop_at_frame) + { + range_type = "frame"; + range_start = start_at_frame; + range_end = stop_at_frame; + } + if (range_start || range_end) + { + hb_dict_t *range_dict = hb_dict_get( + hb_dict_get(job_dict, "Source"), "Range"); + hb_dict_set(range_dict, "Type", hb_value_string(range_type)); + if (range_start) + hb_dict_set(range_dict, "Start", hb_value_int(range_start)); + if (range_end) + hb_dict_set(range_dict, "End", hb_value_int(range_end)); + if (range_seek_points) + hb_dict_set(range_dict, "SeekPoints", + hb_value_int(range_seek_points)); + } + + if (angle) + { + hb_dict_t *source_dict = hb_dict_get(job_dict, "Source"); + hb_dict_set(source_dict, "Angle", hb_value_int(angle)); + } + + hb_dict_t *subtitles_dict = hb_dict_get(job_dict, "Subtitle"); + hb_value_array_t * subtitle_array; + subtitle_array = hb_dict_get(subtitles_dict, "SubtitleList"); + + hb_dict_t *audios_dict = hb_dict_get(job_dict, "Audio"); + hb_value_array_t * audio_array = hb_dict_get(audios_dict, "AudioList"); + hb_dict_t *audio_dict; + int track_count = hb_value_array_len(audio_array); + + /* Grab audio tracks */ + if (atracks != NULL) + { + int ii; + if (atracks[0] != NULL && strcasecmp("none", atracks[0])) + { + // First "track" is not "none", add tracks + for (ii = 0; atracks[ii] != NULL; ii++) + { + int first, last, track; + if (sscanf(atracks[ii], "%d-%d", &first, &last ) == 2) + { + for (track = first - 1; track < last; track++) { - fprintf(stderr, "\n"); + add_audio(audio_array, title, track); } } - return 1; + else if (sscanf(atracks[ii], "%d", &track) == 1) + { + add_audio(audio_array, title, track); + } + else + { + fprintf(stderr, "ERROR: unable to parse audio input \"%s\", skipping\n", atracks[ii]); + } + } + } + track_count = hb_value_array_len(audio_array); + + // Now we need to take care of initializing subtitle selection + // for foreign audio since this could not be done by the preset + // due to disabling of preset audio selection. + + // Determine the language of the first audio track + if (track_count > 0) + { + audio_dict = hb_value_array_get(audio_array, 0); + int track = hb_value_get_int(hb_dict_get(audio_dict, "Track")); + + hb_audio_config_t *audio; + audio = hb_list_audio_config_item(title->list_audio, track); + if (audio != NULL) + { + hb_preset_job_add_subtitles(h, title->index, + preset_dict, job_dict); + } + } + + /* Audio Codecs */ + int acodec; + ii = 0; + if (acodecs != NULL) + { + for (; acodecs[ii] != NULL && ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + acodec = hb_audio_encoder_get_from_name(acodecs[ii]); + if (acodec <= 0) + { + fprintf(stderr, + "Invalid codec %s, using default for container.\n", + acodecs[ii]); + acodec = hb_audio_encoder_get_default(mux); + } + hb_dict_set(audio_dict, "Encoder", hb_value_int(acodec)); + } + if (acodecs[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio encoders\n"); } } + acodec = hb_audio_encoder_get_default(mux); + for (; ii < track_count; ii++) + { + // We have fewer inputs than audio tracks, use the + // default codec for this container for the remaining + // tracks. Unless we only have one input then use that + // codec instead. + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Encoder", hb_value_int(acodec)); + } + + /* Sample Rate */ + int arate; + ii = 0; + if (arates != NULL) + { + for (; arates[ii] != NULL && ii < track_count; ii++) + { + if (!strcasecmp(arates[ii], "auto")) + { + arate = 0; + } + else + { + arate = hb_audio_samplerate_get_from_name(arates[ii]); + } + if (arate <= 0) + { + fprintf(stderr, "Invalid sample rate %s, using input rate\n", + arates[ii]); + arate = 0; + } + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Samplerate", hb_value_int(arate)); + } + if (arates[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio sample rates\n"); + } + } + // If exactly one samplerate was specified, apply it to the reset + // of the tracks. + // + // For any tracks that we do not set the rate for, libhb will + // assign the source audio track's rate + if (ii == 1) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Samplerate", hb_value_int(arate)); + } + + /* Audio Mixdown */ + int mix; + ii = 0; + if (mixdowns != NULL) + { + for (; mixdowns[ii] != NULL && ii < track_count; ii++) + { + mix = hb_mixdown_get_from_name(mixdowns[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Mixdown", hb_value_int(mix)); + } + if (mixdowns[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio mixdowns\n"); + } + } + // If exactly one mix was specified, apply it to the reset + // of the tracks + if (ii == 1) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Mixdown", hb_value_int(mix)); + } + + /* Audio Bitrate */ + int abitrate; + ii = 0; + if (abitrates != NULL) + { + for (; abitrates[ii] != NULL && ii < track_count; ii++) + { + if (*abitrates[ii]) + { + abitrate = atoi(abitrates[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Bitrate", hb_value_int(abitrate)); + } + } + if (abitrates[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio bitrates\n"); + } + } + // If exactly one bitrate was specified, apply it to the reset + // of the tracks + if (ii == 1) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Bitrate", hb_value_int(abitrate)); + } + + /* Audio Quality */ + double aquality; + ii = 0; + if (aqualities != NULL) + { + for (; aqualities[ii] != NULL && ii < track_count; ii++) + { + if (*aqualities[ii]) + { + aquality = atof(aqualities[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Quality", + hb_value_double(aquality)); + hb_dict_set(audio_dict, "Bitrate", hb_value_int(-1)); + } + } + if (aqualities[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio qualities\n"); + } + } + // If exactly one quality was specified, apply it to the reset + // of the tracks that do not already have the bitrate set. + if (ii == 1) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + abitrate = hb_value_get_int(hb_dict_get(audio_dict, "Bitrate")); + if (abitrate <= 0) + { + hb_dict_set(audio_dict, "Quality", hb_value_double(aquality)); + hb_dict_set(audio_dict, "Bitrate", hb_value_int(-1)); + } + } + + /* Audio Compression Level */ + double acompression; + ii = 0; + if (acompressions != NULL) + { + for (; acompressions[ii] != NULL && ii < track_count; ii++) + { + if (*acompressions[ii]) + { + acompression = atof(acompressions[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "CompressionLevel", + hb_value_double(acompression)); + } + } + if (acompressions[ii] != NULL) + { + fprintf(stderr, + "Dropping excess audio compression levels\n"); + } + } + // Compression levels are codec specific values. So don't + // try to apply to other tracks. + + /* Audio DRC */ + ii = 0; + double drc; + if (dynamic_range_compression) + { + char **drcs = dynamic_range_compression; + for (; drcs[ii] != NULL && ii < track_count; ii++) + { + drc = atof(drcs[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "DRC", hb_value_double(drc)); + } + if (drcs[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio dynamic range controls\n"); + } + } + // If exactly one DRC was specified, apply it to the reset + // of the tracks + if (ii == 1) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "DRC", hb_value_double(drc)); + } + + /* Audio Gain */ + ii = 0; + double gain; + if (audio_gain) + { + for (; audio_gain[ii] != NULL && ii < track_count; ii++) + { + gain = atof(audio_gain[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Gain", hb_value_double(gain)); + } + if (audio_gain[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio gains\n"); + } + } + // If exactly one gain was specified, apply it to the reset + // of the tracks + if (ii == 1) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Gain", hb_value_double(gain)); + } + + /* Audio Dither */ + int dither; + ii = 0; + if (audio_dither != NULL) + { + for (; audio_dither[ii] != NULL && ii < track_count; ii++) + { + if (*audio_dither[ii]) + { + dither = hb_audio_dither_get_from_name(audio_dither[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "DitherMethod", + hb_value_int(dither)); + } + } + if (audio_dither[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio dither methods\n"); + } + } + // If exactly one dither was specified, apply it to the reset + // of the tracks + if (ii == 1) for (; ii < track_count; ii++) + { + int codec; + audio_dict = hb_value_array_get(audio_array, ii); + codec = hb_value_get_int(hb_dict_get(audio_dict, "Encoder")); + if (hb_audio_dither_is_supported(codec)) + { + hb_dict_set(audio_dict, "DitherMethod", + hb_value_double(dither)); + } + } + + /* Audio Mix Normalization */ + int norm = 0; + ii = 0; + if (normalize_mix_level) + { + char **nmls = normalize_mix_level; + for (; nmls[ii] != NULL && ii < track_count; ii++) + { + norm = atoi(nmls[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "NormalizeMixLevel", + hb_value_int(norm)); + } + if (nmls[ii] != NULL) + { + fprintf(stderr, + "Dropping excess audio mixdown normalizations\n"); + } + } + // If exactly one norm was specified, apply it to the reset + // of the tracks + if (ii == 1) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "NormalizeMixLevel", hb_value_int(norm)); + } + + /* Audio Track Names */ + ii = 0; + if (anames != NULL) + { + for (; anames[ii] != NULL && ii < track_count; ii++) + { + if (*anames[ii]) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Name", + hb_value_string(anames[ii])); + } + } + if (anames[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio track names\n"); + } + } + // If exactly one name was specified, apply it to the reset + // of the tracks + if (ii == 1 && *anames[0]) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Name", hb_value_string(anames[0])); + } } - return 0; + int one_burned = 0; + if (subtracks != NULL) + { + int ii; + for (ii = 0; subtracks[ii] != NULL; ii++) + { + if (strcasecmp(subtracks[ii], "none" ) == 0) + { + // Taken care of already when initializing the job + // from a preset + continue; + } + if (strcasecmp(subtracks[ii], "scan" ) == 0) + { + // Taken care of already when initializing the job + // from a preset + continue; + } + + int first, last, track; + if (sscanf(subtracks[ii], "%d-%d", &first, &last ) == 2) + { + for (track = first - 1; track < last; track++) + { + add_sub(subtitle_array, title, track, &one_burned); + } + } + else if (sscanf(subtracks[ii], "%d", &track) == 1) + { + add_sub(subtitle_array, title, track, &one_burned); + } + else + { + fprintf(stderr, "ERROR: unable to parse subtitle input \"%s\", skipping\n", subtracks[ii]); + } + } + } + + if (srtfile != NULL) + { + int ii; + for (ii = 0; srtfile[ii] != NULL; ii++) + { + add_srt(subtitle_array, ii, &one_burned); + } + } + + return job_dict; } + static void print_string_list(FILE *out, const char* const *list, const char *prefix) { if (out != NULL && prefix != NULL) |