]> granicus.if.org Git - handbrake/commitdiff
libhb,cli: add preset management to libhb, use it in cli
authorjstebbins <jstebbins.hb@gmail.com>
Wed, 6 May 2015 16:04:08 +0000 (16:04 +0000)
committerjstebbins <jstebbins.hb@gmail.com>
Wed, 6 May 2015 16:04:08 +0000 (16:04 +0000)
This results in custom preset support in the CLI and additional
command line options to fully support all preset keys.

git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@7158 b64f7644-9d1e-0410-96f1-a4d463321fa5

24 files changed:
gtk/src/hb-backend.c
gtk/src/internal_defaults.json
gtk/src/presets.c
gtk/src/queuehandler.c
gtk/src/standard_presets.json
libhb/builtin_presets.h [new file with mode: 0644]
libhb/common.c
libhb/hb.c
libhb/hb.h
libhb/hb_dict.c
libhb/hb_dict.h
libhb/lang.c
libhb/lang.h
libhb/libhb_presets.list [new file with mode: 0644]
libhb/module.defs
libhb/plist.c [new file with mode: 0644]
libhb/plist.h [new file with mode: 0644]
libhb/preset.c [new file with mode: 0644]
libhb/preset.h [new file with mode: 0644]
libhb/preset_builtin.json [new file with mode: 0644]
libhb/preset_template.json [new file with mode: 0644]
scripts/create_resources.py [new file with mode: 0644]
scripts/quotestring.py [new file with mode: 0644]
test/test.c

index f02ec32e74de41222d2691f5da9f358ed24874d9..836f4d714ebdc4016711bd6726bc002d2d776d55 100644 (file)
@@ -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
index 76e78c6ff63ac4118200b63e5ed71f56ba8295db..0d4fd76571eec1a899c65473d2161aea8d730f39 100644 (file)
@@ -12,7 +12,7 @@
         "AudioTrackGainSlider": 0.0,
         "AudioTrackDRCSlider": 0.0,
         "AudioMixdown": "dpl2",
-        "AudioSamplerate": "source",
+        "AudioSamplerate": "auto",
         "AudioTrackName": "",
         "angle_count": 1,
         "angle": 1,
         "window_height": 1
     },
     "Presets": {
-        "PresetBuildNumber": "",
         "PictureAutoCrop": true,
         "ChapterMarkers": true,
         "FileFormat": "mp4",
         "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,
         "AudioCopyMask": [
             "copy:ac3"
         ],
-        "AudioEncoderFallback": "ffac3",
+        "AudioEncoderFallback": "ac3",
         "AudioLanguageList": [
             "und"
         ],
                 "AudioTrackGainSlider": 0.0,
                 "AudioTrackDRCSlider": 0.0,
                 "AudioMixdown": "dpl2",
-                "AudioSamplerate": "source"
+                "AudioSamplerate": "auto"
             }
         ],
         "AudioSecondaryEncoderMode": true,
index 2354bc61811271330cfc827efbaa7e54544ae7f4..5c2825c7e8b8b6bfc247f6a29e6642d251e91df5 100644 (file)
@@ -1972,39 +1972,6 @@ value_map_t decomb_xlat[] =
     {NULL, NULL}
 };
 
-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)
 {
@@ -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);
index 67ce7dca497652f5a1bcaa5406437ea240ffd9a0..876d49ec9bc75b3307c07fc2b4919375bbd21b3c 100644 (file)
@@ -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;
index 160a09d829cb811db9d9bc75fb634f0a7445965f..41c7be7675fe166d6098ee2b5457deaabbe91a68 100644 (file)
@@ -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)",
                 "PictureKeepRatio": 1,
                 "PictureLeftCrop": 0,
                 "PictureModulus": 2,
-                "PicturePAR": 0,
+                "PicturePAR": "off",
                 "PictureRightCrop": 0,
                 "PictureTopCrop": 0,
                 "PictureWidth": 320,
                 "PresetName": "iPod",
                 "Subtitles": "None",
                 "Type": 0,
-                "UsesPictureFilters": 1,
+                "UsesPictureFilters": true,
                 "UsesPictureSettings": 1,
                 "VideoAvgBitrate": "2500",
                 "VideoEncoder": "H.264 (x264)",
                 "PictureKeepRatio": 0,
                 "PictureLeftCrop": 0,
                 "PictureModulus": 2,
-                "PicturePAR": 2,
+                "PicturePAR": "loose",
                 "PictureRightCrop": 0,
                 "PictureTopCrop": 0,
                 "PictureWidth": 960,
                 "PresetName": "iPhone & iPod touch",
                 "Subtitles": "None",
                 "Type": 0,
-                "UsesPictureFilters": 1,
+                "UsesPictureFilters": true,
                 "UsesPictureSettings": 1,
                 "VideoAvgBitrate": "2500",
                 "VideoEncoder": "H.264 (x264)",
                 "PictureKeepRatio": 0,
                 "PictureLeftCrop": 0,
                 "PictureModulus": 2,
-                "PicturePAR": 2,
+                "PicturePAR": "loose",
                 "PictureRightCrop": 0,
                 "PictureTopCrop": 0,
                 "PictureWidth": 1280,
                 "PresetName": "iPad",
                 "Subtitles": "None",
                 "Type": 0,
-                "UsesPictureFilters": 1,
+                "UsesPictureFilters": true,
                 "UsesPictureSettings": 1,
                 "VideoAvgBitrate": "2500",
                 "VideoEncoder": "H.264 (x264)",
                 "PictureKeepRatio": 0,
                 "PictureLeftCrop": 0,
                 "PictureModulus": 2,
-                "PicturePAR": 2,
+                "PicturePAR": "loose",
                 "PictureRightCrop": 0,
                 "PictureTopCrop": 0,
                 "PictureWidth": 960,
                 "PresetName": "AppleTV",
                 "Subtitles": "None",
                 "Type": 0,
-                "UsesPictureFilters": 1,
+                "UsesPictureFilters": true,
                 "UsesPictureSettings": 1,
                 "VideoAvgBitrate": "2500",
                 "VideoEncoder": "H.264 (x264)",
                 "PictureKeepRatio": 0,
                 "PictureLeftCrop": 0,
                 "PictureModulus": 2,
-                "PicturePAR": 2,
+                "PicturePAR": "loose",
                 "PictureRightCrop": 0,
                 "PictureTopCrop": 0,
                 "PictureWidth": 1280,
                 "PresetName": "AppleTV 2",
                 "Subtitles": "None",
                 "Type": 0,
-                "UsesPictureFilters": 1,
+                "UsesPictureFilters": true,
                 "UsesPictureSettings": 1,
                 "VideoAvgBitrate": "2500",
                 "VideoEncoder": "H.264 (x264)",
                 "PictureKeepRatio": 0,
                 "PictureLeftCrop": 0,
                 "PictureModulus": 2,
-                "PicturePAR": 2,
+                "PicturePAR": "loose",
                 "PictureRightCrop": 0,
                 "PictureTopCrop": 0,
                 "PictureWidth": 1920,
                 "PresetName": "AppleTV 3",
                 "Subtitles": "None",
                 "Type": 0,
-                "UsesPictureFilters": 1,
+                "UsesPictureFilters": true,
                 "UsesPictureSettings": 1,
                 "VideoAvgBitrate": "2500",
                 "VideoEncoder": "H.264 (x264)",
                 "PictureKeepRatio": 0,
                 "PictureLeftCrop": 0,
                 "PictureModulus": 2,
-                "PicturePAR": 2,
+                "PicturePAR": "loose",
                 "PictureRightCrop": 0,
                 "PictureTopCrop": 0,
                 "PictureWidth": 720,
                 "PresetName": "Android",
                 "Subtitles": "None",
                 "Type": 0,
-                "UsesPictureFilters": 1,
+                "UsesPictureFilters": true,
                 "UsesPictureSettings": 1,
                 "VideoAvgBitrate": "2500",
                 "VideoEncoder": "H.264 (x264)",
                 "PictureKeepRatio": 0,
                 "PictureLeftCrop": 0,
                 "PictureModulus": 2,
-                "PicturePAR": 2,
+                "PicturePAR": "loose",
                 "PictureRightCrop": 0,
                 "PictureTopCrop": 0,
                 "PictureWidth": 1280,
                 "PresetName": "Android Tablet",
                 "Subtitles": "None",
                 "Type": 0,
-                "UsesPictureFilters": 1,
+                "UsesPictureFilters": true,
                 "UsesPictureSettings": 1,
                 "VideoAvgBitrate": "2500",
                 "VideoEncoder": "H.264 (x264)",
                 "PictureKeepRatio": 1,
                 "PictureLeftCrop": 0,
                 "PictureModulus": 2,
-                "PicturePAR": 0,
+                "PicturePAR": "off",
                 "PictureRightCrop": 0,
                 "PictureTopCrop": 0,
                 "PictureWidth": 1280,
                 "PresetName": "Windows Phone 8",
                 "Subtitles": "None",
                 "Type": 0,
-                "UsesPictureFilters": 1,
+                "UsesPictureFilters": true,
                 "UsesPictureSettings": 1,
                 "VideoAvgBitrate": "2500",
                 "VideoEncoder": "H.264 (x264)",
         ],
         "Default": 0,
         "Folder": true,
-        "PresetBuildNumber": 2013061301,
         "PresetName": "Devices",
         "Type": 0
     },
                 "PictureKeepRatio": 0,
                 "PictureLeftCrop": 0,
                 "PictureModulus": 2,
-                "PicturePAR": 2,
+                "PicturePAR": "loose",
                 "PictureRightCrop": 0,
                 "PictureTopCrop": 0,
                 "PictureWidth": 0,
                 "PresetName": "Normal",
                 "Subtitles": "None",
                 "Type": 0,
-                "UsesPictureFilters": 1,
+                "UsesPictureFilters": true,
                 "UsesPictureSettings": 1,
                 "VideoAvgBitrate": "2500",
                 "VideoEncoder": "H.264 (x264)",
                 "PictureKeepRatio": 0,
                 "PictureLeftCrop": 0,
                 "PictureModulus": 2,
-                "PicturePAR": 2,
+                "PicturePAR": "loose",
                 "PictureRightCrop": 0,
                 "PictureTopCrop": 0,
                 "PictureWidth": 0,
                 "PresetName": "High Profile",
                 "Subtitles": "None",
                 "Type": 0,
-                "UsesPictureFilters": 1,
+                "UsesPictureFilters": true,
                 "UsesPictureSettings": 1,
                 "VideoAvgBitrate": "2500",
                 "VideoEncoder": "H.264 (x264)",
         ],
         "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 (file)
index 0000000..b0b3e5c
--- /dev/null
@@ -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";
index 7bb3f2a56707b6eaa862f4b5ea90990e06c59e58..8a7b72522dca8f3fe1ac3ecb4d1417f305054ae3 100644 (file)
@@ -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)
index 7a74291534138ab209ddc7bd32bb5958dc87485f..f4ec2308fc7a1fa1e944f695b4addaa484c5c8d7 100644 (file)
@@ -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 );
index 38418ac3bbb709426915565a4928c6bcd1da76cd..215455cda56504da3dfd1035d584386b66b0f12b 100644 (file)
 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()
index 7fd12b56589a235a6f05c2bc08abef0f6eec50df..866803f58833cc0b6a46a6f056e5eefbe79dfe26 100644 (file)
@@ -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;
index 52898e413b9c2479c8e70b8473dd5db0bf073e14..2a4f71cfe09dd0829728b0c9a3583145abc495dd 100644 (file)
@@ -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);
index f40520a23936d4125d08a1d1166dbc3dcb6673b6..34466da3436a3187dd9c7ad1796f0a7b18afe254 100644 (file)
@@ -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)) ||
index 21aab70af7bba0d2ad882149fc2f4525496e5122..7e7b82bd596f963fcc855c1029914d60ec9ed45c 100644 (file)
@@ -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 (file)
index 0000000..3742b1d
--- /dev/null
@@ -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>
index 3d7d55ee37013642c300250a4b50aa2331b32ff6..f98930dd5dc082a5e13a5f5852d0654660c6b991 100644 (file)
@@ -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 (file)
index 0000000..f610145
--- /dev/null
@@ -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 (file)
index 0000000..901aeb4
--- /dev/null
@@ -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 (file)
index 0000000..3fef7c6
--- /dev/null
@@ -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 (file)
index 0000000..5e7c460
--- /dev/null
@@ -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 (file)
index 0000000..5bf5d4d
--- /dev/null
@@ -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 (file)
index 0000000..30449e4
--- /dev/null
@@ -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 (file)
index 0000000..95921d3
--- /dev/null
@@ -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 (file)
index 0000000..1038719
--- /dev/null
@@ -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()
+
index a3b18a2270592db42f83f069534b8c25d70663fd..d489ffd08a53e851bb4d0b16b4068226ef293b50 100644 (file)
 #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,3625 +784,3310 @@ static int HandleEvents( hb_handle_t * h )
                 break;
             }
 
-            PrintTitleInfo( title, title_set->feature );
+            fprintf( stderr, "+ Using preset: %s\n",
+                hb_value_get_string(hb_dict_get(preset_dict, "PresetName")));
 
-            /* Set job settings */
-            job = hb_job_init(title);
-            filter_cfr        = job->cfr;
-            filter_vrate      = job->vrate;
+            PrintTitleInfo(title, title_set->feature);
 
+            // 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)
+            {
+                die = 1;
+                return -1;
+            }
 
-            if( chapter_start && chapter_end && !stop_at_pts && !start_at_preview && !stop_at_frame && !start_at_pts && !start_at_frame )
+            hb_job_t   * job = NULL;
+            job = hb_dict_to_job(h, job_dict);
+            hb_value_free(&job_dict);
+            if (job == NULL)
             {
-                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 );
+                fprintf(stderr, "Error in setting up job! Aborting.\n");
+                die = 1;
+                return -1;
             }
 
-            if ( angle )
+            hb_add( h, job );
+            hb_job_close( &job );
+            hb_start( h );
+            break;
+        }
+
+#define p s.param.working
+        case HB_STATE_SEARCHING:
+            fprintf( stdout, "\rEncoding: task %d of %d, Searching for start time, %.2f %%",
+                     p.pass, p.pass_count, 100.0 * p.progress );
+            if( p.seconds > -1 )
             {
-                job->angle = angle;
+                fprintf( stdout, " (ETA %02dh%02dm%02ds)",
+                         p.hours, p.minutes, p.seconds );
             }
+            fflush(stdout);
+            break;
 
-            if (preset)
+        case HB_STATE_WORKING:
+            fprintf( stdout, "\rEncoding: task %d of %d, %.2f %%",
+                     p.pass, p.pass_count, 100.0 * p.progress );
+            if( p.seconds > -1 )
             {
-                fprintf( stderr, "+ Using preset: %s\n", preset_name);
+                fprintf( stdout, " (%.2f fps, avg %.2f fps, ETA "
+                         "%02dh%02dm%02ds)", p.rate_cur, p.rate_avg,
+                         p.hours, p.minutes, p.seconds );
+            }
+            fflush(stdout);
+            break;
+#undef p
 
-                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 )
+#define p s.param.muxing
+        case HB_STATE_MUXING:
+        {
+            if (show_mux_warning)
+            {
+                fprintf( stdout, "\rMuxing: this may take awhile..." );
+                fflush(stdout);
+                show_mux_warning = 0;
+            }
+            break;
+        }
+#undef p
+
+#define p s.param.workdone
+        case HB_STATE_WORKDONE:
+            /* Print error if any, then exit */
+            switch( p.error )
+            {
+                case HB_ERROR_NONE:
+                    fprintf( stderr, "\nEncode done!\n" );
+                    break;
+                case HB_ERROR_CANCELED:
+                    fprintf( stderr, "\nEncode canceled.\n" );
+                    break;
+                default:
+                    fprintf( stderr, "\nEncode failed (error %x).\n",
+                             p.error );
+            }
+            done_error = p.error;
+            die = 1;
+            break;
+#undef p
+    }
+    return 0;
+}
+
+/****************************************************************************
+ * SigHandler:
+ ****************************************************************************/
+static volatile int64_t i_die_date = 0;
+void SigHandler( int i_signal )
+{
+    done_error = HB_ERROR_CANCELED;
+    if( die == 0 )
+    {
+        die = 1;
+        i_die_date = hb_get_date();
+        fprintf( stderr, "Signal %d received, terminating - do it "
+                 "again in case it gets stuck\n", i_signal );
+    }
+    else if( i_die_date + 500 < hb_get_date() )
+    {
+        fprintf( stderr, "Dying badly, files might remain in your /tmp\n" );
+        exit( done_error );
+    }
+}
+
+/****************************************************************************
+ * ShowHelp:
+ ****************************************************************************/
+static void ShowHelp()
+{
+    int i;
+    const hb_rate_t *rate;
+    const hb_dither_t *dither;
+    const hb_mixdown_t *mixdown;
+    const hb_encoder_t *encoder;
+    const hb_container_t *container;
+    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,\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)
+    {
+        fprintf(out, "%s", container->short_name);
+        if (hb_container_get_next(container) != NULL)
+        {
+            fprintf(out, "/");
+        }
+        else
+        {
+            fprintf(out, ")\n");
+        }
+    }
+    fprintf(out,
+"                           (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)
+    {
+        fprintf(out, "%s", encoder->short_name);
+        if (hb_video_encoder_get_next(encoder) != NULL)
+        {
+            fprintf(out, "/");
+        }
+        else
+        {
+            fprintf(out, "\n");
+        }
+    }
+    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\n"
+"                           (works with x264 and x265)\n"
+"   -r, --rate              Set video framerate\n"
+"                           (" );
+    rate = NULL;
+    while ((rate = hb_video_framerate_get_next(rate)) != NULL)
+    {
+        fprintf(out, "%s", rate->name);
+        if (hb_video_framerate_get_next(rate) != NULL)
+        {
+            fprintf(out, "/");
+        }
+    }
+    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"
+"       --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)
+    {
+        fprintf(out, "                               %s\n",
+                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"
+"                           Separate tracks by commas.\n"
+"                           Defaults:\n");
+    container = NULL;
+    while ((container = hb_container_get_next(container)) != NULL)
+    {
+        int audio_encoder = hb_audio_encoder_get_default(container->format);
+        fprintf(out, "                               %-8s %s\n",
+                container->short_name,
+                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"
+"                           (" );
+    i       = 0;
+    encoder = NULL;
+    while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL)
+    {
+        if ((encoder->codec &  HB_ACODEC_PASS_FLAG) &&
+            (encoder->codec != HB_ACODEC_AUTO_PASS))
+        {
+            if (i)
+            {
+                fprintf(out, "/");
+            }
+            i = 1;
+            // skip "copy:"
+            fprintf(out, "%s", encoder->short_name + 5);
+        }
+    }
+    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)
+    {
+        fprintf(out, "                               %s\n",
+                mixdown->short_name);
+    }
+    fprintf(out,
+"                           Separate tracks by commas.\n"
+"                           Defaults:\n");
+    encoder = NULL;
+    while((encoder = hb_audio_encoder_get_next(encoder)) != NULL)
+    {
+        if (!(encoder->codec & HB_ACODEC_PASS_FLAG))
+        {
+            // layout: UINT64_MAX (all channels) should work with any mixdown
+            int mixdown = hb_mixdown_get_default(encoder->codec, UINT64_MAX);
+            // assumes that the encoder short name is <= 16 characters long
+            fprintf(out, "                               %-16s up to %s\n",
+                    encoder->short_name, hb_mixdown_get_short_name(mixdown));
+        }
+    }
+    fprintf(out,
+"       --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)
+    {
+        fprintf(out, "%s", rate->name);
+        if (hb_audio_samplerate_get_next(rate) != NULL)
+        {
+            fprintf(out, "/");
+        }
+    }
+    fprintf( out, " kHz)\n"
+"                           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)
+    {
+        if (hb_audio_dither_is_supported(encoder->codec))
+        {
+            if (i)
+            {
+                fprintf(out, "/");
+            }
+            i = 1;
+            fprintf(out, "%s", encoder->short_name);
+        }
+    }
+    fprintf(out, ").\n");
+    fprintf(out,
+    "                            Options:\n");
+    dither = NULL;
+    while ((dither = hb_audio_dither_get_next(dither)) != NULL)
+    {
+        if (dither->method == hb_audio_dither_get_default())
+        {
+            fprintf(out, "                               %s (default)\n",
+                    dither->short_name);
+        }
+        else
+        {
+            fprintf(out, "                               %s\n",
+                    dither->short_name);
+        }
+    }
+    fprintf(out,
+"   -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())
+{
+    fprintf(out, "/qsv");
+}
+#endif
+     fprintf( out, "> or omitted (default settings)\n"
+     "           or\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"
+"   --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
+}
+
+/****************************************************************************
+ * ShowPresets:
+ ****************************************************************************/
+static const char *
+reverse_search_char(const char *front, const char *back, char delim)
+{
+    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++)
                     {
-                        anamorphic_mode = 2;
+                        Indent(stderr, "    ", indent+1);
+                        fprintf(stderr, "%s\n", split[ii]);
                     }
-                    modulus = 2;
-                    job->chapter_markers = 1;
+                    str_vfree(split);
+                }
+            }
+        }
+    }
+}
+
+static char* strchr_quote(char *pos, char c, char q)
+{
+    if (pos == NULL)
+        return NULL;
+
+    while (*pos != 0 && *pos != c)
+    {
+        if (*pos == q)
+        {
+            pos = strchr_quote(pos+1, q, 0);
+            if (pos == NULL)
+                return NULL;
+            pos++;
+        }
+        else if (*pos == '\\' && *(pos+1) != 0)
+            pos += 2;
+        else
+            pos++;
+    }
+    if (*pos != c)
+        return NULL;
+    return pos;
+}
+
+static char *strndup_quote(char *str, char q, int len)
+{
+    if (str == NULL)
+        return NULL;
+
+    char * res;
+    int str_len = strlen( str );
+    int src = 0, dst = 0;
+    res = malloc( len > str_len ? str_len + 1 : len + 1 );
+
+    while (str[src] != 0 && src < len)
+    {
+        if (str[src] == q)
+            src++;
+        else if (str[src] == '\\' && str[src+1] != 0)
+        {
+            res[dst++] = str[src+1];
+            src += 2;
+        }
+        else
+            res[dst++] = str[src++];
+    }
+    res[dst] = '\0';
+    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;
+    char *  end;
+    char ** ret;
+    int     count, i;
+    char quote = '"';
+
+    if (delem == '"')
+    {
+        quote = '\'';
+    }
+    if ( str == NULL || str[0] == 0 )
+    {
+        ret = malloc( sizeof(char*) );
+        *ret = NULL;
+        return ret;
+    }
+
+    // Find number of elements in the string
+    count = 1;
+    pos = str;
+    while ( ( pos = strchr_quote( pos, delem, quote ) ) != NULL )
+    {
+        count++;
+        pos++;
+    }
+
+    ret = calloc( ( count + 1 ), sizeof(char*) );
+
+    pos = str;
+    for ( i = 0; i < count - 1; i++ )
+    {
+        end = strchr_quote( pos, delem, quote );
+        ret[i] = strndup_quote(pos, quote, end - pos);
+        pos = end + 1;
+    }
+    ret[i] = strndup_quote(pos, quote, strlen(pos));
+
+    return ret;
+}
+
+static void str_vfree( char **strv )
+{
+    int i;
+
+    if (strv == NULL)
+        return;
+
+    for ( i = 0; strv[i]; i++ )
+    {
+        free( strv[i] );
+    }
+    free( strv );
+}
+
+static double parse_hhmmss_strtok()
+{
+    /* Assumes strtok has already been called on a string.  Intends to parse
+     * hh:mm:ss.ss or mm:ss.ss or ss.ss or ss into double seconds.  Actually
+     * parses a list of doubles separated by colons, multiplying the current
+     * result by 60 then adding in the next value.  Malformed input does not
+     * result in a explicit error condition but instead returns an
+     * intermediate result. */
+    double duration = 0;
+    char* str;
+    while ((str = strtok(NULL, ":")) != NULL)
+        duration = 60*duration + strtod(str, NULL);
+    return duration;
+}
+
+/****************************************************************************
+ * ParseOptions:
+ ****************************************************************************/
+static int ParseOptions( int argc, char ** argv )
+{
+
+    #define PREVIEWS             257
+    #define START_AT_PREVIEW     258
+    #define START_AT             259
+    #define STOP_AT              260
+    #define ANGLE                261
+    #define DVDNAV               262
+    #define DISPLAY_WIDTH        263
+    #define PIXEL_ASPECT         264
+    #define MODULUS              265
+    #define KEEP_DISPLAY_ASPECT  266
+    #define SUB_BURNED           267
+    #define SUB_DEFAULT          268
+    #define NATIVE_DUB           269
+    #define SRT_FILE             270
+    #define SRT_CODESET          271
+    #define SRT_OFFSET           272
+    #define SRT_LANG             273
+    #define SRT_DEFAULT          274
+    #define SRT_BURN             275
+    #define ROTATE_FILTER        276
+    #define SCAN_ONLY            277
+    #define MAIN_FEATURE         278
+    #define MIN_DURATION         279
+    #define AUDIO_GAIN           280
+    #define ALLOWED_AUDIO_COPY   281
+    #define AUDIO_FALLBACK       282
+    #define LOOSE_CROP           283
+    #define ENCODER_PRESET       284
+    #define ENCODER_PRESET_LIST  285
+    #define ENCODER_TUNE         286
+    #define ENCODER_TUNE_LIST    287
+    #define ENCODER_PROFILE      288
+    #define ENCODER_PROFILE_LIST 289
+    #define ENCODER_LEVEL        290
+    #define ENCODER_LEVEL_LIST   291
+    #define NORMALIZE_MIX        293
+    #define AUDIO_DITHER         294
+    #define QSV_BASELINE         295
+    #define QSV_ASYNC_DEPTH      296
+    #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( ;; )
+    {
+        static struct option long_options[] =
+          {
+            { "help",        no_argument,       NULL,    'h' },
+            { "update",      no_argument,       NULL,    'u' },
+            { "verbose",     optional_argument, NULL,    'v' },
+            { "no-dvdnav",   no_argument,       NULL,    DVDNAV },
+            { "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,       &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 },
+            { "scan",        no_argument,       NULL,    SCAN_ONLY },
+            { "main-feature",no_argument,       NULL,    MAIN_FEATURE },
+            { "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 },
+            { "subtitle-default", optional_argument,   NULL,    SUB_DEFAULT },
+            { "srt-file",    required_argument, NULL, SRT_FILE },
+            { "srt-codeset", required_argument, NULL, SRT_CODESET },
+            { "srt-offset",  required_argument, NULL, SRT_OFFSET },
+            { "srt-lang",    required_argument, NULL, SRT_LANG },
+            { "srt-default", optional_argument, NULL, SRT_DEFAULT },
+            { "srt-burn",    optional_argument, NULL, SRT_BURN },
+            { "native-language", required_argument, NULL,'N' },
+            { "native-dub",  no_argument,       NULL,    NATIVE_DUB },
+            { "encoder",     required_argument, NULL,    'e' },
+            { "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' },
+            { "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", 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,       },
+            { "x264-preset",          required_argument, NULL, ENCODER_PRESET,       },
+            { "x265-preset",          required_argument, NULL, ENCODER_PRESET,       },
+            { "x264-tune",            required_argument, NULL, ENCODER_TUNE,         },
+            { "x265-tune",            required_argument, NULL, ENCODER_TUNE,         },
+            { "x264-profile",         required_argument, NULL, ENCODER_PROFILE,      },
+            { "h264-profile",         required_argument, NULL, ENCODER_PROFILE,      },
+            { "h265-profile",         required_argument, NULL, ENCODER_PROFILE,      },
+            { "h264-level",           required_argument, NULL, ENCODER_LEVEL,        },
+            { "h265-level",           required_argument, NULL, ENCODER_LEVEL,        },
+            // encoder preset/tune/options/profile/level
+            { "encoder-preset",       required_argument, NULL, ENCODER_PRESET,       },
+            { "encoder-preset-list",  required_argument, NULL, ENCODER_PRESET_LIST,  },
+            { "encoder-tune",         required_argument, NULL, ENCODER_TUNE,         },
+            { "encoder-tune-list",    required_argument, NULL, ENCODER_TUNE_LIST,    },
+            { "encopts",              required_argument, NULL, 'x',                  },
+            { "encoder-profile",      required_argument, NULL, ENCODER_PROFILE,      },
+            { "encoder-profile-list", required_argument, NULL, ENCODER_PROFILE_LIST, },
+            { "encoder-level",        required_argument, NULL, ENCODER_LEVEL,        },
+            { "encoder-level-list",   required_argument, NULL, ENCODER_LEVEL_LIST,   },
+
+            { "vb",          required_argument, NULL,    'b' },
+            { "quality",     required_argument, NULL,    'q' },
+            { "ab",          required_argument, NULL,    'B' },
+            { "aq",          required_argument, NULL,    'Q' },
+            { "ac",          required_argument, NULL,    'C' },
+            { "rate",        required_argument, NULL,    'r' },
+            { "arate",       required_argument, NULL,    'R' },
+            { "turbo",       no_argument,       NULL,    'T' },
+            { "maxHeight",   required_argument, NULL,    'Y' },
+            { "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' },
+            { "previews",    required_argument, NULL,    PREVIEWS },
+            { "start-at-preview", required_argument, NULL, START_AT_PREVIEW },
+            { "start-at",    required_argument, NULL,    START_AT },
+            { "stop-at",    required_argument, NULL,     STOP_AT },
+            { "vfr",         no_argument,       &cfr,    0 },
+            { "cfr",         no_argument,       &cfr,    1 },
+            { "pfr",         no_argument,       &cfr,    2 },
+            { "audio-copy-mask", required_argument, NULL, ALLOWED_AUDIO_COPY },
+            { "audio-fallback",  required_argument, NULL, AUDIO_FALLBACK },
+            { 0, 0, 0, 0 }
+          };
+
+        int option_index = 0;
+        int c;
+        int cur_optind;
+
+        cur_optind = optind;
+        c = getopt_long( argc, argv,
+                         "hv::uC:f:4i:Io:PUt:c:m::M:a:A:6:s:F::N:e:E:Q:C:"
+                         "2dD:7895gOw:l:n:b:q:S:B:r:R:x:TY:X:Z:z",
+                         long_options, &option_index );
+        if( c < 0 )
+        {
+            break;
+        }
+
+        switch( c )
+        {
+            case 0:
+                /* option was handled entirely in getopt_long */
+                break;
+            case 'h':
+                ShowHelp();
+                exit( 0 );
+            case 'u':
+                update = 1;
+                break;
+            case 'v':
+                if( optarg != NULL )
+                {
+                    debug = atoi( optarg );
                 }
-                if (!strcasecmp(preset_name, "Android"))
+                else
                 {
-                    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;
+                    debug = 1;
                 }
-                if (!strcasecmp(preset_name, "Android Tablet"))
+                break;
+            case 'Z':
+                preset_name = strdup(optarg);
+                break;
+            case 'z':
+                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] != '-')
                 {
-                    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 )
+                    int result = hb_presets_add_path(argv[optind]);
+                    if (result != 0)
                     {
-                        anamorphic_mode = 2;
+                        fprintf(stderr, "Preset import failed, file (%s)\n",
+                                argv[optind]);
                     }
-                    modulus = 2;
+                    optind++;
                 }
-                if (!strcasecmp(preset_name, "Windows Phone 8"))
+            }   break;
+            case PRESET_IMPORT_GUI:
+                hb_presets_gui_init();
+                break;
+            case DVDNAV:
+                dvdnav = 0;
+                break;
+
+            case 'f':
+                format = strdup( optarg );
+                break;
+            case 'i':
+                input = strdup( optarg );
+#ifdef __APPLE_CC__
+                char *devName = bsd_name_for_path( input ); // alloc
+                if( devName != NULL )
                 {
-                    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 )
+                    if( device_is_dvd( devName ))
                     {
-                        anamorphic_mode = 0;
+                        free( input );
+                        input = malloc( strlen( "/dev/" ) + strlen( devName ) + 1 );
+                        sprintf( input, "/dev/%s", devName );
                     }
-                    modulus = 2;
+                    free( devName );
+                }
+#endif
+                break;
+            case 'o':
+                output = strdup( optarg );
+                break;
+            case 't':
+                titleindex = atoi( optarg );
+                break;
+            case SCAN_ONLY:
+                titlescan = 1;
+                break;
+            case MAIN_FEATURE:
+                main_feature = 1;
+                break;
+            case 'c':
+            {
+                int start, end;
+                if( sscanf( optarg, "%d-%d", &start, &end ) == 2 )
+                {
+                    chapter_start = start;
+                    chapter_end   = end;
+                }
+                else if( sscanf( optarg, "%d", &start ) == 1 )
+                {
+                    chapter_start = start;
+                    chapter_end   = chapter_start;
+                }
+                else
+                {
+                    fprintf( stderr, "chapters: Invalid syntax (%s)\n",
+                             optarg );
+                    return -1;
+                }
+                break;
+            }
+            case ANGLE:
+                angle = atoi( optarg );
+                break;
+            case 'm':
+                if( optarg != NULL )
+                {
+                    marker_file = strdup( optarg );
+                }
+                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 = str_split(optarg, ',');
+                }
+                else
+                {
+                    atracks = str_split("1", ',');
+                }
+                break;
+            case '6':
+                if( optarg != NULL )
+                {
+                    mixdowns = str_split(optarg, ',');
+                }
+                break;
+            case 'D':
+                if( optarg != NULL )
+                {
+                    dynamic_range_compression = str_split(optarg, ',');
+                }
+                break;
+            case AUDIO_GAIN:
+                if( optarg != NULL )
+                {
+                    audio_gain = str_split(optarg, ',');
                 }
-                if (!strcasecmp(preset_name, "Normal"))
+                break;
+            case AUDIO_DITHER:
+                if (optarg != NULL)
                 {
-                    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;
+                    audio_dither = str_split(optarg, ',');
                 }
-                if (!strcasecmp(preset_name, "High Profile"))
+                break;
+            case NORMALIZE_MIX:
+                normalize_mix_level = str_split(optarg, ',');
+                break;
+            case 's':
+                subtracks = str_split( optarg, ',' );
+                break;
+            case 'F':
+                subforce = str_split( optarg, ',' );
+                break;
+            case SUB_BURNED:
+                if (optarg != NULL)
                 {
-                    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 )
+                    if (!strcasecmp(optarg, "native") ||
+                        !strcasecmp(optarg, "scan"))
+                        subburn_native = 1;
+                    else
                     {
-                        anamorphic_mode = 2;
+                        subburn = strtol(optarg, NULL, 0);
                     }
-                    modulus = 2;
-                    job->chapter_markers = 1;
                 }
-            }
-
-            if ( chapter_markers )
-            {
-                job->chapter_markers = chapter_markers;
-
-                if( marker_file != NULL )
+                else
                 {
-                    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
+                    subburn = 1;
+                }
+                if (subburn > 0)
+                {
+                    if (subtracks != NULL && str_vlen(subtracks) >= subburn &&
+                        !strcasecmp("scan", subtracks[subburn-1]))
                     {
-                        /* 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 );
+                        subburn_native = 1;
                     }
                 }
-            }
-
-            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)
+                break;
+            case SUB_DEFAULT:
+                if (optarg != NULL)
                 {
-                    keep_display_aspect = 0;
+                    subdefault = strtol(optarg, NULL, 0);
                 }
                 else
                 {
-                    uiGeo.mode = HB_ANAMORPHIC_CUSTOM;
+                    subdefault = 1;
                 }
-            }
-            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)
+                break;
+            case 'N':
             {
-                if (height != 0)
+                const iso639_lang_t *lang = lang_lookup(optarg);
+                if (lang != NULL)
                 {
-                    fprintf(stderr, "display_width (%d), width (%d), and height (%d) can not all be specified, ignoring height", display_width, width, height);
+                    native_language = strdup(lang->iso639_2);
                 }
-                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)
+                else
                 {
-                    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;
+                    fprintf(stderr, "Invalid native language (%s)\n", optarg);
                     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 )
+            }   break;
+            case NATIVE_DUB:
+                native_dub = 1;
+                break;
+            case SRT_FILE:
+                srtfile = str_split( optarg, ',' );
+                break;
+            case SRT_CODESET:
+                srtcodeset = str_split( optarg, ',' );
+                break;
+            case SRT_OFFSET:
+                srtoffset = str_split( optarg, ',' );
+                break;
+            case SRT_LANG:
+                srtlang = str_split( optarg, ',' );
+                break;
+            case SRT_DEFAULT:
+                if( optarg != NULL )
                 {
-                    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;
-                        }
-                    }
+                    srtdefault = atoi( optarg );
                 }
                 else
                 {
-                    fprintf( stderr, "Warning: Native language (dubbing) selection ignored since an audio track has already been selected\n" );
+                    srtdefault = 1 ;
                 }
-            }
-
-            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 )
+                break;
+            case SRT_BURN:
+                if( optarg != NULL )
                 {
-                    num_audio_tracks--;
+                    srtburn = atoi( optarg );
                 }
-                if( audio != NULL )
+                else
                 {
-                    hb_list_rem( audios, audio );
-                    if( audio->out.name )
-                    {
-                        free( audio->out.name );
-                    }
-                    free( audio );
+                    srtburn = 1 ;
                 }
-            }
-
-            /* Audio Codecs */
-            i = 0;
-            if( acodecs )
-            {
-                char * token = strtok(acodecs, ",");
-                if( token == NULL )
-                    token = acodecs;
-                while ( token != NULL )
+                break;
+            case '2':
+                twoPass = 1;
+                break;
+            case 'd':
+                free(deinterlace);
+                if (optarg != 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++;
+                    deinterlace = strdup(optarg);
                 }
-            }
-            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++)
+                else
                 {
-                    audio = hb_list_audio_config_item(job->list_audio, i);
-                    audio->out.codec = acodec;
+                    deinterlace = strdup("default");
                 }
-            }
-            // 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)
+                break;
+            case '7':
+                free(deblock);
+                if( optarg != 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;
-                        }
-                    }
+                    deblock = strdup(optarg);
                 }
-            }
-            /* 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 )
+                else
                 {
-                    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, ",");
+                    deblock = strdup("5");
                 }
-            }
-            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++)
+                break;
+            case '8':
+                free(hqdn3d);
+                if (optarg != NULL)
                 {
-                    audio = hb_list_audio_config_item(job->list_audio, i);
-                    if( use_default )
-                        arate = audio->in.samplerate;
-                    audio->out.samplerate = arate;
+                    hqdn3d = strdup(optarg);
                 }
-            }
-            /* Sample Rate */
-            
-            /* Audio Mixdown */
-            i = 0;
-            if ( mixdowns )
-            {
-                char * token = strtok(mixdowns, ",");
-                if (token == NULL)
-                    token = mixdowns;
-                while ( token != NULL )
+                else
                 {
-                    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, ",");
+                    hqdn3d = strdup("default");
                 }
-            }
-            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++)
+                break;
+            case FILTER_NLMEANS:
+                free(nlmeans);
+                if (optarg != NULL)
                 {
-                    audio = hb_list_audio_config_item(job->list_audio, i);
-                    audio->out.mixdown = mixdown;
+                    nlmeans = strdup(optarg);
                 }
-            }
-            /* Audio Mixdown */
-
-            /* Audio Bitrate */
-            i = 0;
-            if( abitrates )
-            {
-                for ( i = 0; abitrates[i] != NULL && i < num_audio_tracks; i++ )
+                else
                 {
-                    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);
-                    }
+                    nlmeans = strdup("light");
                 }
-            }
-            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++)
+                break;
+            case FILTER_NLMEANS_TUNE:
+                free(nlmeans_tune);
+                nlmeans_tune = strdup(optarg);
+                break;
+            case '9':
+                free(detelecine);
+                if (optarg != NULL)
                 {
-                    audio = hb_list_audio_config_item(job->list_audio, i);
-                    audio->out.bitrate = abitrate;
+                    detelecine = strdup(optarg);
                 }
-            }
-            /* Audio Bitrate */
-
-            /* Audio Quality */
-            i = 0;
-            if( aqualities )
-            {
-                for ( i = 0; aqualities[i] != NULL && i < num_audio_tracks; i++ )
+                else
                 {
-                    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;
-                    }
+                    detelecine = strdup("default");
                 }
-            }
-            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++)
+                break;
+            case '5':
+                free(decomb);
+                if (optarg != NULL)
                 {
-                    audio = hb_list_audio_config_item(job->list_audio, i);
-                    if( audio->out.bitrate <= 0 )
-                        audio->out.quality = aquality;
+                    decomb = strdup(optarg);
                 }
-            }
-            /* Audio Quality */
-
-            /* Audio Compression Level */
-            i = 0;
-            if( acompressions )
-            {
-                for ( i = 0; acompressions[i] != NULL && i < num_audio_tracks; i++ )
+                else
                 {
-                    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;
-                    }
+                    decomb = strdup("default");
                 }
-            }
-            // 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 )
+                break;
+            case ROTATE_FILTER:
+                free(rotate);
+                if (optarg != 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, ",");
+                    rotate = strdup(optarg);
                 }
-            }
-            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++)
+                else
                 {
-                    audio = hb_list_audio_config_item(job->list_audio, i);
-                    audio->out.dynamic_range_compression = d_r_c;
+                    rotate = strdup("3");
                 }
-            }
-            /* Audio DRC */
-
-            /* Audio Gain */
-            i = 0;
-            if ( audio_gain )
-            {
-                char * token = strtok(audio_gain, ",");
-                if (token == NULL)
-                    token = audio_gain;
-                while ( token != NULL )
+                break;
+            case KEEP_DISPLAY_ASPECT:
+                if( optarg != 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, ",");
+                    keep_display_aspect = atoi(optarg);
                 }
-            }
-            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++)
+                else
                 {
-                    audio = hb_list_audio_config_item(job->list_audio, i);
-                    audio->out.gain = gain;
+                    keep_display_aspect = 1;
                 }
-            }
-            /* Audio Gain */
-
-            /* Audio Dither */
-            if (audio_dither != NULL)
-            {
-                int dither_method = hb_audio_dither_get_default();
-                for (i = 0; audio_dither[i] != NULL; i++)
+                break;
+            case DISPLAY_WIDTH:
+                if( optarg != NULL )
                 {
-                    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]);
-                    }
+                    display_width = atoi(optarg);
                 }
-                if (i < num_audio_tracks && i == 1)
+                break;
+            case PIXEL_ASPECT:
+                if( optarg != NULL )
                 {
-                    /*
-                     * 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++;
-                    }
+                    sscanf(optarg, "%i:%i", &par_width, &par_height);
                 }
-            }
-            /* 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++ )
+                break;
+            case MODULUS:
+                if( optarg != NULL )
                 {
-                    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);
-                    }
+                    modulus = atoi(optarg);
                 }
-            }
-            if (i < num_audio_tracks && i == 1)
+                break;
+            case 'e':
             {
-                /* We have fewer inputs than audio tracks,
-                 * and we only have one input, use
-                 * that for all tracks.
-                 */
-                for (; i < num_audio_tracks; i++)
+                vcodec = strdup(optarg);
+                break;
+            }
+            case 'E':
+                if( optarg != NULL )
                 {
-                    audio = hb_list_audio_config_item(job->list_audio, i);
-                    audio->out.normalize_mix_level = norm;
+                    acodecs = str_split(optarg, ',');
                 }
-            }
-            /* Audio Mix Normalization */
-
-            /* Audio Track Names */
-            if ( anames )
+                break;
+            case 'w':
+                width = atoi( optarg );
+                break;
+            case 'l':
+                height = atoi( optarg );
+                break;
+            case 'n':
             {
-                char * token;
-                for ( i = 0; anames[i] != NULL && i < num_audio_tracks; i++ )
+                int    i;
+                char * tmp = optarg;
+                for( i = 0; i < 4; 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( !*tmp )
+                        break;
+                    crop[i] = strtol( tmp, &tmp, 0 );
+                    tmp++;
                 }
+                break;
             }
-            if( i < num_audio_tracks && i == 1 )
+            case LOOSE_CROP:
+                if (optarg != NULL)
+                    loose_crop = atoi(optarg);
+                else
+                    loose_crop = 1;
+                break;
+            case 'r':
             {
-                /* We have exactly one name and more than one audio track. Use the same
-                 * name for all tracks. */
-                for ( ; i < num_audio_tracks; i++)
+                vrate = strdup(optarg);
+                if (!cfr)
                 {
-                    audio = hb_list_audio_config_item(job->list_audio, i);
-                    audio->out.name = strdup(anames[0]);
+                    cfr = 1;
                 }
+                break;
             }
-            /* 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 )
+            case 'R':
+                arates = str_split( optarg, ',' );
+                break;
+            case 'b':
+                vbitrate = atoi( optarg );
+                break;
+            case 'q':
+                vquality = atof( optarg );
+                break;
+            case 'B':
+                abitrates = str_split( optarg, ',' );
+                break;
+            case 'Q':
+                aqualities = str_split( optarg, ',' );
+                break;
+            case 'C':
+                acompressions = str_split( optarg, ',' );
+                break;
+            case ENCODER_PRESET:
+                encoder_preset = strdup( optarg );
+                break;
+            case ENCODER_TUNE:
+                encoder_tune = strdup( optarg );
+                break;
+            case 'x':
+                advanced_opts = strdup( optarg );
+                break;
+            case ENCODER_PROFILE:
+                encoder_profile = strdup( optarg );
+                break;
+            case ENCODER_LEVEL:
+                encoder_level = strdup( optarg );
+                break;
+            case ENCODER_PRESET_LIST:
+                fprintf(stderr, "Available --encoder-preset values for '%s' encoder:\n",
+                        hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg)));
+                print_string_list(stderr,
+                                  hb_video_encoder_get_presets(hb_video_encoder_get_from_name(optarg)),
+                                  "    ");
+                exit(0);
+            case ENCODER_TUNE_LIST:
+                fprintf(stderr, "Available --encoder-tune values for '%s' encoder:\n",
+                        hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg)));
+                print_string_list(stderr,
+                                  hb_video_encoder_get_tunes(hb_video_encoder_get_from_name(optarg)),
+                                  "    ");
+                exit(0);
+            case ENCODER_PROFILE_LIST:
+                fprintf(stderr, "Available --encoder-profile values for '%s' encoder:\n",
+                        hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg)));
+                print_string_list(stderr,
+                                  hb_video_encoder_get_profiles(hb_video_encoder_get_from_name(optarg)),
+                                  "    ");
+                exit(0);
+            case ENCODER_LEVEL_LIST:
+                fprintf(stderr, "Available --encoder-level values for '%s' encoder:\n",
+                        hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg)));
+                print_string_list(stderr,
+                                  hb_video_encoder_get_levels(hb_video_encoder_get_from_name(optarg)),
+                                  "    ");
+                exit(0);
+            case 'T':
+                fastfirstpass = 1;
+                break;
+            case 'Y':
+                maxHeight = atoi( optarg );
+                break;
+            case 'X':
+                maxWidth = atoi (optarg );
+                break;
+            case 'A':
+                if( optarg != NULL )
                 {
-                    // 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);
+                    anames = str_split( optarg, ',' );
                 }
-                else if( ( audio->out.codec & HB_ACODEC_PASS_FLAG ) &&
-                        !( audio->out.codec & audio->in.codec & HB_ACODEC_PASS_MASK ) )
+                break;
+            case PREVIEWS:
+                sscanf( optarg, "%i:%i", &preview_count, &store_previews );
+                break;
+            case START_AT_PREVIEW:
+                start_at_preview = atoi( optarg );
+                break;
+            case START_AT:
+            {
+                char * start_at_string = NULL;
+                char * start_at_token = NULL;
+                start_at_string = strdup( optarg );
+                start_at_token = strtok( start_at_string, ":");
+                if( !strcmp( start_at_token, "frame" ) )
                 {
-                    // 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));
+                    start_at_token = strtok( NULL, ":");
+                    start_at_frame = atoi(start_at_token);
                 }
-                // 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++ )
+                else if( !strcmp( start_at_token, "pts" ) )
                 {
-                    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 );
-                    }
+                    start_at_token = strtok( NULL, ":");
+                    sscanf( start_at_token, "%"SCNd64, &start_at_pts );
                 }
-            }
-
-            if( srtfile )
-            {
-                int i;
-                hb_subtitle_config_t sub_config;
-
-                for( i=0; srtfile[i] != NULL; i++ )
+                else if( !strcmp( start_at_token, "duration" ) )
                 {
-                    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);
+                    double duration_seconds = parse_hhmmss_strtok();
+                    start_at_pts = (int64_t)(duration_seconds * 90e3);
                 }
+                free( start_at_string );
+                break;
             }
-
-            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 )
+            case STOP_AT:
             {
-                audio = hb_list_audio_config_item(job->list_audio, 0);
-                
-                if( audio ) 
+                char * stop_at_string = NULL;
+                char * stop_at_token = NULL;
+                stop_at_string = strdup( optarg );
+                stop_at_token = strtok( stop_at_string, ":");
+                if( !strcmp( stop_at_token, "frame" ) )
                 {
-                    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;
-                            }
-                        }
-                    }
+                    stop_at_token = strtok( NULL, ":");
+                    stop_at_frame = atoi(stop_at_token);
                 }
+                else if( !strcmp( stop_at_token, "pts" ) )
+                {
+                    stop_at_token = strtok( NULL, ":");
+                    sscanf( stop_at_token, "%"SCNd64, &stop_at_pts );
+                }
+                else if( !strcmp( stop_at_token, "duration" ) )
+                {
+                    double duration_seconds = parse_hhmmss_strtok();
+                    stop_at_pts = (int64_t)(duration_seconds * 90e3);
+                }
+                free( stop_at_string );
+                break;
             }
-
-            hb_job_set_file( job, output );
-
-            if( color_matrix_code )
-            {
-                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;
-            }
-
-            /* 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 );
-            break;
-        }
-
-#define p s.param.working
-        case HB_STATE_SEARCHING:
-            fprintf( stdout, "\rEncoding: task %d of %d, Searching for start time, %.2f %%",
-                     p.pass, p.pass_count, 100.0 * p.progress );
-            if( p.seconds > -1 )
-            {
-                fprintf( stdout, " (ETA %02dh%02dm%02ds)", 
-                         p.hours, p.minutes, p.seconds );
-            }
-            fflush(stdout);
-            break;
-
-        case HB_STATE_WORKING:
-            fprintf( stdout, "\rEncoding: task %d of %d, %.2f %%",
-                     p.pass, p.pass_count, 100.0 * p.progress );
-            if( p.seconds > -1 )
-            {
-                fprintf( stdout, " (%.2f fps, avg %.2f fps, ETA "
-                         "%02dh%02dm%02ds)", p.rate_cur, p.rate_avg,
-                         p.hours, p.minutes, p.seconds );
-            }
-            fflush(stdout);
-            break;
-#undef p
-
-#define p s.param.muxing
-        case HB_STATE_MUXING:
-        {
-            if (show_mux_warning)
+            case ALLOWED_AUDIO_COPY:
             {
-                fprintf( stdout, "\rMuxing: this may take awhile..." );
-                fflush(stdout);
-                show_mux_warning = 0;
+                audio_copy_list = str_split(optarg, ',');
+                break;
             }
-            break;
+            case AUDIO_FALLBACK:
+                acodec_fallback = strdup( optarg );
+                break;
+            case 'M':
+                if( optarg != NULL )
+                {
+                    if( !strcmp( optarg, "601" ) ||
+                        !strcmp( optarg, "ntsc" ) )
+                        color_matrix_code = 1;
+                    else if( !strcmp( optarg, "pal" ) )
+                        color_matrix_code = 2;
+                    else if( !strcmp( optarg, "709" ) )
+                        color_matrix_code = 3;
+                } break;
+            case MIN_DURATION:
+                min_title_duration = strtol( optarg, NULL, 0 );
+                break;
+#ifdef USE_QSV
+            case QSV_BASELINE:
+                hb_qsv_force_workarounds();
+                break;
+            case QSV_ASYNC_DEPTH:
+                qsv_async_depth = atoi(optarg);
+                break;
+            case QSV_IMPLEMENTATION:
+                hb_qsv_impl_set_preferred(optarg);
+                break;
+#endif
+            default:
+                fprintf( stderr, "unknown option (%s)\n", argv[cur_optind] );
+                return -1;
         }
-#undef p
 
-#define p s.param.workdone
-        case HB_STATE_WORKDONE:
-            /* Print error if any, then exit */
-            switch( p.error )
-            {
-                case HB_ERROR_NONE:
-                    fprintf( stderr, "\nEncode done!\n" );
-                    break;
-                case HB_ERROR_CANCELED:
-                    fprintf( stderr, "\nEncode canceled.\n" );
-                    break;
-                default:
-                    fprintf( stderr, "\nEncode failed (error %x).\n",
-                             p.error );
-            }
-            done_error = p.error;
-            die = 1;
-            break;
-#undef p
     }
-    return 0;
-}
 
-/****************************************************************************
- * SigHandler:
- ****************************************************************************/
-static volatile int64_t i_die_date = 0;
-void SigHandler( int i_signal )
-{
-    done_error = HB_ERROR_CANCELED;
-    if( die == 0 )
+    if (deblock != NULL)
     {
-        die = 1;
-        i_die_date = hb_get_date();
-        fprintf( stderr, "Signal %d received, terminating - do it "
-                 "again in case it gets stuck\n", i_signal );
-    }
-    else if( i_die_date + 500 < hb_get_date() )
-    {
-        fprintf( stderr, "Dying badly, files might remain in your /tmp\n" );
-        exit( done_error );
+        if (deblock_disable)
+        {
+            fprintf(stderr,
+                    "Incompatible options --deblock and --no-deblock\n");
+            return -1;
+        }
+        if (hb_validate_filter_settings(HB_FILTER_DEBLOCK, deblock))
+        {
+            fprintf(stderr, "Invalid deblock option %s\n", deblock);
+            return -1;
+        }
     }
-}
 
-/****************************************************************************
- * ShowHelp:
- ****************************************************************************/
-static void ShowHelp()
-{
-    int i;
-    const char *name;
-    const hb_rate_t *rate;
-    const hb_dither_t *dither;
-    const hb_mixdown_t *mixdown;
-    const hb_encoder_t *encoder;
-    const hb_container_t *container;
-    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 (");
-    container = NULL;
-    while ((container = hb_container_get_next(container)) != NULL)
+    if (detelecine != NULL)
     {
-        fprintf(out, "%s", container->short_name);
-        if (hb_container_get_next(container) != NULL)
+        if (detelecine_disable)
         {
-            fprintf(out, "/");
+            fprintf(stderr,
+                    "Incompatible options --detelecine and --no-detelecine\n");
+            return -1;
+        }
+        if (!hb_validate_filter_preset(HB_FILTER_DETELECINE,
+                                       detelecine, 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_DETELECINE, detelecine))
+        {
+            detelecine_custom = 1;
         }
         else
         {
-            fprintf(out, ")\n");
+            fprintf(stderr, "Invalid detelecine option %s\n", detelecine);
+            return -1;
         }
     }
-    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;
-    encoder = NULL;
-    while ((encoder = hb_video_encoder_get_next(encoder)) != NULL)
+
+    if (deinterlace != NULL)
     {
-        fprintf(out, "%s", encoder->short_name);
-        if (hb_video_encoder_get_next(encoder) != NULL)
+        if (deinterlace_disable)
         {
-            fprintf(out, "/");
+            fprintf(stderr,
+                    "Incompatible options --deinterlace and --no-deinterlace\n");
+            return -1;
         }
-        else
+        if (!hb_validate_filter_preset(HB_FILTER_DEINTERLACE,
+                                       deinterlace, NULL))
         {
-            fprintf(out, "\n");
+            // Nothing to do, but must validate preset before
+            // attempting to validate custom settings to prevent potential
+            // false positive
         }
-        if (encoder->codec == vcodec)
+        else if (!hb_validate_filter_settings(HB_FILTER_HQDN3D, deinterlace))
         {
-            name = encoder->short_name;
+            deinterlace_custom = 1;
         }
-    }
-    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 (" );
-    rate = NULL;
-    while ((rate = hb_video_framerate_get_next(rate)) != NULL)
-    {
-        fprintf(out, "%s", rate->name);
-        if (hb_video_framerate_get_next(rate) != NULL)
+        else
         {
-            fprintf(out, "/");
+            fprintf(stderr, "Invalid deinterlace option %s\n", deinterlace);
+            return -1;
         }
     }
-    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" );
-    encoder = NULL;
-    while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL)
-    {
-        fprintf(out, "                               %s\n",
-                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");
-    container = NULL;
-    while ((container = hb_container_get_next(container)) != NULL)
-    {
-        int audio_encoder = hb_audio_encoder_get_default(container->format);
-        fprintf(out, "                               %-8s %s\n",
-                container->short_name,
-                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"
-    "                            (" );
-    i       = 0;
-    encoder = NULL;
-    while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL)
+
+    if (decomb != NULL)
     {
-        if ((encoder->codec &  HB_ACODEC_PASS_FLAG) &&
-            (encoder->codec != HB_ACODEC_AUTO_PASS))
+        if (decomb_disable)
         {
-            if (i)
-            {
-                fprintf(out, "/");
-            }
-            i = 1;
-            // skip "copy:"
-            fprintf(out, "%s", encoder->short_name + 5);
+            fprintf(stderr,
+                    "Incompatible options --decomb and --no-decomb\n");
+            return -1;
         }
-    }
-    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");
-    // skip HB_AMIXDOWN_NONE
-    mixdown = hb_mixdown_get_next(NULL);
-    while((mixdown = hb_mixdown_get_next(mixdown)) != NULL)
-    {
-        fprintf(out, "                               %s\n",
-                mixdown->short_name);
-    }
-    fprintf(out,
-    "                            Separated by commas for more than one audio track.\n"
-    "                            Defaults:\n");
-    encoder = NULL;
-    while((encoder = hb_audio_encoder_get_next(encoder)) != NULL)
-    {
-        if (!(encoder->codec & HB_ACODEC_PASS_FLAG))
+        if (!hb_validate_filter_preset(HB_FILTER_DECOMB, decomb, NULL))
         {
-            // layout: UINT64_MAX (all channels) should work with any mixdown
-            int mixdown = hb_mixdown_get_default(encoder->codec, UINT64_MAX);
-            // assumes that the encoder short name is <= 16 characters long
-            fprintf(out, "                               %-16s up to %s\n",
-                    encoder->short_name, hb_mixdown_get_short_name(mixdown));
+            // 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;
         }
-    }
-    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) (" );
-    rate = NULL;
-    while ((rate = hb_audio_samplerate_get_next(rate)) != NULL)
-    {
-        fprintf(out, "%s", rate->name);
-        if (hb_audio_samplerate_get_next(rate) != NULL)
+        else
         {
-            fprintf(out, "/");
+            fprintf(stderr, "Invalid decomb option %s\n", decomb);
+            return -1;
         }
     }
-    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 (");
-    i       = 0;
-    encoder = NULL;
-    while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL)
+
+    if (hqdn3d != NULL)
     {
-        if (hb_audio_dither_is_supported(encoder->codec))
+        if (hqdn3d_disable)
         {
-            if (i)
-            {
-                fprintf(out, "/");
-            }
-            i = 1;
-            fprintf(out, "%s", encoder->short_name);
+            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;
         }
     }
-    fprintf(out, ").\n");
-    fprintf(out,
-    "                            Options:\n");
-    dither = NULL;
-    while ((dither = hb_audio_dither_get_next(dither)) != NULL)
+
+    if (nlmeans != NULL)
     {
-        if (dither->method == hb_audio_dither_get_default())
+        if (nlmeans_disable)
         {
-            fprintf(out, "                               %s (default)\n",
-                    dither->short_name);
+            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(out, "                               %s\n",
-                    dither->short_name);
+            fprintf(stderr, "Invalid hqdn3d option %s\n", nlmeans);
+            return -1;
         }
     }
-    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");
-#ifdef USE_QSV
-if (hb_qsv_available())
-{
-    fprintf(out, "/qsv");
-}
-#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"
-    );
-
-#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"
-    );
-}
-#endif
-}
 
-/****************************************************************************
- * ShowPresets:
- ****************************************************************************/
-static void ShowPresets()
-{
-    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");
+    return 0;
 }
 
-static char* strchr_quote(char *pos, char c, char q)
+static int foreign_audio_scan(char **subtracks)
 {
-    if (pos == NULL)
-        return NULL;
-
-    while (*pos != 0 && *pos != c)
+    if (subtracks != NULL)
     {
-        if (*pos == q)
+        int count = str_vlen(subtracks);
+        int ii;
+        for (ii = 0; ii < count; ii++)
         {
-            pos = strchr_quote(pos+1, q, 0);
-            if (pos == NULL)
-                return NULL;
-            pos++;
+            if (!strcasecmp(subtracks[0], "scan"))
+            {
+                return 1;
+            }
         }
-        else if (*pos == '\\' && *(pos+1) != 0)
-            pos += 2;
-        else
-            pos++;
     }
-    if (*pos != c)
-        return NULL;
-    return pos;
+    return 0;
 }
 
-static char *strndup_quote(char *str, char q, int len)
+static int count_subtitles(char **subtracks)
 {
-    if (str == NULL)
-        return NULL;
-
-    char * res;
-    int str_len = strlen( str );
-    int src = 0, dst = 0;
-    res = malloc( len > str_len ? str_len + 1 : len + 1 );
-
-    while (str[src] != 0 && src < len)
+    int subtitle_track_count = 0;
+    if (subtracks != NULL)
     {
-        if (str[src] == q)
-            src++;
-        else if (str[src] == '\\' && str[src+1] != 0)
+        int count = str_vlen(subtracks);
+        int ii;
+        for (ii = 0; ii < count; ii++)
         {
-            res[dst++] = str[src+1];
-            src += 2;
+            if (strcasecmp(subtracks[0], "scan") &&
+                strcasecmp(subtracks[0], "none"))
+            {
+                subtitle_track_count++;
+            }
         }
-        else
-            res[dst++] = str[src++];
     }
-    res[dst] = '\0';
-    return res;
+    return subtitle_track_count;
 }
 
-static char** str_split( char *str, char delem )
+static int CheckOptions( int argc, char ** argv )
 {
-    char *  pos;
-    char *  end;
-    char ** ret;
-    int     count, i;
-    char quote = '"';
-
-    if (delem == '"')
+    if( update )
     {
-        quote = '\'';
+        return 0;
     }
-    if ( str == NULL || str[0] == 0 )
+
+    if (preset_export_name == NULL && (input == NULL || *input == '\0'))
     {
-        ret = malloc( sizeof(char*) );
-        *ret = NULL;
-        return ret;
+        fprintf( stderr, "Missing input device. Run %s --help for "
+                 "syntax.\n", argv[0] );
+        return 1;
     }
 
-    // Find number of elements in the string
-    count = 1;
-    pos = str;
-    while ( ( pos = strchr_quote( pos, delem, quote ) ) != NULL )
+    /* Parse format */
+    if (titleindex > 0 && !titlescan)
     {
-        count++;
-        pos++;
-    }
+        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;
+        }
 
-    ret = calloc( ( count + 1 ), sizeof(char*) );
+        if (format == NULL && output != NULL)
+        {
+            /* autodetect */
+            const char *extension = strrchr(output, '.');
+            if (extension != NULL)
+            {
+                // skip '.'
+                mux = hb_container_get_from_extension(extension + 1);
+            }
+            hb_container_t * c = hb_container_get_from_format(mux);
+            if (c != NULL)
+                format = strdup(c->short_name);
+        }
+    }
 
-    pos = str;
-    for ( i = 0; i < count - 1; i++ )
+    int subtitle_track_count = count_subtitles(subtracks);
+    if (subtitle_track_count > 0 && subtitle_lang_list != NULL)
     {
-        end = strchr_quote( pos, delem, quote );
-        ret[i] = strndup_quote(pos, quote, end - pos);
-        pos = end + 1;
+        fprintf(stderr,
+                "Incompatible options: --subtitle-lang-list and --subtitle\n");
+        return 1;
     }
-    ret[i] = strndup_quote(pos, quote, strlen(pos));
-
-    return ret;
-}
-
-static void str_vfree( char **strv )
-{
-    int i;
 
-    if (strv == NULL)
-        return;
-
-    for ( i = 0; strv[i]; i++ )
+    if (subtitle_track_count > 0 && subtitle_all != -1)
     {
-        free( strv[i] );
+        fprintf(stderr,
+                "Incompatible options: --all-subtitles/--first-subtitle and --subtitle\n");
+        return 1;
     }
-    free( strv );
-}
-
-static double parse_hhmmss_strtok()
-{
-    /* Assumes strtok has already been called on a string.  Intends to parse
-     * hh:mm:ss.ss or mm:ss.ss or ss.ss or ss into double seconds.  Actually
-     * parses a list of doubles separated by colons, multiplying the current
-     * result by 60 then adding in the next value.  Malformed input does not
-     * result in a explicit error condition but instead returns an
-     * intermediate result. */
-    double duration = 0;
-    char* str;
-    while ((str = strtok(NULL, ":")) != NULL)
-        duration = 60*duration + strtod(str, NULL);
-    return duration;
-}
 
-/****************************************************************************
- * ParseOptions:
- ****************************************************************************/
-static int ParseOptions( int argc, char ** argv )
-{
-    
-    #define PREVIEWS             257
-    #define START_AT_PREVIEW     258
-    #define START_AT             259
-    #define STOP_AT              260
-    #define ANGLE                261
-    #define DVDNAV               262
-    #define DISPLAY_WIDTH        263
-    #define PIXEL_ASPECT         264
-    #define MODULUS              265
-    #define KEEP_DISPLAY_ASPECT  266
-    #define SUB_BURNED           267
-    #define SUB_DEFAULT          268
-    #define NATIVE_DUB           269
-    #define SRT_FILE             270
-    #define SRT_CODESET          271
-    #define SRT_OFFSET           272
-    #define SRT_LANG             273
-    #define SRT_DEFAULT          274
-    #define SRT_BURN             275
-    #define ROTATE_FILTER        276
-    #define SCAN_ONLY            277
-    #define MAIN_FEATURE         278
-    #define MIN_DURATION         279
-    #define AUDIO_GAIN           280
-    #define ALLOWED_AUDIO_COPY   281
-    #define AUDIO_FALLBACK       282
-    #define LOOSE_CROP           283
-    #define ENCODER_PRESET       284
-    #define ENCODER_PRESET_LIST  285
-    #define ENCODER_TUNE         286
-    #define ENCODER_TUNE_LIST    287
-    #define ENCODER_PROFILE      288
-    #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
-    #define QSV_ASYNC_DEPTH      296
-    #define QSV_IMPLEMENTATION   297
-    #define FILTER_NLMEANS       298
-    #define FILTER_NLMEANS_TUNE  299
+    if (atracks != NULL && audio_lang_list != NULL)
+    {
+        fprintf(stderr,
+                "Incompatible options: --audio-lang-list and --audio\n");
+        return 1;
+    }
 
-    for( ;; )
+    if (atracks != NULL && audio_all != -1)
     {
-        static struct option long_options[] =
-          {
-            { "help",        no_argument,       NULL,    'h' },
-            { "update",      no_argument,       NULL,    'u' },
-            { "verbose",     optional_argument, NULL,    'v' },
-            { "no-dvdnav",   no_argument,       NULL,    DVDNAV },
-            { "no-opencl",   no_argument,       NULL,    NO_OPENCL },
+        fprintf(stderr,
+                "Incompatible options: --all-audio/--first-audio and --audio\n");
+        return 1;
+    }
 
-#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,                  },
-#endif
+    if ((par_width > 0 && par_height > 0) && display_width > 0)
+    {
+        fprintf(stderr,
+                "Incompatible options: --display-width and --pixel-aspect\n");
+        return 1;
+    }
 
-            { "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' },
+    if (preset_export_file != NULL && preset_export_name == NULL)
+    {
+        fprintf(stderr,
+                "Error: --preset-export-file requires option --preset-export\n");
+        return 1;
+    }
 
-            { "title",       required_argument, NULL,    't' },
-            { "min-duration",required_argument, NULL,    MIN_DURATION },
-            { "scan",        no_argument,       NULL,    SCAN_ONLY },
-            { "main-feature",no_argument,       NULL,    MAIN_FEATURE },
-            { "chapters",    required_argument, NULL,    'c' },
-            { "angle",       required_argument, NULL,    ANGLE },
-            { "markers",     optional_argument, NULL,    'm' },
-            { "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",    required_argument, NULL,    's' },
-            { "subtitle-forced", optional_argument,   NULL,    'F' },
-            { "subtitle-burned", optional_argument,   NULL,    SUB_BURNED },
-            { "subtitle-default", optional_argument,   NULL,    SUB_DEFAULT },
-            { "srt-file",    required_argument, NULL, SRT_FILE },
-            { "srt-codeset", required_argument, NULL, SRT_CODESET },
-            { "srt-offset",  required_argument, NULL, SRT_OFFSET },
-            { "srt-lang",    required_argument, NULL, SRT_LANG },
-            { "srt-default", optional_argument, NULL, SRT_DEFAULT },
-            { "srt-burn",    optional_argument, NULL, SRT_BURN },
-            { "native-language", required_argument, NULL,'N' },
-            { "native-dub",  no_argument,       NULL,    NATIVE_DUB },
-            { "encoder",     required_argument, NULL,    'e' },
-            { "aencoder",    required_argument, NULL,    'E' },
-            { "two-pass",    no_argument,       NULL,    '2' },
-            { "deinterlace", optional_argument, NULL,    'd' },
-            { "deblock",     optional_argument, NULL,    '7' },
-            { "denoise",     optional_argument, NULL,    '8' },
-            { "nlmeans",     optional_argument, NULL,    FILTER_NLMEANS },
-            { "nlmeans-tune",required_argument, NULL,    FILTER_NLMEANS_TUNE },
-            { "detelecine",  optional_argument, NULL,    '9' },
-            { "decomb",      optional_argument, NULL,    '5' },
-            { "grayscale",   no_argument,       NULL,    'g' },
-            { "rotate",      optional_argument, NULL,   ROTATE_FILTER },
-            { "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 },
-            { "pixel-aspect", required_argument, NULL, PIXEL_ASPECT },
-            { "modulus",     required_argument, NULL, MODULUS },
-            { "itu-par",     no_argument,       &itu_par, 1  },
-            { "width",       required_argument, NULL,    'w' },
-            { "height",      required_argument, NULL,    'l' },
-            { "crop",        required_argument, NULL,    'n' },
-            { "loose-crop",  optional_argument, NULL, LOOSE_CROP },
+    if (preset_export_desc != NULL && preset_export_name == NULL)
+    {
+        fprintf(stderr,
+                "Error: --preset-export-desc requires option --preset-export\n");
+        return 1;
+    }
 
-            // mapping of legacy option names for backwards compatibility
-            { "qsv-preset",           required_argument, NULL, ENCODER_PRESET,       },
-            { "x264-preset",          required_argument, NULL, ENCODER_PRESET,       },
-            { "x265-preset",          required_argument, NULL, ENCODER_PRESET,       },
-            { "x264-tune",            required_argument, NULL, ENCODER_TUNE,         },
-            { "x265-tune",            required_argument, NULL, ENCODER_TUNE,         },
-            { "x264-profile",         required_argument, NULL, ENCODER_PROFILE,      },
-            { "h264-profile",         required_argument, NULL, ENCODER_PROFILE,      },
-            { "h265-profile",         required_argument, NULL, ENCODER_PROFILE,      },
-            { "h264-level",           required_argument, NULL, ENCODER_LEVEL,        },
-            { "h265-level",           required_argument, NULL, ENCODER_LEVEL,        },
-            // encoder preset/tune/options/profile/level
-            { "encoder-preset",       required_argument, NULL, ENCODER_PRESET,       },
-            { "encoder-preset-list",  required_argument, NULL, ENCODER_PRESET_LIST,  },
-            { "encoder-tune",         required_argument, NULL, ENCODER_TUNE,         },
-            { "encoder-tune-list",    required_argument, NULL, ENCODER_TUNE_LIST,    },
-            { "encopts",              required_argument, NULL, 'x',                  },
-            { "encoder-profile",      required_argument, NULL, ENCODER_PROFILE,      },
-            { "encoder-profile-list", required_argument, NULL, ENCODER_PROFILE_LIST, },
-            { "encoder-level",        required_argument, NULL, ENCODER_LEVEL,        },
-            { "encoder-level-list",   required_argument, NULL, ENCODER_LEVEL_LIST,   },
+    return 0;
+}
 
-            { "vb",          required_argument, NULL,    'b' },
-            { "quality",     required_argument, NULL,    'q' },
-            { "ab",          required_argument, NULL,    'B' },
-            { "aq",          required_argument, NULL,    'Q' },
-            { "ac",          required_argument, NULL,    'C' },
-            { "rate",        required_argument, NULL,    'r' },
-            { "arate",       required_argument, NULL,    'R' },
-            { "turbo",       no_argument,       NULL,    'T' },
-            { "maxHeight",   required_argument, NULL,    'Y' },
-            { "maxWidth",    required_argument, NULL,    'X' },
-            { "preset",      required_argument, NULL,    'Z' },
-            { "preset-list", no_argument,       NULL,    'z' },
+static hb_dict_t * PreparePreset(const char *preset_name)
+{
+    int ii;
+    hb_dict_t *preset;
 
-            { "aname",       required_argument, NULL,    'A' },
-            { "color-matrix",required_argument, NULL,    'M' },
-            { "previews",    required_argument, NULL,    PREVIEWS },
-            { "start-at-preview", required_argument, NULL, START_AT_PREVIEW },
-            { "start-at",    required_argument, NULL,    START_AT },
-            { "stop-at",    required_argument, NULL,     STOP_AT },
-            { "vfr",         no_argument,       &cfr,    0 },
-            { "cfr",         no_argument,       &cfr,    1 },
-            { "pfr",         no_argument,       &cfr,    2 },
-            { "audio-copy-mask", required_argument, NULL, ALLOWED_AUDIO_COPY },
-            { "audio-fallback",  required_argument, NULL, AUDIO_FALLBACK },
-            { 0, 0, 0, 0 }
-          };
+    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 option_index = 0;
-        int c;
-        int cur_optind;
+    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)
+            {
+                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;
+            }
+        }
+        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)
+        {
+            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));
+    }
 
-        cur_optind = optind;
-        c = getopt_long( argc, argv,
-                         "hv::uC:f:4i:Io:PUt:c:m::M:a:A:6:s:F::N:e:E:Q:C:"
-                         "2dD:7895gOw:l:n:b:q:S:B:r:R:x:TY:X:Z:z",
-                         long_options, &option_index );
-        if( c < 0 )
+    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++)
         {
-            break;
+            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));
+    }
 
-        switch( c )
+    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++)
         {
-            case 0:
-                /* option was handled entirely in getopt_long */
-                break;
-            case 'h':
-                ShowHelp();
-                exit( 0 );
-            case 'u':
-                update = 1;
-                break;
-            case 'v':
-                if( optarg != NULL )
-                {
-                    debug = atoi( optarg );
-                }
-                else
-                {
-                    debug = 1;
-                }
-                break;
-            case 'Z':
-                preset = 1;
-                preset_name = strdup(optarg);
-                break;
-            case 'z':
-                ShowPresets();
-                exit ( 0 );
-            case DVDNAV:
-                dvdnav = 0;
-                break;
+            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, "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"));
+    }
 
-            case 'f':
-                format = strdup( optarg );
-                break;
-            case 'i':
-                input = strdup( optarg );
-#ifdef __APPLE_CC__
-                char *devName = bsd_name_for_path( input ); // alloc
-                if( devName )
-                {
-                    if( device_is_dvd( devName ))
-                    {
-                        free( input );
-                        input = malloc( strlen( "/dev/" ) + strlen( devName ) + 1 );
-                        sprintf( input, "/dev/%s", devName );
-                    }
-                    free( devName );
-                }
-#endif
-                break;
-            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;
+    // 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);
+            }
+        }
 
-            case 't':
-                titleindex = atoi( optarg );
-                break;
-            case SCAN_ONLY:
-                titlescan = 1;
-                break;
-            case MAIN_FEATURE:
-                main_feature = 1;
-                break;
-            case 'c':
+        // Update codecs
+        if (str_vlen(acodecs) > 0)
+        {
+            for (ii = 0; acodecs[ii] != NULL; ii++)
             {
-                int start, end;
-                if( sscanf( optarg, "%d-%d", &start, &end ) == 2 )
-                {
-                    chapter_start = start;
-                    chapter_end   = end;
-                }
-                else if( sscanf( optarg, "%d", &start ) == 1 )
-                {
-                    chapter_start = start;
-                    chapter_end   = chapter_start;
-                }
-                else
-                {
-                    fprintf( stderr, "chapters: invalid syntax (%s)\n",
-                             optarg );
-                    return -1;
-                }
-                break;
+                audio_dict = hb_value_array_get(list, ii);
+                hb_dict_set(audio_dict, "AudioEncoder",
+                                    hb_value_string(acodecs[ii]));
             }
-            case NO_OPENCL:
-                use_opencl = 0;
-                break;
-            case ANGLE:
-                angle = atoi( optarg );
-                break;
-            case 'm':
-                if( optarg != NULL )
-                {
-                    marker_file = strdup( optarg );
-                }
-                chapter_markers = 1;
-                break;
-            case 'a':
-                if( optarg != NULL )
-                {
-                    atracks = strdup( optarg );
-                    audio_explicit = 1;
-                }
-                else
-                {
-                    atracks = "1" ;
-                }
-                break;
-            case '6':
-                if( optarg != NULL )
-                {
-                    mixdowns = strdup( optarg );
-                }
-                break;
-            case 'D':
-                if( optarg != NULL )
-                {
-                    dynamic_range_compression = strdup( optarg );
-                }
-                break;
-            case AUDIO_GAIN:
-                if( optarg != NULL )
-                {
-                    audio_gain = strdup( optarg );
-                }
-                break;
-            case AUDIO_DITHER:
-                if (optarg != NULL)
-                {
-                    audio_dither = str_split(optarg, ',');
-                }
-                break;
-            case NORMALIZE_MIX:
-                if( optarg != NULL )
-                {
-                    normalize_mix_level = str_split( optarg, ',' );
-                }
-                break;
-            case 's':
-                subtracks = str_split( optarg, ',' );
-                break;
-            case 'F':
-                subforce = str_split( optarg, ',' );
-                break;
-            case SUB_BURNED:
-                if( optarg != NULL )
-                {
-                    subburn = strdup( optarg );
-                }
-                else
-                {
-                    subburn = "" ;
-                }
-                break;
-            case SUB_DEFAULT:
-                if( optarg != NULL )
-                {
-                    subdefault = strdup( optarg );
-                }
-                else
-                {
-                    subdefault = "" ;
-                }
-                break;
-            case 'N':
-                native_language = strdup( optarg );
-                break;
-            case NATIVE_DUB:
-                native_dub = 1;
-                break;
-            case SRT_FILE:
-                srtfile = str_split( optarg, ',' );
-                break;
-            case SRT_CODESET:
-                srtcodeset = str_split( optarg, ',' );
-                break;
-            case SRT_OFFSET:
-                srtoffset = str_split( optarg, ',' );
-                break;
-            case SRT_LANG:
-                srtlang = str_split( optarg, ',' );
-                break;
-            case SRT_DEFAULT:
-                if( optarg != NULL )
-                {
-                    srtdefault = atoi( optarg );
-                }
-                else
-                {
-                    srtdefault = 1 ;
-                }
-                break;
-            case SRT_BURN:
-                if( optarg != NULL )
-                {
-                    srtburn = atoi( optarg );
-                }
-                else
-                {
-                    srtburn = 1 ;
-                }
-                break;
-            case '2':
-                twoPass = 1;
-                break;
-            case 'd':
-                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 = 1;
-                break;
-            case '7':
-                if( optarg != NULL )
-                {
-                    deblock_opt = strdup( optarg );
-                }
-                deblock = 1;
-                break;
-            case '8':
-                if( optarg != NULL )
-                {
-                    free(denoise_opt);
-                    denoise_opt = strdup( optarg );
-                }
-                denoise = 1;
-                break;
-            case FILTER_NLMEANS:
-                if (optarg != NULL)
-                {
-                    free(nlmeans_opt);
-                    nlmeans_opt = strdup(optarg);
-                }
-                nlmeans = 1;
-                break;
-            case FILTER_NLMEANS_TUNE:
-                if (optarg != NULL)
-                {
-                    free(nlmeans_tune_opt);
-                    nlmeans_tune_opt = strdup(optarg);
-                }
-                break;
-            case '9':
-                if( optarg != NULL )
+            // 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")))
                 {
-                    detelecine_opt = strdup( optarg );
+                    continue;
                 }
-                detelecine = 1;
-                break;
-            case '5':
-                if( optarg != NULL )
+                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++)
                 {
-                    if (!( strcmp( optarg, "fast" ) ))
-                    {
-                        decomb_opt = "7:2:6:9:1:80";
-                    }
-                    else if (!( strcmp( optarg, "bob" ) ))
-                    {
-                        decomb_opt = "455";
-                    }
-                    else
+                    audio_dict = hb_value_array_get(list, ii);
+                    if (hb_value_get_bool(hb_dict_get(audio_dict,
+                                        "AudioTrackQualityEnable")))
                     {
-                        decomb_opt = strdup( optarg );
+                        continue;
                     }
+                    hb_dict_set(audio_dict, "AudioBitrate",
+                        hb_value_int(atoi(abitrates[ii-1])));
                 }
-                decomb = 1;
-                break;
-            case 'g':
-                grayscale = 1;
-                break;
-            case ROTATE_FILTER:
-                if( optarg != NULL )
-                {
-                    rotate_opt = strdup( optarg );
-                    rotate_val = atoi( optarg );
-                }
-                rotate = 1;
-                break;
-            case DISPLAY_WIDTH:
-                if( optarg != NULL )
-                {
-                    sscanf( optarg, "%i", &display_width );
-                }
-                break;
-            case PIXEL_ASPECT:
-                if( optarg != NULL )
-                {
-                    sscanf( optarg, "%i:%i", &par_width, &par_height );
-                }
-                break;
-            case MODULUS:
-                if( optarg != NULL )
+        }
+
+        // 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++)
                 {
-                    sscanf( optarg, "%i", &modulus );
+                    audio_dict = hb_value_array_get(list, ii);
+                    hb_dict_set(audio_dict, "AudioSamplerate",
+                                hb_value_string(arates[ii-1]));
                 }
-                break;
-            case 'e':
+        }
+
+        // Update mixdowns
+        if (str_vlen(mixdowns) > 0)
+        {
+            for (ii = 0; mixdowns[ii]    != NULL &&
+                         mixdowns[ii][0] != 0; ii++)
             {
-                vcodec = hb_video_encoder_get_from_name(optarg);
-                if (vcodec <= 0)
+                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++)
                 {
-                    fprintf(stderr, "invalid codec (%s)\n", optarg);
-                    return -1;
+                    audio_dict = hb_value_array_get(list, ii);
+                    hb_dict_set(audio_dict, "AudioMixdown",
+                                hb_value_string(mixdowns[ii-1]));
                 }
-                break;
+        }
+
+        // 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])));
             }
-            case 'E':
-                if( optarg != NULL )
+            // Apply last mix norm in list to all other entries
+            if (normalize_mix_level[ii-1][0] != 0)
+                for (; ii < count; ii++)
                 {
-                    acodecs = strdup( optarg );
+                    audio_dict = hb_value_array_get(list, ii);
+                    hb_dict_set(audio_dict,
+                                "AudioNormalizeMixLevel",
+                        hb_value_bool(
+                            atoi(normalize_mix_level[ii-1])));
                 }
-                break;
-            case 'w':
-                width = atoi( optarg );
-                break;
-            case 'l':
-                height = atoi( optarg );
-                break;
-            case 'n':
+        }
+
+        // Update DRC
+        if (str_vlen(dynamic_range_compression) > 0)
+        {
+            for (ii = 0;dynamic_range_compression[ii]    != NULL &&
+                        dynamic_range_compression[ii][0] != 0; ii++)
             {
-                int    i;
-                char * tmp = optarg;
-                for( i = 0; i < 4; i++ )
+                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++)
                 {
-                    if( !*tmp )
-                        break;
-                    crop[i] = strtol( tmp, &tmp, 0 );
-                    tmp++;
+                    audio_dict = hb_value_array_get(list, ii);
+                    hb_dict_set(audio_dict, "AudioTrackDRCSlider",
+                        hb_value_double(
+                            strtod(dynamic_range_compression[ii-1],
+                                   NULL)));
                 }
-                break;
-            }
-            case LOOSE_CROP:
-                loose_crop = optarg ? atoi(optarg) : 15;
-                break;
-            case 'r':
+        }
+
+        // Update Gain
+        if (str_vlen(audio_gain) > 0)
+        {
+            for (ii = 0; audio_gain[ii]    != NULL &&
+                         audio_gain[ii][0] != 0; ii++)
             {
-                vrate = hb_video_framerate_get_from_name(optarg);
-                if (vrate <= 0)
+                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++)
                 {
-                    vrate = 0;
-                    fprintf(stderr, "invalid framerate %s\n", optarg);
+                    audio_dict = hb_value_array_get(list, ii);
+                    hb_dict_set(audio_dict, "AudioTrackGainSlider",
+                      hb_value_double(
+                        strtod(audio_gain[ii-1], NULL)));
                 }
-                else if (!cfr)
+        }
+
+        // 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++)
                 {
-                    cfr = 1;
+                    audio_dict = hb_value_array_get(list, ii);
+                    hb_dict_set(audio_dict, "AudioDitherMethod",
+                            hb_value_string(audio_dither[ii-1]));
                 }
-                break;
+        }
+
+        // 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)));
             }
-            case 'R':
-                if( optarg != NULL )
+            // Apply last compression in list to all other entries
+            if (acompressions[ii-1][0] != 0)
+                for (; ii < count; ii++)
                 {
-                    arates = strdup( optarg );
+                    audio_dict = hb_value_array_get(list, ii);
+                    hb_dict_set(audio_dict, "AudioCompressionLevel",
+                      hb_value_double(
+                        strtod(acompressions[ii-1], NULL)));
                 }
-                break;
-            case 'b':
-                vbitrate = atoi( optarg );
-                break;
-            case 'q':
-                vquality = atof( optarg );
-                break;
-            case 'B':
-                abitrates = str_split( optarg, ',' );
-                break;
-            case 'Q':
-                aqualities = str_split( optarg, ',' );
-                break;
-            case 'C':
-                acompressions = str_split( optarg, ',' );
-                break;
-            case ENCODER_PRESET:
-                x264_preset = strdup( optarg );
-                break;
-            case ENCODER_TUNE:
-                x264_tune = strdup( optarg );
-                break;
-            case 'x':
-                advanced_opts = strdup( optarg );
-                break;
-            case ENCODER_PROFILE:
-                h264_profile = strdup( optarg );
-                break;
-            case ENCODER_LEVEL:
-                h264_level = strdup( optarg );
-                break;
-            case ENCODER_PRESET_LIST:
-                fprintf(stderr, "Available --encoder-preset values for '%s' encoder:\n",
-                        hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg)));
-                print_string_list(stderr,
-                                  hb_video_encoder_get_presets(hb_video_encoder_get_from_name(optarg)),
-                                  "    ");
-                exit(0);
-            case ENCODER_TUNE_LIST:
-                fprintf(stderr, "Available --encoder-tune values for '%s' encoder:\n",
-                        hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg)));
-                print_string_list(stderr,
-                                  hb_video_encoder_get_tunes(hb_video_encoder_get_from_name(optarg)),
-                                  "    ");
-                exit(0);
-            case ENCODER_PROFILE_LIST:
-                fprintf(stderr, "Available --encoder-profile values for '%s' encoder:\n",
-                        hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg)));
-                print_string_list(stderr,
-                                  hb_video_encoder_get_profiles(hb_video_encoder_get_from_name(optarg)),
-                                  "    ");
-                exit(0);
-            case ENCODER_LEVEL_LIST:
-                fprintf(stderr, "Available --encoder-level values for '%s' encoder:\n",
-                        hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg)));
-                print_string_list(stderr,
-                                  hb_video_encoder_get_levels(hb_video_encoder_get_from_name(optarg)),
-                                  "    ");
-                exit(0);
-            case 'T':
-                fastfirstpass = 1;
-                break;
-            case 'Y':
-                maxHeight = atoi( optarg );
-                break;
-            case 'X':
-                maxWidth = atoi (optarg );
-                break;
-            case 'A':
-                if( optarg != 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)
                 {
-                    anames = str_split( optarg, ',' );
+                    for (track = first - 1; track < last; track++)
+                    {
+                        add_audio(audio_array, title, track);
+                    }
                 }
-                break;
-            case PREVIEWS:
-                sscanf( optarg, "%i:%i", &preview_count, &store_previews );
-                break;
-            case START_AT_PREVIEW:
-                start_at_preview = atoi( optarg );
-                break;
-            case START_AT:
-            {
-                char * start_at_string = NULL;
-                char * start_at_token = NULL;
-                start_at_string = strdup( optarg );
-                start_at_token = strtok( start_at_string, ":");
-                if( !strcmp( start_at_token, "frame" ) )
+                else if (sscanf(atracks[ii], "%d", &track) == 1)
                 {
-                    start_at_token = strtok( NULL, ":");
-                    start_at_frame = atoi(start_at_token);
+                    add_audio(audio_array, title, track);
                 }
-                else if( !strcmp( start_at_token, "pts" ) )
+                else
                 {
-                    start_at_token = strtok( NULL, ":");
-                    sscanf( start_at_token, "%"SCNd64, &start_at_pts );
+                    fprintf(stderr, "ERROR: unable to parse audio input \"%s\", skipping\n", atracks[ii]);
                 }
-                else if( !strcmp( start_at_token, "duration" ) )
+            }
+        }
+        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)
                 {
-                    double duration_seconds = parse_hhmmss_strtok();
-                    start_at_pts = (int64_t)(duration_seconds * 90e3);
+                    fprintf(stderr,
+                        "Invalid codec %s, using default for container.\n",
+                        acodecs[ii]);
+                    acodec = hb_audio_encoder_get_default(mux);
                 }
-                free( start_at_string );
-                break;
+                hb_dict_set(audio_dict, "Encoder", hb_value_int(acodec));
             }
-            case STOP_AT:
+            if (acodecs[ii] != NULL)
             {
-                char * stop_at_string = NULL;
-                char * stop_at_token = NULL;
-                stop_at_string = strdup( optarg );
-                stop_at_token = strtok( stop_at_string, ":");
-                if( !strcmp( stop_at_token, "frame" ) )
+                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"))
                 {
-                    stop_at_token = strtok( NULL, ":");
-                    stop_at_frame = atoi(stop_at_token);
+                    arate = 0;
                 }
-                else if( !strcmp( stop_at_token, "pts" ) )
+                else
                 {
-                    stop_at_token = strtok( NULL, ":");
-                    sscanf( stop_at_token, "%"SCNd64, &stop_at_pts );
+                    arate = hb_audio_samplerate_get_from_name(arates[ii]);
                 }
-                else if( !strcmp( stop_at_token, "duration" ) )
+                if (arate <= 0)
                 {
-                    double duration_seconds = parse_hhmmss_strtok();
-                    stop_at_pts = (int64_t)(duration_seconds * 90e3);
+                    fprintf(stderr, "Invalid sample rate %s, using input rate\n",
+                            arates[ii]);
+                    arate = 0;
                 }
-                free( stop_at_string );
-                break;
+                audio_dict = hb_value_array_get(audio_array, ii);
+                hb_dict_set(audio_dict, "Samplerate", hb_value_int(arate));
             }
-            case ALLOWED_AUDIO_COPY:
+            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)
             {
-                allowed_audio_copy                = 0;
-                const hb_encoder_t *audio_encoder = NULL;
-                char **allowed                    = str_split(optarg, ',');
+                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));
+        }
 
-                while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL)
+        /* Audio Bitrate */
+        int abitrate;
+        ii = 0;
+        if (abitrates != NULL)
+        {
+            for (; abitrates[ii] != NULL && ii < track_count; ii++)
+            {
+                if (*abitrates[ii])
                 {
-                    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;
-                            }
-                        }
-                    }
+                    abitrate = atoi(abitrates[ii]);
+                    audio_dict = hb_value_array_get(audio_array, ii);
+                    hb_dict_set(audio_dict, "Bitrate", hb_value_int(abitrate));
                 }
-
-                allowed_audio_copy &= HB_ACODEC_PASS_MASK;
-                str_vfree(allowed);
-                break;
             }
-            case AUDIO_FALLBACK:
-                acodec_fallback = strdup( optarg );
-                break;
-            case 'M':
-                if( optarg != NULL )
-                {
-                    if( !strcmp( optarg, "601" ) ||
-                        !strcmp( optarg, "ntsc" ) )
-                        color_matrix_code = 1;
-                    else if( !strcmp( optarg, "pal" ) )
-                        color_matrix_code = 2;
-                    else if( !strcmp( optarg, "709" ) )
-                        color_matrix_code = 3;
-                } break;
-            case MIN_DURATION:
-                min_title_duration = strtol( optarg, NULL, 0 );
-                break;
-#ifdef USE_QSV
-            case QSV_BASELINE:
-                hb_qsv_force_workarounds();
-                break;
-            case QSV_ASYNC_DEPTH:
-                qsv_async_depth = atoi(optarg);
-                break;
-            case QSV_IMPLEMENTATION:
-                hb_qsv_impl_set_preferred(optarg);
-                break;
-#endif
-            default:
-                fprintf( stderr, "unknown option (%s)\n", argv[cur_optind] );
-                return -1;
+            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));
         }
 
-    }
-
-    if (nlmeans)
-    {
-        char *opt = hb_generate_filter_settings(HB_FILTER_NLMEANS,
-                                                nlmeans_opt, nlmeans_tune_opt);
-        if (opt != NULL)
+        /* Audio Quality */
+        double aquality;
+        ii = 0;
+        if (aqualities != NULL)
         {
-            free(nlmeans_opt);
-            nlmeans_opt = opt;
+            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");
+            }
         }
-        else if (nlmeans_opt != NULL)
+        // 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++)
         {
-            fprintf(stderr, "Invalid parameters for nlmeans (%s).", nlmeans_opt);
-            return -1;
+            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));
+            }
         }
-        else if (nlmeans_tune_opt != NULL)
+
+        /* Audio Compression Level */
+        double acompression;
+        ii = 0;
+        if (acompressions != NULL)
         {
-            fprintf(stdout, "Default nlmeans parameters specified; ignoring nlmeans tune (%s).\n", nlmeans_tune_opt);
+            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");
+            }
         }
-    }
-    if (denoise)
-    {
-        char *opt = hb_generate_filter_settings(HB_FILTER_DENOISE,
-                                                denoise_opt, NULL);
-        if (opt != NULL)
+        // 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)
         {
-            free(denoise_opt);
-            denoise_opt = opt;
+            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");
+            }
         }
-        else if (denoise_opt != NULL)
+        // If exactly one DRC was specified, apply it to the reset
+        // of the tracks
+        if (ii == 1) for (; ii < track_count; ii++)
         {
-            fprintf(stderr, "Invalid parameters for hqdn3d (%s).", denoise_opt);
-            return -1;
+            audio_dict = hb_value_array_get(audio_array, ii);
+            hb_dict_set(audio_dict, "DRC", hb_value_double(drc));
         }
-    }
-
-    return 0;
-}
 
-static int CheckOptions( int argc, char ** argv )
-{
-    if( update )
-    {
-        return 0;
-    }
-
-    if( input == NULL || *input == '\0' )
-    {
-        fprintf( stderr, "Missing input device. Run %s --help for "
-                 "syntax.\n", argv[0] );
-        return 1;
-    }
+        /* 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));
+        }
 
-    /* Parse format */
-    if( titleindex > 0 && !titlescan )
-    {
-        if( output == NULL || *output == '\0' )
+        /* Audio Dither */
+        int dither;
+        ii = 0;
+        if (audio_dither != NULL)
         {
-            fprintf( stderr, "Missing output file name. Run %s --help "
-                     "for syntax.\n", argv[0] );
-            return 1;
+            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));
+            }
         }
 
-        if (format == NULL)
+        /* Audio Mix Normalization */
+        int norm = 0;
+        ii = 0;
+        if (normalize_mix_level)
         {
-            /* autodetect */
-            const char *extension = strrchr(output, '.');
-            if (extension != NULL)
+            char **nmls = normalize_mix_level;
+            for (; nmls[ii] != NULL && ii < track_count; ii++)
             {
-                // skip '.'
-                mux = hb_container_get_from_extension(extension + 1);
+                norm = atoi(nmls[ii]);
+                audio_dict = hb_value_array_get(audio_array, ii);
+                hb_dict_set(audio_dict, "NormalizeMixLevel",
+                            hb_value_int(norm));
             }
-            if (mux <= 0)
+            if (nmls[ii] != 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;
+                        "Dropping excess audio mixdown normalizations\n");
             }
         }
-        else
+        // 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)
         {
-            mux = hb_container_get_from_name(format);
-            if (mux <= 0)
+            for (; anames[ii] != NULL && ii < track_count; ii++)
             {
-                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)
+                if (*anames[ii])
                 {
-                    fprintf(stderr, "%s", container->short_name);
-                    if (hb_container_get_next(container) != NULL)
-                    {
-                        fprintf(stderr, ", ");
-                    }
-                    else
-                    {
-                        fprintf(stderr, "\n");
-                    }
+                    audio_dict = hb_value_array_get(audio_array, ii);
+                    hb_dict_set(audio_dict, "Name",
+                                hb_value_string(anames[ii]));
                 }
-                return 1;
+            }
+            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)