summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authorjstebbins <[email protected]>2015-05-06 16:04:08 +0000
committerjstebbins <[email protected]>2015-05-06 16:04:08 +0000
commit9c0e97cc2c369e0c720441d182bbde20210742f4 (patch)
tree8b8f74ed24d0f617c9980d9043ebd53c33c531f3 /libhb
parent81bcee10d2ad88f44c0f7791f2dd8da4ee2c1b76 (diff)
libhb,cli: add preset management to libhb, use it in cli
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
Diffstat (limited to 'libhb')
-rw-r--r--libhb/builtin_presets.h965
-rw-r--r--libhb/common.c21
-rw-r--r--libhb/hb.c5
-rw-r--r--libhb/hb.h3
-rw-r--r--libhb/hb_dict.c5
-rw-r--r--libhb/hb_dict.h1
-rw-r--r--libhb/lang.c8
-rw-r--r--libhb/lang.h2
-rw-r--r--libhb/libhb_presets.list9
-rw-r--r--libhb/module.defs6
-rw-r--r--libhb/plist.c678
-rw-r--r--libhb/plist.h13
-rw-r--r--libhb/preset.c2198
-rw-r--r--libhb/preset.h84
-rw-r--r--libhb/preset_builtin.json855
-rw-r--r--libhb/preset_template.json104
16 files changed, 4945 insertions, 12 deletions
diff --git a/libhb/builtin_presets.h b/libhb/builtin_presets.h
new file mode 100644
index 000000000..b0b3e5cd8
--- /dev/null
+++ b/libhb/builtin_presets.h
@@ -0,0 +1,965 @@
+const char hb_builtin_presets_json[] =
+"{\n"
+" \"PresetBuiltin\": [\n"
+" {\n"
+" \"ChildrenArray\": [\n"
+" {\n"
+" \"AudioAllowAACPass\": 1, \n"
+" \"AudioAllowAC3Pass\": 1, \n"
+" \"AudioAllowDTSHDPass\": 1, \n"
+" \"AudioAllowDTSPass\": 1, \n"
+" \"AudioAllowMP3Pass\": 1, \n"
+" \"AudioEncoderFallback\": \"ac3\", \n"
+" \"AudioList\": [\n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"aac\", \n"
+" \"AudioMixdown\": \"dpl2\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }, \n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"copy:ac3\", \n"
+" \"AudioMixdown\": \"none\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }\n"
+" ], \n"
+" \"ChapterMarkers\": 1, \n"
+" \"Default\": 0, \n"
+" \"FileFormat\": \"mp4\", \n"
+" \"Folder\": false, \n"
+" \"Mp4HttpOptimize\": 0, \n"
+" \"Mp4iPodCompatible\": 0, \n"
+" \"PictureAutoCrop\": 1, \n"
+" \"PictureBottomCrop\": 0, \n"
+" \"PictureDeblock\": 0, \n"
+" \"PictureDecomb\": 0, \n"
+" \"PictureDecombCustom\": \"\", \n"
+" \"PictureDecombDeinterlace\": 1, \n"
+" \"PictureDeinterlace\": 0, \n"
+" \"PictureDeinterlaceCustom\": \"\", \n"
+" \"PictureDenoiseCustom\": \"\", \n"
+" \"PictureDenoiseFilter\": \"off\", \n"
+" \"PictureDetelecine\": 0, \n"
+" \"PictureDetelecineCustom\": \"\", \n"
+" \"PictureHeight\": 576, \n"
+" \"PictureKeepRatio\": 0, \n"
+" \"PictureLeftCrop\": 0, \n"
+" \"PictureModulus\": 2, \n"
+" \"PicturePAR\": \"loose\", \n"
+" \"PictureRightCrop\": 0, \n"
+" \"PictureTopCrop\": 0, \n"
+" \"PictureWidth\": 720, \n"
+" \"PresetDescription\": \"HandBrake's settings for compatibility with all Apple devices (including the iPod 6G and later). Includes Dolby Digital audio for surround sound.\", \n"
+" \"PresetName\": \"Universal\", \n"
+" \"Type\": 0, \n"
+" \"UsesPictureFilters\": 1, \n"
+" \"UsesPictureSettings\": 1, \n"
+" \"VideoAvgBitrate\": \"2500\", \n"
+" \"VideoEncoder\": \"x264\", \n"
+" \"VideoFramerate\": \"30\", \n"
+" \"VideoFramerateMode\": \"pfr\", \n"
+" \"VideoGrayScale\": 0, \n"
+" \"VideoLevel\": \"3.0\", \n"
+" \"VideoOptionExtra\": \"\", \n"
+" \"VideoPreset\": \"fast\", \n"
+" \"VideoProfile\": \"baseline\", \n"
+" \"VideoQualitySlider\": 20.0, \n"
+" \"VideoQualityType\": 2, \n"
+" \"VideoTune\": \"\", \n"
+" \"VideoTurboTwoPass\": 0, \n"
+" \"VideoTwoPass\": 0, \n"
+" \"x264Option\": \"\", \n"
+" \"x264UseAdvancedOptions\": 0\n"
+" }, \n"
+" {\n"
+" \"AudioAllowAACPass\": 1, \n"
+" \"AudioAllowAC3Pass\": 1, \n"
+" \"AudioAllowDTSHDPass\": 1, \n"
+" \"AudioAllowDTSPass\": 1, \n"
+" \"AudioAllowMP3Pass\": 1, \n"
+" \"AudioEncoderFallback\": \"ac3\", \n"
+" \"AudioList\": [\n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"aac\", \n"
+" \"AudioMixdown\": \"dpl2\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }\n"
+" ], \n"
+" \"ChapterMarkers\": 1, \n"
+" \"Default\": 0, \n"
+" \"FileFormat\": \"mp4\", \n"
+" \"Folder\": false, \n"
+" \"Mp4HttpOptimize\": 0, \n"
+" \"Mp4iPodCompatible\": 1, \n"
+" \"PictureAutoCrop\": 1, \n"
+" \"PictureBottomCrop\": 0, \n"
+" \"PictureDeblock\": 0, \n"
+" \"PictureDecomb\": 0, \n"
+" \"PictureDecombCustom\": \"\", \n"
+" \"PictureDecombDeinterlace\": 1, \n"
+" \"PictureDeinterlace\": 0, \n"
+" \"PictureDeinterlaceCustom\": \"\", \n"
+" \"PictureDenoiseCustom\": \"\", \n"
+" \"PictureDenoiseFilter\": \"off\", \n"
+" \"PictureDetelecine\": 0, \n"
+" \"PictureDetelecineCustom\": \"\", \n"
+" \"PictureHeight\": 240, \n"
+" \"PictureKeepRatio\": 1, \n"
+" \"PictureLeftCrop\": 0, \n"
+" \"PictureModulus\": 2, \n"
+" \"PicturePAR\": \"off\", \n"
+" \"PictureRightCrop\": 0, \n"
+" \"PictureTopCrop\": 0, \n"
+" \"PictureWidth\": 320, \n"
+" \"PresetDescription\": \"HandBrake's settings for playback on the iPod with Video (all generations).\", \n"
+" \"PresetName\": \"iPod\", \n"
+" \"Type\": 0, \n"
+" \"UsesPictureFilters\": 1, \n"
+" \"UsesPictureSettings\": 1, \n"
+" \"VideoAvgBitrate\": \"2500\", \n"
+" \"VideoEncoder\": \"x264\", \n"
+" \"VideoFramerate\": \"30\", \n"
+" \"VideoFramerateMode\": \"pfr\", \n"
+" \"VideoGrayScale\": 0, \n"
+" \"VideoLevel\": \"1.3\", \n"
+" \"VideoOptionExtra\": \"\", \n"
+" \"VideoPreset\": \"medium\", \n"
+" \"VideoProfile\": \"baseline\", \n"
+" \"VideoQualitySlider\": 22.0, \n"
+" \"VideoQualityType\": 2, \n"
+" \"VideoTune\": \"\", \n"
+" \"VideoTurboTwoPass\": 0, \n"
+" \"VideoTwoPass\": 0, \n"
+" \"x264Option\": \"\", \n"
+" \"x264UseAdvancedOptions\": 0\n"
+" }, \n"
+" {\n"
+" \"AudioAllowAACPass\": 1, \n"
+" \"AudioAllowAC3Pass\": 1, \n"
+" \"AudioAllowDTSHDPass\": 1, \n"
+" \"AudioAllowDTSPass\": 1, \n"
+" \"AudioAllowMP3Pass\": 1, \n"
+" \"AudioEncoderFallback\": \"ac3\", \n"
+" \"AudioList\": [\n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"aac\", \n"
+" \"AudioMixdown\": \"dpl2\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }\n"
+" ], \n"
+" \"ChapterMarkers\": 1, \n"
+" \"Default\": 0, \n"
+" \"FileFormat\": \"mp4\", \n"
+" \"Folder\": false, \n"
+" \"Mp4HttpOptimize\": 0, \n"
+" \"Mp4iPodCompatible\": 0, \n"
+" \"PictureAutoCrop\": 1, \n"
+" \"PictureBottomCrop\": 0, \n"
+" \"PictureDeblock\": 0, \n"
+" \"PictureDecomb\": 0, \n"
+" \"PictureDecombCustom\": \"\", \n"
+" \"PictureDecombDeinterlace\": 1, \n"
+" \"PictureDeinterlace\": 0, \n"
+" \"PictureDeinterlaceCustom\": \"\", \n"
+" \"PictureDenoiseCustom\": \"\", \n"
+" \"PictureDenoiseFilter\": \"off\", \n"
+" \"PictureDetelecine\": 0, \n"
+" \"PictureDetelecineCustom\": \"\", \n"
+" \"PictureHeight\": 640, \n"
+" \"PictureKeepRatio\": 0, \n"
+" \"PictureLeftCrop\": 0, \n"
+" \"PictureModulus\": 2, \n"
+" \"PicturePAR\": \"loose\", \n"
+" \"PictureRightCrop\": 0, \n"
+" \"PictureTopCrop\": 0, \n"
+" \"PictureWidth\": 960, \n"
+" \"PresetDescription\": \"HandBrake's settings for handheld iOS devices (iPhone 4, iPod touch 3G and later).\", \n"
+" \"PresetName\": \"iPhone & iPod touch\", \n"
+" \"Type\": 0, \n"
+" \"UsesPictureFilters\": 1, \n"
+" \"UsesPictureSettings\": 1, \n"
+" \"VideoAvgBitrate\": \"2500\", \n"
+" \"VideoEncoder\": \"x264\", \n"
+" \"VideoFramerate\": \"30\", \n"
+" \"VideoFramerateMode\": \"pfr\", \n"
+" \"VideoGrayScale\": 0, \n"
+" \"VideoLevel\": \"3.1\", \n"
+" \"VideoOptionExtra\": \"\", \n"
+" \"VideoPreset\": \"medium\", \n"
+" \"VideoProfile\": \"high\", \n"
+" \"VideoQualitySlider\": 22.0, \n"
+" \"VideoQualityType\": 2, \n"
+" \"VideoTune\": \"\", \n"
+" \"VideoTurboTwoPass\": 0, \n"
+" \"VideoTwoPass\": 0, \n"
+" \"x264Option\": \"\", \n"
+" \"x264UseAdvancedOptions\": 0\n"
+" }, \n"
+" {\n"
+" \"AudioAllowAACPass\": 1, \n"
+" \"AudioAllowAC3Pass\": 1, \n"
+" \"AudioAllowDTSHDPass\": 1, \n"
+" \"AudioAllowDTSPass\": 1, \n"
+" \"AudioAllowMP3Pass\": 1, \n"
+" \"AudioEncoderFallback\": \"ac3\", \n"
+" \"AudioList\": [\n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"aac\", \n"
+" \"AudioMixdown\": \"dpl2\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }\n"
+" ], \n"
+" \"ChapterMarkers\": 1, \n"
+" \"Default\": 0, \n"
+" \"FileFormat\": \"mp4\", \n"
+" \"Folder\": false, \n"
+" \"Mp4HttpOptimize\": 0, \n"
+" \"Mp4iPodCompatible\": 0, \n"
+" \"PictureAutoCrop\": 1, \n"
+" \"PictureBottomCrop\": 0, \n"
+" \"PictureDeblock\": 0, \n"
+" \"PictureDecomb\": 0, \n"
+" \"PictureDecombCustom\": \"\", \n"
+" \"PictureDecombDeinterlace\": 1, \n"
+" \"PictureDeinterlace\": 0, \n"
+" \"PictureDeinterlaceCustom\": \"\", \n"
+" \"PictureDenoiseCustom\": \"\", \n"
+" \"PictureDenoiseFilter\": \"off\", \n"
+" \"PictureDetelecine\": 0, \n"
+" \"PictureDetelecineCustom\": \"\", \n"
+" \"PictureHeight\": 720, \n"
+" \"PictureKeepRatio\": 0, \n"
+" \"PictureLeftCrop\": 0, \n"
+" \"PictureModulus\": 2, \n"
+" \"PicturePAR\": \"loose\", \n"
+" \"PictureRightCrop\": 0, \n"
+" \"PictureTopCrop\": 0, \n"
+" \"PictureWidth\": 1280, \n"
+" \"PresetDescription\": \"HandBrake's settings for playback on the iPad (all generations).\", \n"
+" \"PresetName\": \"iPad\", \n"
+" \"Type\": 0, \n"
+" \"UsesPictureFilters\": 1, \n"
+" \"UsesPictureSettings\": 1, \n"
+" \"VideoAvgBitrate\": \"2500\", \n"
+" \"VideoEncoder\": \"x264\", \n"
+" \"VideoFramerate\": \"30\", \n"
+" \"VideoFramerateMode\": \"pfr\", \n"
+" \"VideoGrayScale\": 0, \n"
+" \"VideoLevel\": \"3.1\", \n"
+" \"VideoOptionExtra\": \"\", \n"
+" \"VideoPreset\": \"medium\", \n"
+" \"VideoProfile\": \"high\", \n"
+" \"VideoQualitySlider\": 20.0, \n"
+" \"VideoQualityType\": 2, \n"
+" \"VideoTune\": \"\", \n"
+" \"VideoTurboTwoPass\": 0, \n"
+" \"VideoTwoPass\": 0, \n"
+" \"x264Option\": \"\", \n"
+" \"x264UseAdvancedOptions\": 0\n"
+" }, \n"
+" {\n"
+" \"AudioAllowAACPass\": 1, \n"
+" \"AudioAllowAC3Pass\": 1, \n"
+" \"AudioAllowDTSHDPass\": 1, \n"
+" \"AudioAllowDTSPass\": 1, \n"
+" \"AudioAllowMP3Pass\": 1, \n"
+" \"AudioEncoderFallback\": \"ac3\", \n"
+" \"AudioList\": [\n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"aac\", \n"
+" \"AudioMixdown\": \"dpl2\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }, \n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"copy:ac3\", \n"
+" \"AudioMixdown\": \"none\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }\n"
+" ], \n"
+" \"ChapterMarkers\": 1, \n"
+" \"Default\": 0, \n"
+" \"FileFormat\": \"mp4\", \n"
+" \"Folder\": false, \n"
+" \"Mp4HttpOptimize\": 0, \n"
+" \"Mp4iPodCompatible\": 0, \n"
+" \"PictureAutoCrop\": 1, \n"
+" \"PictureBottomCrop\": 0, \n"
+" \"PictureDeblock\": 0, \n"
+" \"PictureDecomb\": 0, \n"
+" \"PictureDecombCustom\": \"\", \n"
+" \"PictureDecombDeinterlace\": 1, \n"
+" \"PictureDeinterlace\": 0, \n"
+" \"PictureDeinterlaceCustom\": \"\", \n"
+" \"PictureDenoiseCustom\": \"\", \n"
+" \"PictureDenoiseFilter\": \"off\", \n"
+" \"PictureDetelecine\": 0, \n"
+" \"PictureDetelecineCustom\": \"\", \n"
+" \"PictureHeight\": 720, \n"
+" \"PictureKeepRatio\": 0, \n"
+" \"PictureLeftCrop\": 0, \n"
+" \"PictureModulus\": 2, \n"
+" \"PicturePAR\": \"loose\", \n"
+" \"PictureRightCrop\": 0, \n"
+" \"PictureTopCrop\": 0, \n"
+" \"PictureWidth\": 960, \n"
+" \"PresetDescription\": \"HandBrake's settings for the original AppleTV. Includes Dolby Digital audio for surround sound. Also compatible with iOS devices released since 2009.\", \n"
+" \"PresetName\": \"AppleTV\", \n"
+" \"Type\": 0, \n"
+" \"UsesPictureFilters\": 1, \n"
+" \"UsesPictureSettings\": 1, \n"
+" \"VideoAvgBitrate\": \"2500\", \n"
+" \"VideoEncoder\": \"x264\", \n"
+" \"VideoFramerate\": \"30\", \n"
+" \"VideoFramerateMode\": \"pfr\", \n"
+" \"VideoGrayScale\": 0, \n"
+" \"VideoLevel\": \"3.1\", \n"
+" \"VideoOptionExtra\": \"qpmin=4:cabac=0:ref=2:b-pyramid=none:weightb=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500\", \n"
+" \"VideoPreset\": \"medium\", \n"
+" \"VideoProfile\": \"high\", \n"
+" \"VideoQualitySlider\": 20.0, \n"
+" \"VideoQualityType\": 2, \n"
+" \"VideoTune\": \"\", \n"
+" \"VideoTurboTwoPass\": 0, \n"
+" \"VideoTwoPass\": 0, \n"
+" \"x264Option\": \"\", \n"
+" \"x264UseAdvancedOptions\": 0\n"
+" }, \n"
+" {\n"
+" \"AudioAllowAACPass\": 1, \n"
+" \"AudioAllowAC3Pass\": 1, \n"
+" \"AudioAllowDTSHDPass\": 1, \n"
+" \"AudioAllowDTSPass\": 1, \n"
+" \"AudioAllowMP3Pass\": 1, \n"
+" \"AudioEncoderFallback\": \"ac3\", \n"
+" \"AudioList\": [\n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"aac\", \n"
+" \"AudioMixdown\": \"dpl2\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }, \n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"copy:ac3\", \n"
+" \"AudioMixdown\": \"none\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }\n"
+" ], \n"
+" \"ChapterMarkers\": 1, \n"
+" \"Default\": 0, \n"
+" \"FileFormat\": \"mp4\", \n"
+" \"Folder\": false, \n"
+" \"Mp4HttpOptimize\": 0, \n"
+" \"Mp4iPodCompatible\": 0, \n"
+" \"PictureAutoCrop\": 1, \n"
+" \"PictureBottomCrop\": 0, \n"
+" \"PictureDeblock\": 0, \n"
+" \"PictureDecomb\": 0, \n"
+" \"PictureDecombCustom\": \"\", \n"
+" \"PictureDecombDeinterlace\": 1, \n"
+" \"PictureDeinterlace\": 0, \n"
+" \"PictureDeinterlaceCustom\": \"\", \n"
+" \"PictureDenoiseCustom\": \"\", \n"
+" \"PictureDenoiseFilter\": \"off\", \n"
+" \"PictureDetelecine\": 0, \n"
+" \"PictureDetelecineCustom\": \"\", \n"
+" \"PictureHeight\": 720, \n"
+" \"PictureKeepRatio\": 0, \n"
+" \"PictureLeftCrop\": 0, \n"
+" \"PictureModulus\": 2, \n"
+" \"PicturePAR\": \"loose\", \n"
+" \"PictureRightCrop\": 0, \n"
+" \"PictureTopCrop\": 0, \n"
+" \"PictureWidth\": 1280, \n"
+" \"PresetDescription\": \"HandBrake's settings for the second-generation AppleTV. Includes Dolby Digital audio for surround sound. NOT compatible with the original AppleTV.\", \n"
+" \"PresetName\": \"AppleTV 2\", \n"
+" \"Type\": 0, \n"
+" \"UsesPictureFilters\": 1, \n"
+" \"UsesPictureSettings\": 1, \n"
+" \"VideoAvgBitrate\": \"2500\", \n"
+" \"VideoEncoder\": \"x264\", \n"
+" \"VideoFramerate\": \"30\", \n"
+" \"VideoFramerateMode\": \"pfr\", \n"
+" \"VideoGrayScale\": 0, \n"
+" \"VideoLevel\": \"3.1\", \n"
+" \"VideoOptionExtra\": \"\", \n"
+" \"VideoPreset\": \"medium\", \n"
+" \"VideoProfile\": \"high\", \n"
+" \"VideoQualitySlider\": 20.0, \n"
+" \"VideoQualityType\": 2, \n"
+" \"VideoTune\": \"\", \n"
+" \"VideoTurboTwoPass\": 0, \n"
+" \"VideoTwoPass\": 0, \n"
+" \"x264Option\": \"\", \n"
+" \"x264UseAdvancedOptions\": 0\n"
+" }, \n"
+" {\n"
+" \"AudioAllowAACPass\": 1, \n"
+" \"AudioAllowAC3Pass\": 1, \n"
+" \"AudioAllowDTSHDPass\": 1, \n"
+" \"AudioAllowDTSPass\": 1, \n"
+" \"AudioAllowMP3Pass\": 1, \n"
+" \"AudioEncoderFallback\": \"ac3\", \n"
+" \"AudioList\": [\n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"aac\", \n"
+" \"AudioMixdown\": \"dpl2\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }, \n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"copy:ac3\", \n"
+" \"AudioMixdown\": \"none\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }\n"
+" ], \n"
+" \"ChapterMarkers\": 1, \n"
+" \"Default\": 0, \n"
+" \"FileFormat\": \"mp4\", \n"
+" \"Folder\": false, \n"
+" \"Mp4HttpOptimize\": 0, \n"
+" \"Mp4iPodCompatible\": 0, \n"
+" \"PictureAutoCrop\": 1, \n"
+" \"PictureBottomCrop\": 0, \n"
+" \"PictureDeblock\": 0, \n"
+" \"PictureDecomb\": 3, \n"
+" \"PictureDecombCustom\": \"\", \n"
+" \"PictureDecombDeinterlace\": 1, \n"
+" \"PictureDeinterlace\": 0, \n"
+" \"PictureDeinterlaceCustom\": \"\", \n"
+" \"PictureDenoiseCustom\": \"\", \n"
+" \"PictureDenoiseFilter\": \"off\", \n"
+" \"PictureDetelecine\": 0, \n"
+" \"PictureDetelecineCustom\": \"\", \n"
+" \"PictureHeight\": 1080, \n"
+" \"PictureKeepRatio\": 0, \n"
+" \"PictureLeftCrop\": 0, \n"
+" \"PictureModulus\": 2, \n"
+" \"PicturePAR\": \"loose\", \n"
+" \"PictureRightCrop\": 0, \n"
+" \"PictureTopCrop\": 0, \n"
+" \"PictureWidth\": 1920, \n"
+" \"PresetDescription\": \"HandBrake's settings for the third-generation AppleTV. Includes Dolby Digital audio for surround sound. NOT compatible with the original AppleTV. May stutter on the second-generation AppleTV.\", \n"
+" \"PresetName\": \"AppleTV 3\", \n"
+" \"Type\": 0, \n"
+" \"UsesPictureFilters\": 1, \n"
+" \"UsesPictureSettings\": 1, \n"
+" \"VideoAvgBitrate\": \"2500\", \n"
+" \"VideoEncoder\": \"x264\", \n"
+" \"VideoFramerate\": \"30\", \n"
+" \"VideoFramerateMode\": \"pfr\", \n"
+" \"VideoGrayScale\": 0, \n"
+" \"VideoLevel\": \"4.0\", \n"
+" \"VideoOptionExtra\": \"\", \n"
+" \"VideoPreset\": \"medium\", \n"
+" \"VideoProfile\": \"high\", \n"
+" \"VideoQualitySlider\": 20.0, \n"
+" \"VideoQualityType\": 2, \n"
+" \"VideoTune\": \"\", \n"
+" \"VideoTurboTwoPass\": 0, \n"
+" \"VideoTwoPass\": 0, \n"
+" \"x264Option\": \"\", \n"
+" \"x264UseAdvancedOptions\": 0\n"
+" }, \n"
+" {\n"
+" \"AudioAllowAACPass\": 1, \n"
+" \"AudioAllowAC3Pass\": 1, \n"
+" \"AudioAllowDTSHDPass\": 1, \n"
+" \"AudioAllowDTSPass\": 1, \n"
+" \"AudioAllowMP3Pass\": 1, \n"
+" \"AudioEncoderFallback\": \"ac3\", \n"
+" \"AudioList\": [\n"
+" {\n"
+" \"AudioBitrate\": \"128\", \n"
+" \"AudioEncoder\": \"aac\", \n"
+" \"AudioMixdown\": \"dpl2\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }\n"
+" ], \n"
+" \"ChapterMarkers\": 0, \n"
+" \"Default\": 0, \n"
+" \"FileFormat\": \"mp4\", \n"
+" \"Folder\": false, \n"
+" \"Mp4HttpOptimize\": 0, \n"
+" \"Mp4iPodCompatible\": 0, \n"
+" \"PictureAutoCrop\": 1, \n"
+" \"PictureBottomCrop\": 0, \n"
+" \"PictureDeblock\": 0, \n"
+" \"PictureDecomb\": 0, \n"
+" \"PictureDecombCustom\": \"\", \n"
+" \"PictureDecombDeinterlace\": 1, \n"
+" \"PictureDeinterlace\": 0, \n"
+" \"PictureDeinterlaceCustom\": \"\", \n"
+" \"PictureDenoiseCustom\": \"\", \n"
+" \"PictureDenoiseFilter\": \"off\", \n"
+" \"PictureDetelecine\": 0, \n"
+" \"PictureDetelecineCustom\": \"\", \n"
+" \"PictureHeight\": 576, \n"
+" \"PictureKeepRatio\": 0, \n"
+" \"PictureLeftCrop\": 0, \n"
+" \"PictureModulus\": 2, \n"
+" \"PicturePAR\": \"loose\", \n"
+" \"PictureRightCrop\": 0, \n"
+" \"PictureTopCrop\": 0, \n"
+" \"PictureWidth\": 720, \n"
+" \"PresetDescription\": \"HandBrake's settings for midrange devices running Android 2.3 or later.\", \n"
+" \"PresetName\": \"Android\", \n"
+" \"Type\": 0, \n"
+" \"UsesPictureFilters\": 1, \n"
+" \"UsesPictureSettings\": 1, \n"
+" \"VideoAvgBitrate\": \"2500\", \n"
+" \"VideoEncoder\": \"x264\", \n"
+" \"VideoFramerate\": \"30\", \n"
+" \"VideoFramerateMode\": \"pfr\", \n"
+" \"VideoGrayScale\": 0, \n"
+" \"VideoLevel\": \"3.0\", \n"
+" \"VideoOptionExtra\": \"\", \n"
+" \"VideoPreset\": \"medium\", \n"
+" \"VideoProfile\": \"main\", \n"
+" \"VideoQualitySlider\": 22.0, \n"
+" \"VideoQualityType\": 2, \n"
+" \"VideoTune\": \"\", \n"
+" \"VideoTurboTwoPass\": 0, \n"
+" \"VideoTwoPass\": 0, \n"
+" \"x264Option\": \"\", \n"
+" \"x264UseAdvancedOptions\": 0\n"
+" }, \n"
+" {\n"
+" \"AudioAllowAACPass\": 1, \n"
+" \"AudioAllowAC3Pass\": 1, \n"
+" \"AudioAllowDTSHDPass\": 1, \n"
+" \"AudioAllowDTSPass\": 1, \n"
+" \"AudioAllowMP3Pass\": 1, \n"
+" \"AudioEncoderFallback\": \"ac3\", \n"
+" \"AudioList\": [\n"
+" {\n"
+" \"AudioBitrate\": \"128\", \n"
+" \"AudioEncoder\": \"aac\", \n"
+" \"AudioMixdown\": \"dpl2\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }\n"
+" ], \n"
+" \"ChapterMarkers\": 0, \n"
+" \"Default\": 0, \n"
+" \"FileFormat\": \"mp4\", \n"
+" \"Folder\": false, \n"
+" \"Mp4HttpOptimize\": 0, \n"
+" \"Mp4iPodCompatible\": 0, \n"
+" \"PictureAutoCrop\": 1, \n"
+" \"PictureBottomCrop\": 0, \n"
+" \"PictureDeblock\": 0, \n"
+" \"PictureDecomb\": 0, \n"
+" \"PictureDecombCustom\": \"\", \n"
+" \"PictureDecombDeinterlace\": 1, \n"
+" \"PictureDeinterlace\": 0, \n"
+" \"PictureDeinterlaceCustom\": \"\", \n"
+" \"PictureDenoiseCustom\": \"\", \n"
+" \"PictureDenoiseFilter\": \"off\", \n"
+" \"PictureDetelecine\": 0, \n"
+" \"PictureDetelecineCustom\": \"\", \n"
+" \"PictureHeight\": 720, \n"
+" \"PictureKeepRatio\": 0, \n"
+" \"PictureLeftCrop\": 0, \n"
+" \"PictureModulus\": 2, \n"
+" \"PicturePAR\": \"loose\", \n"
+" \"PictureRightCrop\": 0, \n"
+" \"PictureTopCrop\": 0, \n"
+" \"PictureWidth\": 1280, \n"
+" \"PresetDescription\": \"HandBrake's preset for tablets running Android 2.3 or later.\", \n"
+" \"PresetName\": \"Android Tablet\", \n"
+" \"Type\": 0, \n"
+" \"UsesPictureFilters\": 1, \n"
+" \"UsesPictureSettings\": 1, \n"
+" \"VideoAvgBitrate\": \"2500\", \n"
+" \"VideoEncoder\": \"x264\", \n"
+" \"VideoFramerate\": \"30\", \n"
+" \"VideoFramerateMode\": \"pfr\", \n"
+" \"VideoGrayScale\": 0, \n"
+" \"VideoLevel\": \"3.1\", \n"
+" \"VideoOptionExtra\": \"\", \n"
+" \"VideoPreset\": \"medium\", \n"
+" \"VideoProfile\": \"main\", \n"
+" \"VideoQualitySlider\": 22.0, \n"
+" \"VideoQualityType\": 2, \n"
+" \"VideoTune\": \"\", \n"
+" \"VideoTurboTwoPass\": 0, \n"
+" \"VideoTwoPass\": 0, \n"
+" \"x264Option\": \"\", \n"
+" \"x264UseAdvancedOptions\": 0\n"
+" }, \n"
+" {\n"
+" \"AudioAllowAACPass\": 1, \n"
+" \"AudioAllowAC3Pass\": 1, \n"
+" \"AudioAllowDTSHDPass\": 1, \n"
+" \"AudioAllowDTSPass\": 1, \n"
+" \"AudioAllowMP3Pass\": 1, \n"
+" \"AudioEncoderFallback\": \"ac3\", \n"
+" \"AudioList\": [\n"
+" {\n"
+" \"AudioBitrate\": \"128\", \n"
+" \"AudioEncoder\": \"aac\", \n"
+" \"AudioMixdown\": \"dpl2\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }\n"
+" ], \n"
+" \"ChapterMarkers\": 0, \n"
+" \"Default\": 0, \n"
+" \"FileFormat\": \"mp4\", \n"
+" \"Folder\": false, \n"
+" \"Mp4HttpOptimize\": 0, \n"
+" \"Mp4iPodCompatible\": 0, \n"
+" \"PictureAutoCrop\": 1, \n"
+" \"PictureBottomCrop\": 0, \n"
+" \"PictureDeblock\": 0, \n"
+" \"PictureDecomb\": 0, \n"
+" \"PictureDecombCustom\": \"\", \n"
+" \"PictureDecombDeinterlace\": 1, \n"
+" \"PictureDeinterlace\": 0, \n"
+" \"PictureDeinterlaceCustom\": \"\", \n"
+" \"PictureDenoiseCustom\": \"\", \n"
+" \"PictureDenoiseFilter\": \"off\", \n"
+" \"PictureDetelecine\": 0, \n"
+" \"PictureDetelecineCustom\": \"\", \n"
+" \"PictureHeight\": 720, \n"
+" \"PictureKeepRatio\": 1, \n"
+" \"PictureLeftCrop\": 0, \n"
+" \"PictureModulus\": 2, \n"
+" \"PicturePAR\": \"off\", \n"
+" \"PictureRightCrop\": 0, \n"
+" \"PictureTopCrop\": 0, \n"
+" \"PictureWidth\": 1280, \n"
+" \"PresetDescription\": \"HandBrake's preset for Windows Phone 8 devices\", \n"
+" \"PresetName\": \"Windows Phone 8\", \n"
+" \"Type\": 0, \n"
+" \"UsesPictureFilters\": 1, \n"
+" \"UsesPictureSettings\": 1, \n"
+" \"VideoAvgBitrate\": \"2500\", \n"
+" \"VideoEncoder\": \"x264\", \n"
+" \"VideoFramerate\": \"30\", \n"
+" \"VideoFramerateMode\": \"pfr\", \n"
+" \"VideoGrayScale\": 0, \n"
+" \"VideoLevel\": \"3.1\", \n"
+" \"VideoOptionExtra\": \"\", \n"
+" \"VideoPreset\": \"medium\", \n"
+" \"VideoProfile\": \"main\", \n"
+" \"VideoQualitySlider\": 22.0, \n"
+" \"VideoQualityType\": 2, \n"
+" \"VideoTune\": \"\", \n"
+" \"VideoTurboTwoPass\": 0, \n"
+" \"VideoTwoPass\": 0, \n"
+" \"x264Option\": \"\", \n"
+" \"x264UseAdvancedOptions\": 0\n"
+" }\n"
+" ], \n"
+" \"Default\": 0, \n"
+" \"Folder\": true, \n"
+" \"PresetName\": \"Devices\", \n"
+" \"Type\": 0\n"
+" }, \n"
+" {\n"
+" \"ChildrenArray\": [\n"
+" {\n"
+" \"AudioAllowAACPass\": 1, \n"
+" \"AudioAllowAC3Pass\": 1, \n"
+" \"AudioAllowDTSHDPass\": 1, \n"
+" \"AudioAllowDTSPass\": 1, \n"
+" \"AudioAllowMP3Pass\": 1, \n"
+" \"AudioEncoderFallback\": \"ac3\", \n"
+" \"AudioList\": [\n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"aac\", \n"
+" \"AudioMixdown\": \"dpl2\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }\n"
+" ], \n"
+" \"ChapterMarkers\": 1, \n"
+" \"Default\": 1, \n"
+" \"FileFormat\": \"mp4\", \n"
+" \"Folder\": false, \n"
+" \"Mp4HttpOptimize\": 0, \n"
+" \"Mp4iPodCompatible\": 0, \n"
+" \"PictureAutoCrop\": 1, \n"
+" \"PictureBottomCrop\": 0, \n"
+" \"PictureDeblock\": 0, \n"
+" \"PictureDecomb\": 0, \n"
+" \"PictureDecombCustom\": \"\", \n"
+" \"PictureDecombDeinterlace\": 1, \n"
+" \"PictureDeinterlace\": 0, \n"
+" \"PictureDeinterlaceCustom\": \"\", \n"
+" \"PictureDenoiseCustom\": \"\", \n"
+" \"PictureDenoiseFilter\": \"off\", \n"
+" \"PictureDetelecine\": 0, \n"
+" \"PictureDetelecineCustom\": \"\", \n"
+" \"PictureHeight\": 0, \n"
+" \"PictureKeepRatio\": 0, \n"
+" \"PictureLeftCrop\": 0, \n"
+" \"PictureModulus\": 2, \n"
+" \"PicturePAR\": \"loose\", \n"
+" \"PictureRightCrop\": 0, \n"
+" \"PictureTopCrop\": 0, \n"
+" \"PictureWidth\": 0, \n"
+" \"PresetDescription\": \"HandBrake's normal, default settings.\", \n"
+" \"PresetName\": \"Normal\", \n"
+" \"Type\": 0, \n"
+" \"UsesPictureFilters\": 1, \n"
+" \"UsesPictureSettings\": 1, \n"
+" \"VideoAvgBitrate\": \"2500\", \n"
+" \"VideoEncoder\": \"x264\", \n"
+" \"VideoFramerate\": \"auto\", \n"
+" \"VideoFramerateMode\": \"vfr\", \n"
+" \"VideoGrayScale\": 0, \n"
+" \"VideoLevel\": \"4.0\", \n"
+" \"VideoOptionExtra\": \"\", \n"
+" \"VideoPreset\": \"veryfast\", \n"
+" \"VideoProfile\": \"main\", \n"
+" \"VideoQualitySlider\": 20.0, \n"
+" \"VideoQualityType\": 2, \n"
+" \"VideoTune\": \"\", \n"
+" \"VideoTurboTwoPass\": 0, \n"
+" \"VideoTwoPass\": 0, \n"
+" \"x264Option\": \"\", \n"
+" \"x264UseAdvancedOptions\": 0\n"
+" }, \n"
+" {\n"
+" \"AudioAllowAACPass\": 1, \n"
+" \"AudioAllowAC3Pass\": 1, \n"
+" \"AudioAllowDTSHDPass\": 1, \n"
+" \"AudioAllowDTSPass\": 1, \n"
+" \"AudioAllowMP3Pass\": 1, \n"
+" \"AudioEncoderFallback\": \"ac3\", \n"
+" \"AudioList\": [\n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"aac\", \n"
+" \"AudioMixdown\": \"dpl2\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }, \n"
+" {\n"
+" \"AudioBitrate\": \"160\", \n"
+" \"AudioEncoder\": \"copy:ac3\", \n"
+" \"AudioMixdown\": \"none\", \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrack\": 1, \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0\n"
+" }\n"
+" ], \n"
+" \"ChapterMarkers\": 1, \n"
+" \"Default\": 0, \n"
+" \"FileFormat\": \"mp4\", \n"
+" \"Folder\": false, \n"
+" \"Mp4HttpOptimize\": 0, \n"
+" \"Mp4iPodCompatible\": 0, \n"
+" \"PictureAutoCrop\": 1, \n"
+" \"PictureBottomCrop\": 0, \n"
+" \"PictureDeblock\": 0, \n"
+" \"PictureDecomb\": 2, \n"
+" \"PictureDecombCustom\": \"\", \n"
+" \"PictureDecombDeinterlace\": 1, \n"
+" \"PictureDeinterlace\": 0, \n"
+" \"PictureDeinterlaceCustom\": \"\", \n"
+" \"PictureDenoiseCustom\": \"\", \n"
+" \"PictureDenoiseFilter\": \"off\", \n"
+" \"PictureDetelecine\": 0, \n"
+" \"PictureDetelecineCustom\": \"\", \n"
+" \"PictureHeight\": 0, \n"
+" \"PictureKeepRatio\": 0, \n"
+" \"PictureLeftCrop\": 0, \n"
+" \"PictureModulus\": 2, \n"
+" \"PicturePAR\": \"loose\", \n"
+" \"PictureRightCrop\": 0, \n"
+" \"PictureTopCrop\": 0, \n"
+" \"PictureWidth\": 0, \n"
+" \"PresetDescription\": \"HandBrake's general-purpose preset for High Profile H.264 video.\", \n"
+" \"PresetName\": \"High Profile\", \n"
+" \"Type\": 0, \n"
+" \"UsesPictureFilters\": 1, \n"
+" \"UsesPictureSettings\": 1, \n"
+" \"VideoAvgBitrate\": \"2500\", \n"
+" \"VideoEncoder\": \"x264\", \n"
+" \"VideoFramerate\": \"auto\", \n"
+" \"VideoFramerateMode\": \"vfr\", \n"
+" \"VideoGrayScale\": 0, \n"
+" \"VideoLevel\": \"4.1\", \n"
+" \"VideoOptionExtra\": \"\", \n"
+" \"VideoPreset\": \"medium\", \n"
+" \"VideoProfile\": \"high\", \n"
+" \"VideoQualitySlider\": 20.0, \n"
+" \"VideoQualityType\": 2, \n"
+" \"VideoTune\": \"\", \n"
+" \"VideoTurboTwoPass\": 0, \n"
+" \"VideoTwoPass\": 0, \n"
+" \"x264Option\": \"\", \n"
+" \"x264UseAdvancedOptions\": 0\n"
+" }\n"
+" ], \n"
+" \"Default\": 0, \n"
+" \"Folder\": true, \n"
+" \"PresetName\": \"Regular\", \n"
+" \"Type\": 0\n"
+" }\n"
+" ], \n"
+" \"PresetTemplate\": {\n"
+" \"Preset\": {\n"
+" \"AudioAllowAACPass\": false, \n"
+" \"AudioAllowAC3Pass\": true, \n"
+" \"AudioAllowDTSHDPass\": false, \n"
+" \"AudioAllowDTSPass\": false, \n"
+" \"AudioAllowEAC3Pass\": false, \n"
+" \"AudioAllowFLACPass\": false, \n"
+" \"AudioAllowMP3Pass\": false, \n"
+" \"AudioAllowTRUEHDPass\": false, \n"
+" \"AudioCopyMask\": [], \n"
+" \"AudioEncoderFallback\": \"ac3\", \n"
+" \"AudioLanguageList\": [\n"
+" \"und\"\n"
+" ], \n"
+" \"AudioList\": [\n"
+" {\n"
+" \"AudioBitrate\": \"192\", \n"
+" \"AudioCompressionLevel\": -1.0, \n"
+" \"AudioDitherMethod\": \"auto\", \n"
+" \"AudioEncoder\": \"copy:ac3\", \n"
+" \"AudioMixdown\": \"dpl2\", \n"
+" \"AudioNormalizeMixLevel\": false, \n"
+" \"AudioSamplerate\": \"auto\", \n"
+" \"AudioTrackDRCSlider\": 0.0, \n"
+" \"AudioTrackGainSlider\": 0.0, \n"
+" \"AudioTrackQuality\": -1.0, \n"
+" \"AudioTrackQualityEnable\": false\n"
+" }\n"
+" ], \n"
+" \"AudioSecondaryEncoderMode\": true, \n"
+" \"AudioTrackSelectionBehavior\": \"first\", \n"
+" \"ChapterMarkers\": true, \n"
+" \"Default\": false, \n"
+" \"FileFormat\": \"mp4\", \n"
+" \"Folder\": false, \n"
+" \"Mp4HttpOptimize\": false, \n"
+" \"Mp4iPodCompatible\": false, \n"
+" \"PictureAutoCrop\": true, \n"
+" \"PictureBottomCrop\": 0, \n"
+" \"PictureDARWidth\": 0, \n"
+" \"PictureDeblock\": 0, \n"
+" \"PictureDecomb\": \"off\", \n"
+" \"PictureDecombCustom\": \"\", \n"
+" \"PictureDecombDeinterlace\": true, \n"
+" \"PictureDeinterlace\": \"off\", \n"
+" \"PictureDeinterlaceCustom\": \"\", \n"
+" \"PictureDenoiseCustom\": \"\", \n"
+" \"PictureDenoiseFilter\": \"off\", \n"
+" \"PictureDenoisePreset\": \"medium\", \n"
+" \"PictureDenoiseTune\": \"none\", \n"
+" \"PictureDetelecine\": \"off\", \n"
+" \"PictureDetelecineCustom\": \"\", \n"
+" \"PictureForceHeight\": 0, \n"
+" \"PictureForceWidth\": 0, \n"
+" \"PictureHeight\": 0, \n"
+" \"PictureItuPAR\": false, \n"
+" \"PictureKeepRatio\": true, \n"
+" \"PictureLeftCrop\": 0, \n"
+" \"PictureLooseCrop\": false, \n"
+" \"PictureModulus\": 2, \n"
+" \"PicturePAR\": \"loose\", \n"
+" \"PicturePARHeight\": 720, \n"
+" \"PicturePARWidth\": 853, \n"
+" \"PictureRightCrop\": 0, \n"
+" \"PictureRotate\": 0, \n"
+" \"PictureTopCrop\": 0, \n"
+" \"PictureWidth\": 0, \n"
+" \"PresetDescription\": \"\", \n"
+" \"PresetName\": \"Name Missing\", \n"
+" \"SubtitleAddCC\": false, \n"
+" \"SubtitleAddForeignAudioSearch\": false, \n"
+" \"SubtitleAddForeignAudioSubtitle\": false, \n"
+" \"SubtitleBurnBDSub\": false, \n"
+" \"SubtitleBurnBehavior\": \"none\", \n"
+" \"SubtitleBurnDVDSub\": false, \n"
+" \"SubtitleLanguageList\": [], \n"
+" \"SubtitleTrackSelectionBehavior\": \"none\", \n"
+" \"Type\": 1, \n"
+" \"UsesPictureFilters\": true, \n"
+" \"UsesPictureSettings\": 2, \n"
+" \"VideoAvgBitrate\": 1800, \n"
+" \"VideoColorMatrixCode\": 0, \n"
+" \"VideoEncoder\": \"x264\", \n"
+" \"VideoFramerate\": \"auto\", \n"
+" \"VideoFramerateMode\": \"vfr\", \n"
+" \"VideoGrayScale\": false, \n"
+" \"VideoHWDecode\": false, \n"
+" \"VideoLevel\": \"auto\", \n"
+" \"VideoOptionExtra\": \"\", \n"
+" \"VideoPreset\": \"medium\", \n"
+" \"VideoProfile\": \"auto\", \n"
+" \"VideoQSVAsyncDepth\": 4, \n"
+" \"VideoQSVDecode\": false, \n"
+" \"VideoQualitySlider\": 20.0, \n"
+" \"VideoQualityType\": 2, \n"
+" \"VideoScaler\": \"swscale\", \n"
+" \"VideoTune\": \"none\", \n"
+" \"VideoTurboTwoPass\": false, \n"
+" \"VideoTwoPass\": false, \n"
+" \"x264Option\": \"\", \n"
+" \"x264UseAdvancedOptions\": false\n"
+" }, \n"
+" \"VersionMajor\": 10, \n"
+" \"VersionMicro\": 0, \n"
+" \"VersionMinor\": 0\n"
+" }\n"
+"}\n";
diff --git a/libhb/common.c b/libhb/common.c
index 7bb3f2a56..8a7b72522 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -262,6 +262,9 @@ hb_encoder_internal_t hb_audio_encoders[] =
{ { "Vorbis (vorbis)", "libvorbis", NULL, HB_ACODEC_VORBIS, HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_VORBIS, },
{ { "FLAC (ffmpeg)", "ffflac", NULL, HB_ACODEC_FFFLAC, HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_FLAC, },
{ { "FLAC (24-bit)", "ffflac24", NULL, HB_ACODEC_FFFLAC24, HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_FLAC, },
+ // generic names
+ { { "AAC", "aac", NULL, 0, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_AAC, },
+ { { "HE-AAC", "haac", NULL, 0, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_AAC_HE, },
// actual encoders
{ { "AAC (CoreAudio)", "ca_aac", "AAC (Apple AudioToolbox)", HB_ACODEC_CA_AAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AAC, },
{ { "HE-AAC (CoreAudio)", "ca_haac", "HE-AAC (Apple AudioToolbox)", HB_ACODEC_CA_HAAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AAC_HE, },
@@ -1793,7 +1796,7 @@ int hb_video_encoder_get_default(int muxer)
}
fail:
- return 0;
+ return HB_VCODEC_INVALID;
}
hb_encoder_t * hb_video_encoder_get_from_codec(int codec)
@@ -1826,7 +1829,7 @@ int hb_video_encoder_get_from_name(const char *name)
}
fail:
- return 0;
+ return HB_VCODEC_INVALID;
}
const char* hb_video_encoder_get_name(int encoder)
@@ -1937,10 +1940,10 @@ int hb_audio_encoder_get_fallback_for_passthru(int passthru)
}
// passthru tracks are often the second audio from the same source track
- // if we don't have an encoder matching the passthru codec, return 0
+ // if we don't have an encoder matching the passthru codec, return INVALID
// dropping the track, as well as ensuring that there is at least one
// audio track in the output is then up to the UIs
- return 0;
+ return HB_ACODEC_INVALID;
}
int hb_audio_encoder_get_default(int muxer)
@@ -1974,7 +1977,7 @@ int hb_audio_encoder_get_default(int muxer)
}
fail:
- return 0;
+ return HB_ACODEC_INVALID;
}
hb_encoder_t* hb_audio_encoder_get_from_codec(int codec)
@@ -1988,7 +1991,7 @@ hb_encoder_t* hb_audio_encoder_get_from_codec(int codec)
}
}
- return 0;
+ return NULL;
}
int hb_audio_encoder_get_from_name(const char *name)
@@ -2007,7 +2010,7 @@ int hb_audio_encoder_get_from_name(const char *name)
}
fail:
- return 0;
+ return HB_ACODEC_INVALID;
}
const char* hb_audio_encoder_get_name(int encoder)
@@ -2302,7 +2305,7 @@ int hb_container_get_from_name(const char *name)
}
fail:
- return 0;
+ return HB_MUX_INVALID;
}
int hb_container_get_from_extension(const char *extension)
@@ -2320,7 +2323,7 @@ int hb_container_get_from_extension(const char *extension)
}
fail:
- return 0;
+ return HB_MUX_INVALID;
}
const char* hb_container_get_name(int format)
diff --git a/libhb/hb.c b/libhb/hb.c
index 7a7429153..f4ec2308f 100644
--- a/libhb/hb.c
+++ b/libhb/hb.c
@@ -1655,6 +1655,9 @@ int hb_global_init()
*/
hb_buffer_pool_init();
+ // Initialize the builtin presets hb_dict_t
+ hb_presets_builtin_init();
+
return result;
}
@@ -1667,6 +1670,8 @@ void hb_global_close()
DIR * dir;
struct dirent * entry;
+ hb_presets_free();
+
/* Find and remove temp folder */
memset( dirname, 0, 1024 );
hb_get_temporary_directory( dirname );
diff --git a/libhb/hb.h b/libhb/hb.h
index 38418ac3b..215455cda 100644
--- a/libhb/hb.h
+++ b/libhb/hb.h
@@ -14,11 +14,12 @@
extern "C" {
#endif
-#include "project.h"
#include "common.h"
+#include "project.h"
#include "compat.h"
#include "hb_dict.h"
#include "hb_json.h"
+#include "preset.h"
#include "param.h"
/* hb_init()
diff --git a/libhb/hb_dict.c b/libhb/hb_dict.c
index 7fd12b565..866803f58 100644
--- a/libhb/hb_dict.c
+++ b/libhb/hb_dict.c
@@ -21,6 +21,11 @@ hb_value_type_t hb_value_type(const hb_value_t *value)
return type;
}
+int hb_value_is_number(const hb_value_t *value)
+{
+ return json_is_number(value);
+}
+
hb_value_t * hb_value_dup(const hb_value_t *value)
{
if (value == NULL) return NULL;
diff --git a/libhb/hb_dict.h b/libhb/hb_dict.h
index 52898e413..2a4f71cfe 100644
--- a/libhb/hb_dict.h
+++ b/libhb/hb_dict.h
@@ -84,6 +84,7 @@ size_t hb_value_array_len(const hb_value_array_t *array);
/* hb_value_t */
int hb_value_type(const hb_value_t *value);
+int hb_value_is_number(const hb_value_t *value);
hb_value_t * hb_value_dup(const hb_value_t *value);
void hb_value_incref(hb_value_t *value);
void hb_value_decref(hb_value_t *value);
diff --git a/libhb/lang.c b/libhb/lang.c
index f40520a23..34466da34 100644
--- a/libhb/lang.c
+++ b/libhb/lang.c
@@ -202,10 +202,16 @@ static const iso639_lang_t languages[] =
static const int lang_count = sizeof(languages) / sizeof(languages[0]);
-iso639_lang_t * lang_lookup( const char * str )
+const iso639_lang_t * lang_lookup( const char * str )
{
iso639_lang_t * lang;
+ // We use "Any" as a synonym for undefined
+ if (!strcasecmp("any", str))
+ {
+ return &languages[0];
+ }
+
for (lang = (iso639_lang_t*) languages; lang->eng_name; lang++)
{
if ((lang->iso639_1 != NULL && !strcasecmp(lang->iso639_1, str)) ||
diff --git a/libhb/lang.h b/libhb/lang.h
index 21aab70af..7e7b82bd5 100644
--- a/libhb/lang.h
+++ b/libhb/lang.h
@@ -24,7 +24,7 @@ typedef struct iso639_lang_t
extern "C" {
#endif
/* find language, match any of names in lang struct */
-iso639_lang_t * lang_lookup( const char * str );
+const iso639_lang_t * lang_lookup( const char * str );
/* find language associated with ISO-639-1 language code */
iso639_lang_t * lang_for_code( int code );
diff --git a/libhb/libhb_presets.list b/libhb/libhb_presets.list
new file mode 100644
index 000000000..3742b1d40
--- /dev/null
+++ b/libhb/libhb_presets.list
@@ -0,0 +1,9 @@
+<resources>
+ <section name="PresetTemplate">
+ <integer name="VersionMajor" value="10" />
+ <integer name="VersionMinor" value="0" />
+ <integer name="VersionMicro" value="0" />
+ <json name="Preset" file="preset_template.json" />
+ </section>
+ <json name="PresetBuiltin" file="preset_builtin.json" />
+</resources>
diff --git a/libhb/module.defs b/libhb/module.defs
index 3d7d55ee3..f98930dd5 100644
--- a/libhb/module.defs
+++ b/libhb/module.defs
@@ -47,6 +47,12 @@ endif
LIBHB.GCC.D += __LIBHB__ USE_PTHREAD
LIBHB.GCC.I += $(LIBHB.build/) $(CONTRIB.build/)include
+ifneq (,$(filter $(BUILD.system),darwin cygwin mingw))
+LIBHB.GCC.I += $(CONTRIB.build/)include/libxml2
+else
+LIBHB.GCC.I += /usr/include/libxml2
+endif
+
ifeq ($(BUILD.system),cygwin)
LIBHB.GCC.D += SYS_CYGWIN
else ifeq ($(BUILD.system),darwin)
diff --git a/libhb/plist.c b/libhb/plist.c
new file mode 100644
index 000000000..f61014537
--- /dev/null
+++ b/libhb/plist.c
@@ -0,0 +1,678 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <inttypes.h>
+#include "libxml/parser.h"
+
+#include "common.h"
+#include "hb_dict.h"
+#include "plist.h"
+
+#define BUF_SZ (128*1024)
+
+static char *preamble =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+ "<plist version=\"1.0\">\n";
+static char *postfix =
+ "</plist>\n";
+
+typedef struct queue_item_s queue_item_t;
+struct queue_item_s
+{
+ void *value;
+ queue_item_t *next;
+};
+
+typedef struct
+{
+ queue_item_t *head;
+} queue_t;
+
+queue_t * queue_new()
+{
+ return calloc(1, sizeof(queue_t));
+}
+
+void queue_free(queue_t **_q)
+{
+ queue_t *q = *_q;
+ if (q == NULL)
+ return;
+
+ queue_item_t *n, *i = q->head;
+ while (i != NULL)
+ {
+ n = i->next;
+ free(i);
+ i = n;
+ }
+ free(q);
+ *_q = NULL;
+}
+
+void queue_push_head(queue_t *q, void *v)
+{
+ queue_item_t *i = calloc(1, sizeof(queue_item_t));
+ i->value = v;
+ i->next = q->head;
+ q->head = i;
+}
+
+void * queue_peek_head(queue_t *q)
+{
+ if (q->head != NULL)
+ return q->head->value;
+ return NULL;
+}
+
+void * queue_pop_head(queue_t *q)
+{
+ void *result;
+ queue_item_t *i;
+
+ if (q->head == NULL)
+ return NULL;
+
+ i = q->head;
+ result = i->value;
+ q->head = i->next;
+ free(i);
+
+ return result;
+}
+
+int queue_is_empty(queue_t *q)
+{
+ return q->head == NULL;
+}
+
+char * markup_escape_text(const char *str)
+{
+ int ii, jj;
+ int len = strlen(str);
+ int step = 40;
+ int alloc = len + step;
+ char *markup = malloc(alloc);
+
+ for (ii = 0, jj = 0; ii < len; ii++)
+ {
+ if (jj > alloc - 8)
+ {
+ alloc += step;
+ char *tmp = realloc(markup, alloc);
+ if (tmp == NULL)
+ {
+ markup[jj] = 0;
+ return markup;
+ }
+ markup = tmp;
+ }
+ switch (str[ii])
+ {
+ case '<':
+ markup[jj++] = '&';
+ markup[jj++] = 'l';
+ markup[jj++] = 't';
+ markup[jj++] = ';';
+ break;
+ case '>':
+ markup[jj++] = '&';
+ markup[jj++] = 'g';
+ markup[jj++] = 't';
+ markup[jj++] = ';';
+ break;
+ case '\'':
+ markup[jj++] = '&';
+ markup[jj++] = 'a';
+ markup[jj++] = 'p';
+ markup[jj++] = 'o';
+ markup[jj++] = 's';
+ markup[jj++] = ';';
+ break;
+ case '"':
+ markup[jj++] = '&';
+ markup[jj++] = 'q';
+ markup[jj++] = 'u';
+ markup[jj++] = 'o';
+ markup[jj++] = 't';
+ markup[jj++] = ';';
+ break;
+ case '&':
+ markup[jj++] = '&';
+ markup[jj++] = 'a';
+ markup[jj++] = 'm';
+ markup[jj++] = 'p';
+ markup[jj++] = ';';
+ break;
+ default:
+ markup[jj++] = str[ii];
+ break;
+ }
+ markup[jj] = 0;
+ }
+ return markup;
+}
+
+enum
+{
+ P_NONE = 0,
+ P_PLIST,
+ P_KEY,
+ P_ARRAY,
+ P_DICT,
+ P_INTEGER,
+ P_REAL,
+ P_STRING,
+ P_DATE,
+ P_TRUE,
+ P_FALSE,
+ P_DATA,
+};
+
+typedef struct
+{
+ char *tag;
+ int id;
+} tag_map_t;
+
+static tag_map_t tag_map[] =
+{
+ {"plist", P_PLIST},
+ {"key", P_KEY},
+ {"array", P_ARRAY},
+ {"dict", P_DICT},
+ {"integer", P_INTEGER},
+ {"real", P_REAL},
+ {"string", P_STRING},
+ {"date", P_DATE},
+ {"true", P_TRUE},
+ {"false", P_FALSE},
+ {"data", P_DATA},
+};
+#define TAG_MAP_SZ (sizeof(tag_map)/sizeof(tag_map_t))
+
+typedef struct
+{
+ char *key;
+ char *value;
+ hb_value_t *plist;
+ queue_t *stack;
+ queue_t *tag_stack;
+ int closed_top;
+} parse_data_t;
+
+static void
+start_element(
+ void *ud,
+ const xmlChar *xname,
+ const xmlChar **attr_names)
+{
+ char *name = (char*)xname;
+ parse_data_t *pd = (parse_data_t*)ud;
+ union
+ {
+ int id;
+ void * pid;
+ } id;
+ int ii;
+
+ // Check to see if the first element found has been closed
+ // If so, ignore any junk following it.
+ if (pd->closed_top)
+ return;
+
+ for (ii = 0; ii < TAG_MAP_SZ; ii++)
+ {
+ if (strcmp(name, tag_map[ii].tag) == 0)
+ {
+ id.id = tag_map[ii].id;
+ break;
+ }
+ }
+ if (ii == TAG_MAP_SZ)
+ {
+ hb_error("Unrecognized start tag (%s)", name);
+ return;
+ }
+ if (pd->value)
+ {
+ free(pd->value);
+ pd->value = NULL;
+ }
+ queue_push_head(pd->tag_stack, id.pid);
+ hb_value_type_t gtype = 0;
+ hb_value_t *gval = NULL;
+ hb_value_t *current = queue_peek_head(pd->stack);
+ switch (id.id)
+ {
+ case P_PLIST:
+ { // Ignore
+ } break;
+ case P_KEY:
+ {
+ if (pd->key) free(pd->key);
+ pd->key = NULL;
+ } break;
+ case P_DICT:
+ {
+ gval = hb_dict_init();
+ queue_push_head(pd->stack, gval);
+ } break;
+ case P_ARRAY:
+ {
+ gval = hb_value_array_init();
+ queue_push_head(pd->stack, gval);
+ } break;
+ case P_INTEGER:
+ {
+ } break;
+ case P_REAL:
+ {
+ } break;
+ case P_STRING:
+ {
+ } break;
+ case P_DATE:
+ {
+ } break;
+ case P_TRUE:
+ {
+ } break;
+ case P_FALSE:
+ {
+ } break;
+ case P_DATA:
+ {
+ } break;
+ }
+ // Add the element to the current container
+ if (gval)
+ { // There's an element to add
+ if (current == NULL)
+ {
+ pd->plist = gval;
+ return;
+ }
+ gtype = hb_value_type(current);
+ if (gtype == HB_VALUE_TYPE_ARRAY)
+ {
+ hb_value_array_append(current, gval);
+ }
+ else if (gtype == HB_VALUE_TYPE_DICT)
+ {
+ if (pd->key == NULL)
+ {
+ hb_error("No key for dictionary item");
+ hb_value_free(&gval);
+ }
+ else
+ {
+ hb_dict_set(current, pd->key, gval);
+ }
+ }
+ else
+ {
+ hb_error("Invalid container type. This shouldn't happen");
+ }
+ }
+}
+
+static void
+end_element(
+ void *ud,
+ const xmlChar *xname)
+{
+ char *name = (char*)xname;
+ parse_data_t *pd = (parse_data_t*)ud;
+ int id;
+ union
+ {
+ int id;
+ void * pid;
+ } start_id;
+ int ii;
+
+ // Check to see if the first element found has been closed
+ // If so, ignore any junk following it.
+ if (pd->closed_top)
+ return;
+
+ for (ii = 0; ii < TAG_MAP_SZ; ii++)
+ {
+ if (strcmp(name, tag_map[ii].tag) == 0)
+ {
+ id = tag_map[ii].id;
+ break;
+ }
+ }
+ if (ii == TAG_MAP_SZ)
+ {
+ hb_error("Unrecognized start tag (%s)", name);
+ return;
+ }
+ start_id.pid = queue_pop_head(pd->tag_stack);
+ if (start_id.id != id)
+ hb_error("start tag != end tag: (%s %d) %d", name, id, id);
+
+ hb_value_t *gval = NULL;
+ hb_value_t *current = queue_peek_head(pd->stack);
+ hb_value_type_t gtype = 0;
+ const char *value;
+ if (pd->value != NULL)
+ value = pd->value;
+ else
+ value = "";
+ switch (id)
+ {
+ case P_PLIST:
+ { // Ignore
+ } break;
+ case P_KEY:
+ {
+ if (pd->key) free(pd->key);
+ pd->key = strdup(value);
+ return;
+ } break;
+ case P_DICT:
+ {
+ queue_pop_head(pd->stack);
+ } break;
+ case P_ARRAY:
+ {
+ queue_pop_head(pd->stack);
+ } break;
+ case P_INTEGER:
+ {
+ uint64_t val = strtoll(value, NULL, 0);
+ gval = hb_value_int(val);
+ } break;
+ case P_REAL:
+ {
+ double val = strtod(value, NULL);
+ gval = hb_value_double(val);
+ } break;
+ case P_STRING:
+ {
+ gval = hb_value_string(value);
+ } break;
+ case P_TRUE:
+ {
+ gval = hb_value_bool(1);
+ } break;
+ case P_FALSE:
+ {
+ gval = hb_value_bool(0);
+ } break;
+ default:
+ {
+ hb_error("Unhandled plist type %d", id);
+ } break;
+ }
+ if (gval)
+ {
+ // Get the top of the data structure stack and if it's an array
+ // or dict, add the current element
+ if (current == NULL)
+ {
+ pd->plist = gval;
+ pd->closed_top = 1;
+ return;
+ }
+ gtype = hb_value_type(current);
+ if (gtype == HB_VALUE_TYPE_ARRAY)
+ {
+ hb_value_array_append(current, gval);
+ }
+ else if (gtype == HB_VALUE_TYPE_DICT)
+ {
+ if (pd->key == NULL)
+ {
+ hb_error("No key for dictionary item");
+ hb_value_free(&gval);
+ }
+ else
+ {
+ hb_dict_set(current, pd->key, gval);
+ }
+ }
+ else
+ {
+ hb_error("Invalid container type. This shouldn't happen");
+ }
+ }
+ if (queue_is_empty(pd->stack))
+ pd->closed_top = 1;
+}
+
+static void
+text_data(
+ void *ud,
+ const xmlChar *xtext,
+ int len)
+{
+ char *text = (char*)xtext;
+ parse_data_t *pd = (parse_data_t*)ud;
+ if (pd->value) free(pd->value);
+ pd->value = malloc(len + 1);
+ strncpy(pd->value, text, len);
+ pd->value[len] = 0;
+}
+
+static void
+parse_warning(void *ud, const char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ hb_valog(0, "Plist parse warning: ", msg, args);
+ va_end(args);
+}
+
+static void
+parse_error(void *ud, const char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ hb_valog(0, "Plist parse error: ", msg, args);
+ va_end(args);
+}
+
+hb_value_t*
+hb_plist_parse(const char *buf, size_t len)
+{
+ xmlSAXHandler parser;
+ parse_data_t pd;
+
+ pd.stack = queue_new();
+ pd.tag_stack = queue_new();
+ pd.key = NULL;
+ pd.value = NULL;
+ pd.plist = NULL;
+ pd.closed_top = 0;
+
+ memset(&parser, 0, sizeof(parser));
+ parser.initialized = XML_SAX2_MAGIC;
+ parser.startElement = start_element;
+ parser.endElement = end_element;
+ parser.characters = text_data;
+ parser.warning = parse_warning;
+ parser.error = parse_error;
+ int result = xmlSAXUserParseMemory(&parser, &pd, buf, len);
+ if (result != 0)
+ {
+ hb_error("Plist parse failed");
+ return NULL;
+ }
+ xmlCleanupParser();
+
+ if (pd.key) free(pd.key);
+ if (pd.value) free(pd.value);
+ queue_free(&pd.stack);
+ queue_free(&pd.tag_stack);
+
+ return pd.plist;
+}
+
+hb_value_t*
+hb_plist_parse_file(const char *filename)
+{
+ char *buffer;
+ size_t size;
+ hb_value_t *gval;
+ FILE *fd;
+
+ fd = fopen(filename, "r");
+ if (fd == NULL)
+ {
+ // File doesn't exist
+ return NULL;
+ }
+ fseek(fd, 0, SEEK_END);
+ size = ftell(fd);
+ fseek(fd, 0, SEEK_SET);
+ buffer = malloc(size+1);
+ size = fread(buffer, 1, size, fd);
+ buffer[size] = 0;
+ gval = hb_plist_parse(buffer, size);
+ free(buffer);
+ fclose(fd);
+ return gval;
+}
+
+static void
+indent_fprintf(FILE *file, int indent, const char *fmt, ...)
+{
+ va_list ap;
+
+ for (; indent; indent--)
+ putc('\t', file);
+ va_start(ap, fmt);
+ vfprintf(file, fmt, ap);
+ va_end(ap);
+}
+
+static void
+gval_write(FILE *file, hb_value_t *gval)
+{
+ static int indent = 0;
+ int ii;
+ hb_value_type_t gtype;
+
+ if (gval == NULL) return;
+ gtype = hb_value_type(gval);
+ if (gtype == HB_VALUE_TYPE_ARRAY)
+ {
+ hb_value_t *val;
+ int count;
+
+ indent_fprintf(file, indent, "<array>\n");
+ indent++;
+ count = hb_value_array_len(gval);
+ for (ii = 0; ii < count; ii++)
+ {
+ val = hb_value_array_get(gval, ii);
+ gval_write(file, val);
+ }
+ indent--;
+ indent_fprintf(file, indent, "</array>\n");
+ }
+ else if (gtype == HB_VALUE_TYPE_DICT)
+ {
+ const char *key;
+ hb_value_t *val;
+ hb_dict_iter_t iter;
+
+ indent_fprintf(file, indent, "<dict>\n");
+ indent++;
+
+ for (iter = hb_dict_iter_init(gval);
+ iter != HB_DICT_ITER_DONE;
+ iter = hb_dict_iter_next(gval, iter))
+ {
+ key = hb_dict_iter_key(iter);
+ val = hb_dict_iter_value(iter);
+ indent_fprintf(file, indent, "<key>%s</key>\n", key);
+ gval_write(file, val);
+ }
+
+ indent--;
+ indent_fprintf(file, indent, "</dict>\n");
+ }
+ else if (gtype == HB_VALUE_TYPE_BOOL)
+ {
+ char *tag;
+ if (hb_value_get_bool(gval))
+ {
+ tag = "true";
+ }
+ else
+ {
+ tag = "false";
+ }
+ indent_fprintf(file, indent, "<%s />\n", tag);
+ }
+ else if (gtype == HB_VALUE_TYPE_DOUBLE)
+ {
+ double val = hb_value_get_double(gval);
+ indent_fprintf(file, indent, "<real>%.17g</real>\n", val);
+ }
+ else if (gtype == HB_VALUE_TYPE_INT)
+ {
+ int64_t val = hb_value_get_int(gval);
+ indent_fprintf(file, indent, "<integer>%"PRId64"</integer>\n", val);
+ }
+ else if (gtype == HB_VALUE_TYPE_STRING)
+ {
+ const char *str = hb_value_get_string(gval);
+ char *esc = markup_escape_text(str);
+ indent_fprintf(file, indent, "<string>%s</string>\n", esc);
+ free(esc);
+ }
+ else
+ {
+ // Try to make anything thats unrecognized into a string
+ hb_error("Unhandled data type %d", gtype);
+ }
+}
+
+void
+hb_plist_write(FILE *file, hb_value_t *gval)
+{
+ fprintf(file, "%s", preamble);
+ gval_write(file, gval);
+ fprintf(file, "%s", postfix);
+}
+
+void
+hb_plist_write_file(const char *filename, hb_value_t *gval)
+{
+ FILE *file;
+
+ file = fopen(filename, "w");
+ if (file == NULL)
+ return;
+
+ hb_plist_write(file, gval);
+ fclose(file);
+}
+
+
+#if defined(PL_TEST)
+int
+main(int argc, char *argv[])
+{
+ hb_value_t *gval;
+
+ file = fopen(argv[1], "r");
+ gval = hb_plist_parse_file(file);
+ if (argc > 2)
+ hb_plist_write_file(argv[2], gval);
+ else
+ hb_plist_write(stdout, gval);
+ if (file) fclose (file);
+ return 0;
+}
+#endif
diff --git a/libhb/plist.h b/libhb/plist.h
new file mode 100644
index 000000000..901aeb4b0
--- /dev/null
+++ b/libhb/plist.h
@@ -0,0 +1,13 @@
+#if !defined(_HB_PLIST_H_)
+#define _HB_PLIST_H_
+
+#include <stdio.h>
+#include "hb_dict.h"
+
+hb_value_t * hb_plist_parse(const char *buf, size_t len);
+hb_value_t * hb_plist_parse_file(const char *filename);
+void hb_plist_write(FILE *file, hb_value_t *val);
+void hb_plist_write_file(const char *filename, hb_value_t *val);
+
+#endif // _HB_PLIST_H_
+
diff --git a/libhb/preset.c b/libhb/preset.c
new file mode 100644
index 000000000..3fef7c686
--- /dev/null
+++ b/libhb/preset.c
@@ -0,0 +1,2198 @@
+/* hb_preset.c
+
+ Copyright (c) 2003-2015 HandBrake Team
+ This file is part of the HandBrake source code
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License v2.
+ For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
+ */
+
+#include "builtin_presets.h"
+#include "hb.h"
+#include "hb_dict.h"
+#include "plist.h"
+
+#if defined(SYS_LINUX)
+#define HB_PRESET_PLIST_FILE "ghb/presets"
+#define HB_PRESET_JSON_FILE "ghb/presets.json"
+#elif defined(SYS_MINGW)
+#define HB_PRESET_PLIST_FILE "HandBrake\\user_presets.xml"
+#define HB_PRESET_JSON_FILE "HandBrake\\user_presets.json"
+#elif defined(SYS_DARWIN)
+#define HB_PRESET_PLIST_FILE "HandBrake/UserPresets.plist"
+#endif
+
+int hb_preset_version_major;
+int hb_preset_version_minor;
+int hb_preset_version_micro;
+
+static hb_value_t *hb_preset_template = NULL;
+static hb_value_t *hb_presets = NULL;
+static hb_value_t *hb_presets_custom = NULL;
+static hb_value_t *hb_presets_builtin = NULL;
+
+static int get_job_mux(hb_dict_t *job_dict)
+{
+ int mux;
+
+ hb_dict_t *dest_dict = hb_dict_get(job_dict, "Destination");
+ hb_value_t *mux_value = hb_dict_get(dest_dict, "Mux");
+ if (hb_value_type(mux_value) == HB_VALUE_TYPE_STRING)
+ {
+ mux = hb_container_get_from_name(hb_value_get_string(mux_value));
+ if (mux == 0)
+ mux = hb_container_get_from_extension(
+ hb_value_get_string(mux_value));
+ }
+ else
+ {
+ mux = hb_value_get_int(mux_value);
+ }
+ hb_container_t *container = hb_container_get_from_format(mux);
+ if (container == NULL)
+ {
+ char *str = hb_value_get_string_xform(mux_value);
+ hb_error("Invalid container (%s)", str);
+ free(str);
+ return HB_MUX_INVALID;
+ }
+ return mux;
+}
+
+static int get_audio_copy_mask(hb_dict_t * preset)
+{
+ int mask = HB_ACODEC_PASS_FLAG;
+
+ hb_value_array_t *copy_mask_array = hb_dict_get(preset, "AudioCopyMask");
+ if (copy_mask_array != NULL)
+ {
+ int count = hb_value_array_len(copy_mask_array);
+ int ii;
+ for (ii = 0; ii < count; ii++)
+ {
+ int codec;
+ hb_value_t *value;
+ value = hb_value_array_get(copy_mask_array, ii);
+ if (hb_value_type(value) == HB_VALUE_TYPE_STRING)
+ {
+ char *tmp = NULL;
+ const char * s = hb_value_get_string(value);
+ // Only codecs that start with 'copy:' can be copied
+ if (strncmp(s, "copy:", 5))
+ {
+ s = tmp = hb_strdup_printf("copy:%s", s);
+ }
+ codec = hb_audio_encoder_get_from_name(s);
+ if (codec == 0)
+ {
+ hb_error("Invalid audio codec in autopassthru copy mask (%s)", s);
+ hb_error("Codec name is invalid or can not be copied");
+ free(tmp);
+ return HB_ACODEC_INVALID;
+ }
+ free(tmp);
+ }
+ else
+ {
+ codec = hb_value_get_int(value);
+ }
+ mask |= codec;
+ }
+ }
+ else
+ {
+ mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowMP3Pass")) *
+ HB_ACODEC_MP3;
+ mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowAACPass")) *
+ HB_ACODEC_FFAAC;
+ mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowAC3Pass")) *
+ HB_ACODEC_AC3;
+ mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowDTSPass")) *
+ HB_ACODEC_DCA;
+ mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowDTSHDPass")) *
+ HB_ACODEC_DCA_HD;
+ mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowEAC3Pass")) *
+ HB_ACODEC_FFEAC3;
+ mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowFLACPass")) *
+ HB_ACODEC_FFFLAC;
+ mask |= hb_value_get_bool(hb_dict_get(preset, "AudioAllowTRUEHDPass")) *
+ HB_ACODEC_FFTRUEHD;
+ }
+ return mask;
+}
+
+static hb_dict_t * source_audio_track_used(hb_dict_t *track_dict, int track)
+{
+ // Kind of hacky, but keys must be strings
+ char key[8];
+ snprintf(key, sizeof(key), "%d", track);
+
+ hb_dict_t *used = hb_dict_get(track_dict, key);
+ if (used == NULL)
+ {
+ used = hb_dict_init();
+ hb_dict_set(track_dict, key, used);
+ }
+ return used;
+}
+
+// Find a source audio track matching given language
+static int find_audio_track(const hb_title_t *title,
+ const char *lang, int start)
+{
+ hb_audio_config_t * audio;
+ int ii, count;
+
+ count = hb_list_count(title->list_audio);
+ for (ii = start; ii < count; ii++)
+ {
+ audio = hb_list_audio_config_item(title->list_audio, ii);
+ // Ignore secondary audio types
+ if ((audio->lang.type == HB_AUDIO_TYPE_NONE ||
+ audio->lang.type == HB_AUDIO_TYPE_NORMAL) &&
+ (!strcmp(lang, audio->lang.iso639_2) || !strcmp(lang, "und")))
+ {
+ return ii;
+ }
+ }
+ return -1;
+}
+
+static int validate_audio_encoders(hb_dict_t *preset)
+{
+ hb_value_array_t * encoder_list = hb_dict_get(preset, "AudioList");
+ int count = hb_value_array_len(encoder_list);
+ int ii;
+ for (ii = 0; ii < count; ii++)
+ {
+ hb_value_t *audio_dict = hb_value_array_get(encoder_list, ii);
+ hb_value_t *value;
+ int codec, mix, sr;
+ value = hb_dict_get(audio_dict, "AudioEncoder");
+ if (hb_value_type(value) == HB_VALUE_TYPE_STRING)
+ {
+ codec = hb_audio_encoder_get_from_name(hb_value_get_string(value));
+ }
+ else
+ {
+ codec = hb_value_get_int(value);
+ }
+ if (hb_audio_encoder_get_from_codec(codec) == NULL)
+ {
+ char *str = hb_value_get_string_xform(value);
+ hb_error("Invalid audio encoder (%s)", str);
+ free(str);
+ return -1;
+ }
+
+ value = hb_dict_get(audio_dict, "AudioMixdown");
+ if (hb_value_type(value) == HB_VALUE_TYPE_STRING)
+ {
+ mix = hb_audio_encoder_get_from_name(hb_value_get_string(value));
+ }
+ else
+ {
+ mix = hb_value_get_int(value);
+ }
+ if (hb_mixdown_get_from_mixdown(mix) == NULL)
+ {
+ char *str = hb_value_get_string_xform(value);
+ hb_error("Invalid audio mixdown (%s)", str);
+ free(str);
+ return -1;
+ }
+
+ value = hb_dict_get(audio_dict, "AudioSamplerate");
+ if (hb_value_type(value) == HB_VALUE_TYPE_STRING)
+ {
+ const char *str = hb_value_get_string(value);
+ if (!strcasecmp(str, "source") ||
+ !strcasecmp(str, "auto") ||
+ !strcasecmp(str, "same as source"))
+ {
+ sr = 0;
+ }
+ else
+ {
+ sr = hb_audio_samplerate_get_from_name(str);
+ }
+ }
+ else
+ {
+ sr = hb_value_get_int(value);
+ }
+ if (sr != 0 && hb_audio_samplerate_get_name(sr) == NULL)
+ {
+ char *str = hb_value_get_string_xform(value);
+ hb_error("Invalid audio samplerate (%s)", str);
+ free(str);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int sanitize_audio_codec(int in_codec, int out_codec,
+ int copy_mask, int fallback, int mux)
+{
+ int codec = out_codec;
+ if (out_codec == HB_ACODEC_AUTO_PASS)
+ {
+ codec = hb_autopassthru_get_encoder(in_codec, copy_mask, fallback, mux);
+ }
+ else if ((out_codec & HB_ACODEC_PASS_FLAG) &&
+ !(in_codec & out_codec & HB_ACODEC_PASS_MASK))
+ {
+ codec = hb_audio_encoder_get_fallback_for_passthru(out_codec);
+ if (codec == 0)
+ codec = fallback;
+ }
+
+ // Check that encoder is valid for mux
+ const hb_encoder_t *encoder = NULL;
+ while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL)
+ {
+ if (encoder->codec == codec &&
+ !(encoder->muxers & mux))
+ {
+ codec = hb_audio_encoder_get_default(mux);
+ break;
+ }
+ }
+ if (codec == 0)
+ codec = hb_audio_encoder_get_default(mux);
+ return codec;
+}
+
+static void add_audio_for_lang(hb_value_array_t *list, hb_dict_t *preset,
+ hb_title_t *title, int mux, int copy_mask,
+ int fallback, const char *lang,
+ int behavior, int mode, hb_dict_t *track_dict)
+{
+ hb_value_array_t * encoder_list = hb_dict_get(preset, "AudioList");
+ int count = hb_value_array_len(encoder_list);
+ int track = find_audio_track(title, lang, 0);
+ int current_mode = 0;
+ while (track >= 0)
+ {
+ char key[8];
+ snprintf(key, sizeof(key), "%d", track);
+
+ count = current_mode ? 1 : count;
+ int ii;
+ for (ii = 0; ii < count; ii++)
+ {
+ // Check if this source track has already been added using these
+ // same encoder settings. If so, continue to next track.
+ hb_dict_t *used = source_audio_track_used(track_dict, ii);
+ if (hb_value_get_bool(hb_dict_get(used, key)))
+ continue;
+ hb_dict_set(used, key, hb_value_bool(1));
+
+ // Create new audio output track settings
+ hb_dict_t *audio_dict = hb_dict_init();
+ hb_value_t *acodec_value;
+ hb_dict_t *encoder_dict = hb_value_array_get(encoder_list, ii);
+ int out_codec;
+
+ acodec_value = hb_dict_get(encoder_dict, "AudioEncoder");
+ if (hb_value_type(acodec_value) == HB_VALUE_TYPE_STRING)
+ {
+ out_codec = hb_audio_encoder_get_from_name(
+ hb_value_get_string(acodec_value));
+ }
+ else
+ {
+ out_codec = hb_value_get_int(acodec_value);
+ }
+ // Save the encoder value before sanitizing. This value is
+ // useful to the frontends.
+ hb_dict_set(audio_dict, "PresetEncoder", hb_value_int(out_codec));
+
+ hb_audio_config_t *aconfig;
+ aconfig = hb_list_audio_config_item(title->list_audio, track);
+ out_codec = sanitize_audio_codec(aconfig->in.codec, out_codec,
+ copy_mask, fallback, mux);
+ hb_dict_set(audio_dict, "Track", hb_value_int(track));
+ hb_dict_set(audio_dict, "Encoder", hb_value_int(out_codec));
+ if (hb_dict_get(encoder_dict, "AudioTrackName") != NULL)
+ {
+ hb_dict_set(audio_dict, "Name", hb_value_dup(
+ hb_dict_get(encoder_dict, "AudioTrackName")));
+ }
+ if (!(out_codec & HB_ACODEC_PASS_FLAG))
+ {
+ if (hb_dict_get(encoder_dict, "AudioTrackGainSlider") != NULL)
+ {
+ hb_dict_set(audio_dict, "Gain", hb_value_dup(
+ hb_dict_get(encoder_dict, "AudioTrackGainSlider")));
+ }
+ if (hb_dict_get(encoder_dict, "AudioTrackDRCSlider") != NULL)
+ {
+ hb_dict_set(audio_dict, "DRC", hb_value_dup(
+ hb_dict_get(encoder_dict, "AudioTrackDRCSlider")));
+ }
+ if (hb_dict_get(encoder_dict, "AudioMixdown") != NULL)
+ {
+ hb_dict_set(audio_dict, "Mixdown", hb_value_dup(
+ hb_dict_get(encoder_dict, "AudioMixdown")));
+ }
+ if (hb_dict_get(encoder_dict, "AudioNormalizeMixLevel") != NULL)
+ {
+ hb_dict_set(audio_dict, "NormalizeMixLevel", hb_value_dup(
+ hb_dict_get(encoder_dict, "AudioNormalizeMixLevel")));
+ }
+ if (hb_dict_get(encoder_dict, "AudioDitherMethod") != NULL)
+ {
+ hb_dict_set(audio_dict, "DitherMethod", hb_value_dup(
+ hb_dict_get(encoder_dict, "AudioDitherMethod")));
+ }
+ if (hb_dict_get(encoder_dict, "AudioSamplerate") != NULL)
+ {
+ hb_dict_set(audio_dict, "Samplerate", hb_value_dup(
+ hb_dict_get(encoder_dict, "AudioSamplerate")));
+ }
+ if (hb_dict_get(encoder_dict, "AudioCompressionLevel") != NULL)
+ {
+ hb_dict_set(audio_dict, "CompressionLevel", hb_value_dup(
+ hb_dict_get(encoder_dict, "AudioCompressionLevel")));
+ }
+ if (hb_value_get_bool(hb_dict_get(encoder_dict,
+ "AudioTrackQualityEnable")))
+ {
+ hb_dict_set(audio_dict, "Quality", hb_value_xform(
+ hb_dict_get(encoder_dict, "AudioTrackQuality"),
+ HB_VALUE_TYPE_DOUBLE));
+ }
+ else
+ {
+ hb_dict_set(audio_dict, "Bitrate", hb_value_xform(
+ hb_dict_get(encoder_dict, "AudioBitrate"),
+ HB_VALUE_TYPE_INT));
+ }
+ }
+ hb_value_array_append(list, audio_dict);
+ }
+ if (behavior == 2)
+ track = find_audio_track(title, lang, track + 1);
+ else
+ break;
+ }
+}
+
+// This function assumes that Mux has already been initialized in
+// the job_dict
+int hb_preset_job_add_audio(hb_handle_t *h, int title_index,
+ hb_dict_t *preset, hb_dict_t *job_dict)
+{
+ hb_title_t *title = hb_find_title_by_index(h, title_index);
+ if (title == NULL)
+ {
+ // Can't create audio track list without knowing source audio tracks
+ hb_error("Invalid title index (%d)", title_index);
+ return -1;
+ }
+ if (hb_list_count(title->list_audio) <= 0)
+ {
+ // Source has no audio
+ return 0;
+ }
+
+ int mux = get_job_mux(job_dict);
+ if (mux == HB_MUX_INVALID)
+ {
+ return -1;
+ }
+
+ hb_dict_t *audio_dict = hb_dict_get(job_dict, "Audio");
+ int copy_mask = get_audio_copy_mask(preset);
+ if (copy_mask == HB_ACODEC_INVALID)
+ {
+ return -1;
+ }
+ int fallback = 0;
+ hb_dict_set(audio_dict, "CopyMask", hb_value_int(copy_mask));
+ hb_value_t *fallback_value = hb_dict_get(preset, "AudioEncoderFallback");
+ if (fallback_value != NULL)
+ {
+ hb_dict_set(audio_dict, "FallbackEncoder",
+ hb_value_dup(fallback_value));
+ if (hb_value_type(fallback_value) == HB_VALUE_TYPE_STRING)
+ {
+ const char * s = hb_value_get_string(fallback_value);
+ fallback = hb_audio_encoder_get_from_name(s);
+ if (fallback == 0)
+ {
+ hb_error("Invalid fallback audio codec (%s)", s);
+ return -1;
+ }
+ }
+ else
+ {
+ fallback = hb_value_get_int(fallback_value);
+ }
+ }
+ if (validate_audio_encoders(preset) < 0)
+ return -1;
+
+ hb_value_array_t *list = hb_dict_get(audio_dict, "AudioList");
+ if (list == NULL)
+ {
+ list = hb_value_array_init();
+ hb_dict_set(audio_dict, "AudioList", list);
+ }
+
+ int behavior = 1; // default first
+ const char *s;
+ s = hb_value_get_string(hb_dict_get(preset, "AudioTrackSelectionBehavior"));
+ if (s != NULL)
+ {
+ if (!strcasecmp(s, "none"))
+ return 0;
+ else if (!strcasecmp(s, "all"))
+ behavior = 2;
+ }
+
+ // Create hash that is used to track which tracks have been already added
+ // We do not want to add the same track with the same settings twice
+ hb_dict_t *track_dict = hb_dict_init();
+
+ // Add tracks for all languages in the language list
+ int mode;
+ hb_value_array_t *lang_list = hb_dict_get(preset, "AudioLanguageList");
+ mode = hb_value_get_bool(hb_dict_get(preset, "AudioSecondaryEncoderMode"));
+ int count = hb_value_array_len(lang_list);
+ int ii;
+ for (ii = 0; ii < count; ii++)
+ {
+ const char *lang;
+ lang = hb_value_get_string(hb_value_array_get(lang_list, ii));
+ add_audio_for_lang(list, preset, title, mux, copy_mask, fallback,
+ lang, behavior, mode, track_dict);
+ }
+ // If no audios found, try "und" language option
+ if (hb_value_array_len(list) <= 0)
+ {
+ add_audio_for_lang(list, preset, title, mux, copy_mask, fallback,
+ "und", behavior, mode, track_dict);
+ }
+ hb_dict_free(&track_dict);
+ return 0;
+}
+
+// Find a source audio track matching given language
+static int find_subtitle_track(const hb_title_t *title,
+ const char *lang, int start)
+{
+ hb_subtitle_t * subtitle;
+ int ii, count;
+
+ count = hb_list_count(title->list_subtitle);
+ for (ii = start; ii < count; ii++)
+ {
+ subtitle = hb_list_item(title->list_subtitle, ii);
+ if (!strcmp(lang, subtitle->iso639_2) || !strcmp(lang, "und"))
+ {
+ return ii;
+ }
+ }
+ return -1;
+}
+
+static void add_subtitle(hb_value_array_t *list, int track,
+ int make_default, int force, int burn)
+{
+ hb_dict_t *subtitle_dict = hb_dict_init();
+ hb_dict_set(subtitle_dict, "Track", hb_value_int(track));
+ hb_dict_set(subtitle_dict, "Default", hb_value_bool(make_default));
+ hb_dict_set(subtitle_dict, "Forced", hb_value_bool(force));
+ hb_dict_set(subtitle_dict, "Burn", hb_value_bool(burn));
+ hb_value_array_append(list, subtitle_dict);
+}
+
+typedef struct subtitle_behavior_s
+{
+ int one;
+ int burn_foreign;
+ int make_default;
+ int burn_first;
+ int burn_dvd;
+ int burn_bd;
+ int one_burned;
+ uint8_t *used;
+} subtitle_behavior_t;
+
+static void add_subtitle_for_lang(hb_value_array_t *list, hb_title_t *title,
+ int mux, const char *lang,
+ subtitle_behavior_t *behavior)
+{
+ int t;
+ t = find_subtitle_track(title, lang, 0);
+ for (t = find_subtitle_track(title, lang, 0);
+ t >= 0;
+ t = behavior->one ? -1 : find_subtitle_track(title, lang, t + 1))
+ {
+ if (behavior->used[t])
+ {
+ if (behavior->one)
+ break;
+ continue;
+ }
+ int burn, make_default;
+ hb_subtitle_t *subtitle;
+ subtitle = hb_list_item(title->list_subtitle, t);
+ burn = !behavior->one_burned &&
+ ((subtitle->source == VOBSUB && behavior->burn_dvd) ||
+ (subtitle->source == PGSSUB && behavior->burn_bd) ||
+ !hb_subtitle_can_pass(subtitle->source, mux) ||
+ behavior->burn_first || behavior->burn_foreign);
+ make_default = !burn && behavior->make_default;
+ behavior->burn_first &= !burn;
+ behavior->one_burned |= burn;
+ behavior->used[t] = 1;
+ add_subtitle(list, t, make_default, 0 /*!force*/, burn);
+ }
+}
+
+// This function assumes that the AudioList and Mux have already been
+// initialized in the job_dict
+int hb_preset_job_add_subtitles(hb_handle_t *h, int title_index,
+ hb_dict_t *preset, hb_dict_t *job_dict)
+{
+ hb_title_t *title = hb_find_title_by_index(h, title_index);
+ if (title == NULL)
+ {
+ // Can't create subtitle track list without knowing source
+ hb_error("Invalid title index (%d)", title_index);
+ return -1;
+ }
+
+ int mux = get_job_mux(job_dict);
+ if (mux == HB_MUX_INVALID)
+ {
+ return -1;
+ }
+
+ // Get the language of the first audio output track
+ // Needed for subtitle track selection
+ hb_dict_t *audio_dict = hb_dict_get(job_dict, "Audio");
+ hb_value_array_t *audio_list = hb_dict_get(audio_dict, "AudioList");
+ const char *first_audio_lang = NULL;
+ if (hb_value_array_len(audio_list) > 0)
+ {
+ int track;
+ hb_value_t *audio = hb_value_array_get(audio_list, 0);
+ track = hb_value_get_int(hb_dict_get(audio, "Track"));
+ if (hb_list_count(title->list_audio) > track)
+ {
+ hb_audio_config_t *aconfig;
+ aconfig = hb_list_audio_config_item(title->list_audio, track);
+ if (aconfig != NULL)
+ first_audio_lang = aconfig->lang.iso639_2;
+ }
+ }
+
+ int source_subtitle_count = hb_list_count(title->list_subtitle);
+ if (source_subtitle_count == 0)
+ return 0;
+
+ hb_dict_t *subtitle_dict = hb_dict_get(job_dict, "Subtitle");
+ hb_value_array_t *list = hb_dict_get(subtitle_dict, "SubtitleList");
+ if (list == NULL)
+ {
+ list = hb_value_array_init();
+ hb_dict_set(subtitle_dict, "SubtitleList", list);
+ }
+
+ int track_behavior = 0; // default no subtitles
+ int burn_behavior = 0;
+ int burn_foreign;
+
+ struct subtitle_behavior_s behavior;
+ behavior.one = 0;
+ behavior.burn_foreign = 0;
+ behavior.make_default = 0;
+ behavior.burn_first = 0;
+ behavior.burn_dvd = 0;
+ behavior.burn_bd = 0;
+ behavior.one_burned = 0;
+ // Create array that is used to track which tracks have been already added
+ // We do not want to add the same track with the same settings twice
+ behavior.used = calloc(source_subtitle_count, sizeof(*behavior.used));
+
+ const char *s;
+ s = hb_value_get_string(hb_dict_get(preset,
+ "SubtitleTrackSelectionBehavior"));
+ if (s != NULL)
+ {
+ if (!strcasecmp(s, "first"))
+ track_behavior = 1;
+ else if (!strcasecmp(s, "all"))
+ track_behavior = 2;
+ }
+
+ s = hb_value_get_string(hb_dict_get(preset, "SubtitleBurnBehavior"));
+ if (s != NULL)
+ {
+ if (!strcasecmp(s, "foreign"))
+ burn_behavior = 1;
+ else if (!strcasecmp(s, "first"))
+ burn_behavior = 2;
+ else if (!strcasecmp(s, "foreign_first"))
+ burn_behavior = 3;
+ }
+
+ behavior.burn_dvd = hb_value_get_int(hb_dict_get(preset,
+ "SubtitleBurnDVDSub"));
+ behavior.burn_bd = hb_value_get_int(hb_dict_get(preset,
+ "SubtitleBurnBDSub"));
+
+ burn_foreign = burn_behavior == 1 || burn_behavior == 3;
+ behavior.burn_first = burn_behavior == 2 || burn_behavior == 3;
+
+ int foreign_audio_search, foreign_first_audio;
+ foreign_audio_search = hb_value_get_bool(hb_dict_get(preset,
+ "SubtitleAddForeignAudioSearch"));
+ foreign_first_audio = hb_value_get_bool(hb_dict_get(preset,
+ "SubtitleAddForeignAudioSubtitle"));
+
+
+ // Add tracks for all languages in the language list
+ hb_value_array_t *lang_list = hb_dict_get(preset, "SubtitleLanguageList");
+ int count = hb_value_array_len(lang_list);
+ const char *pref_lang = "und";
+ if (count > 0)
+ {
+ pref_lang = hb_value_get_string(hb_value_array_get(lang_list, 0));
+ }
+ if (!strcmp(pref_lang, "und"))
+ {
+ foreign_audio_search = foreign_first_audio = 0;
+ }
+
+ int track;
+ if (first_audio_lang != NULL &&
+ foreign_first_audio && strncmp(first_audio_lang, pref_lang, 4))
+ {
+ // First audio lang does not match the preferred subittle lang.
+ // Preset says to add pref lang subtitle.
+ // Foreign audio search is not necessary since entire audio track
+ // is foreign.
+ foreign_audio_search = 0;
+ behavior.one = 1;
+ behavior.burn_foreign = burn_foreign;
+ behavior.make_default = 1;
+ add_subtitle_for_lang(list, title, mux, pref_lang, &behavior);
+ }
+
+ hb_dict_t *search_dict = hb_dict_get(subtitle_dict, "Search");
+ if (search_dict == NULL)
+ {
+ search_dict = hb_dict_init();
+ hb_dict_set(subtitle_dict, "Search", search_dict);
+ }
+ if (first_audio_lang != NULL &&
+ foreign_audio_search && !strncmp(first_audio_lang, pref_lang, 4))
+ {
+ // First audio lang matches the preferred subittle lang.
+ // Preset says to add search for foreign audio subtitles.
+ int burn = burn_foreign || behavior.burn_first;
+ // If not burning, make this the default track.
+ hb_dict_set(search_dict, "Enable", hb_value_bool(1));
+ hb_dict_set(search_dict, "Default", hb_value_bool(!burn));
+ hb_dict_set(search_dict, "Forced", hb_value_bool(1));
+ hb_dict_set(search_dict, "Burn", hb_value_bool(burn));
+ }
+ else
+ {
+ hb_dict_set(search_dict, "Enable", hb_value_bool(0));
+ }
+
+ if (track_behavior > 0)
+ {
+ int ii;
+ behavior.one = track_behavior == 1;
+ behavior.burn_foreign = 0;
+ behavior.make_default = 0;
+ for (ii = 0; ii < count; ii++)
+ {
+ const char *lang;
+ lang = hb_value_get_string(hb_value_array_get(lang_list, ii));
+ add_subtitle_for_lang(list, title, mux, lang, &behavior);
+ }
+ if (count <= 0)
+ {
+ add_subtitle_for_lang(list, title, mux, "und", &behavior);
+ }
+ }
+
+ if (hb_value_get_bool(hb_dict_get(preset, "SubtitleAddCC")))
+ {
+ // Add Closed Caption track
+ for (track = 0; track < source_subtitle_count; track++)
+ {
+ if (behavior.used[track])
+ {
+ continue;
+ }
+ hb_subtitle_t *subtitle = hb_list_item(title->list_subtitle, track);
+ if (subtitle->source == CC608SUB || subtitle->source == CC708SUB)
+ {
+ int burn;
+ burn = !behavior.one_burned &&
+ (!hb_subtitle_can_pass(subtitle->source, mux) ||
+ behavior.burn_first);
+ behavior.used[track] = 1;
+ behavior.one_burned |= burn;
+ add_subtitle(list, track, 0 /*default*/, 0 /*!force*/, burn);
+ break;
+ }
+ }
+ }
+ free(behavior.used);
+
+ return 0;
+}
+
+static int get_video_framerate(hb_value_t *rate_value)
+{
+ int rate = 0;
+ if (hb_value_type(rate_value) != HB_VALUE_TYPE_STRING)
+ {
+ double d;
+ d = hb_value_get_double(rate_value);
+ if (d != 0 && d <= 600)
+ {
+ // Assume the value is an actual framerate and compute
+ // 27Mhz based denominator
+ rate = (int)(27000000 / d);
+ }
+ else
+ {
+ // Assume the value is a 27Mhz based denominator
+ rate = (int)d;
+ }
+ }
+ else
+ {
+ const char *rate_name = hb_value_get_string(rate_value);
+ if (strcasecmp(rate_name, "source") &&
+ strcasecmp(rate_name, "auto") &&
+ strcasecmp(rate_name, "same as source"))
+ {
+ rate = hb_video_framerate_get_from_name(rate_name);
+ if (rate < 0)
+ {
+ // No matching rate found. Error out.
+ rate = -1;
+ }
+ }
+ }
+ return rate;
+}
+
+/**
+ * Initialize an hb_job_t and return a hb_dict_t representation of the job.
+ * This dict will have key/value pairs compatible with json jobs.
+ * @param h - Pointer to hb_handle_t instance that contains the
+ * specified title_index
+ * @param title_index - Index of hb_title_t to use for job initialization.
+ * Index comes from title->index or "Index" key
+ * in json representation of a title.
+ * @param preset - Preset to initialize job with
+ */
+hb_dict_t* hb_preset_job_init(hb_handle_t *h, int title_index, hb_dict_t *preset)
+{
+ hb_title_t *title = hb_find_title_by_index(h, title_index);
+ if (title == NULL)
+ {
+ hb_error("Invalid title index (%d)", title_index);
+ return NULL;
+ }
+
+ hb_job_t *job = hb_job_init(title);
+ hb_dict_t *job_dict = hb_job_to_dict(job);
+ hb_job_close(&job);
+
+ // Now apply preset settings to the job dict
+
+ hb_value_t *mux_value = hb_dict_get(preset, "FileFormat");
+ int mux;
+ if (hb_value_type(mux_value) == HB_VALUE_TYPE_STRING)
+ {
+ mux = hb_container_get_from_name(hb_value_get_string(mux_value));
+ if (mux == 0)
+ mux = hb_container_get_from_extension(
+ hb_value_get_string(mux_value));
+ }
+ else
+ {
+ mux = hb_value_get_int(mux_value);
+ }
+ hb_container_t *container = hb_container_get_from_format(mux);
+ if (container == NULL)
+ {
+ char *str = hb_value_get_string_xform(mux_value);
+ hb_error("Invalid container (%s)", str);
+ free(str);
+ goto fail;
+ }
+
+ hb_value_t *vcodec_value = hb_dict_get(preset, "VideoEncoder");
+ int vcodec;
+ if (hb_value_type(vcodec_value) == HB_VALUE_TYPE_STRING)
+ {
+ vcodec = hb_video_encoder_get_from_name(
+ hb_value_get_string(vcodec_value));
+ }
+ else
+ {
+ vcodec = hb_value_get_int(vcodec_value);
+ }
+ hb_encoder_t *encoder = hb_video_encoder_get_from_codec(vcodec);
+ if (encoder == NULL)
+ {
+ char *str = hb_value_get_string_xform(vcodec_value);
+ hb_error("Invalid video encoder (%s)", str);
+ free(str);
+ goto fail;
+ }
+ if (!(encoder->muxers & mux))
+ {
+ hb_error("Incompatible video encoder (%s) for muxer (%s)",
+ hb_video_encoder_get_name(vcodec),
+ hb_container_get_name(mux));
+ goto fail;
+ }
+
+ int chapters;
+ chapters = hb_value_get_bool(hb_dict_get(preset, "ChapterMarkers"));
+ if (hb_list_count(title->list_chapter) <= 1)
+ chapters = 0;
+
+ // Set "Destination" settings in job
+ hb_dict_t *dest_dict = hb_dict_get(job_dict, "Destination");
+ hb_dict_set(dest_dict, "ChapterMarkers", hb_value_bool(chapters));
+ hb_dict_set(dest_dict, "Mux", hb_value_dup(mux_value));
+ if (mux == HB_MUX_MASK_MP4)
+ {
+ hb_dict_t *mp4_dict = hb_dict_init();
+ hb_dict_set(mp4_dict, "Mp4Optimize",
+ hb_value_xform(hb_dict_get(preset, "Mp4HttpOptimize"),
+ HB_VALUE_TYPE_BOOL));
+ if (vcodec == HB_VCODEC_X264)
+ {
+ hb_dict_set(mp4_dict, "IpodAtom",
+ hb_value_xform(hb_dict_get(preset, "Mp4iPodCompatible"),
+ HB_VALUE_TYPE_BOOL));
+ }
+ hb_dict_set(dest_dict, "Mp4Options", mp4_dict);
+ }
+ dest_dict = NULL;
+
+ // Calculate default job geometry settings
+ hb_geometry_t srcGeo, resultGeo;
+ hb_geometry_settings_t geo;
+ int keep_aspect;
+
+ srcGeo = title->geometry;
+ if (!hb_value_get_bool(hb_dict_get(preset, "PictureAutoCrop")))
+ {
+ geo.crop[0] = hb_value_get_int(hb_dict_get(preset, "PictureTopCrop"));
+ geo.crop[1] = hb_value_get_int(hb_dict_get(preset, "PictureBottomCrop"));
+ geo.crop[2] = hb_value_get_int(hb_dict_get(preset, "PictureLeftCrop"));
+ geo.crop[3] = hb_value_get_int(hb_dict_get(preset, "PictureRightCrop"));
+ }
+ else
+ {
+ memcpy(geo.crop, title->crop, sizeof(geo.crop));
+ }
+ geo.modulus = hb_value_get_int(hb_dict_get(preset, "PictureModulus"));
+ if (geo.modulus < 2)
+ geo.modulus = 2;
+ if (hb_value_get_bool(hb_dict_get(preset, "PictureLooseCrop")))
+ {
+ // Crop a few extra pixels to avoid scaling to fit Modulus
+ int extra1, extra2, crop_width, crop_height, width, height;
+
+ crop_width = srcGeo.width - geo.crop[2] - geo.crop[3];
+ crop_height = srcGeo.height - geo.crop[0] - geo.crop[1];
+ width = MULTIPLE_MOD_DOWN(crop_width, geo.modulus);
+ height = MULTIPLE_MOD_DOWN(crop_height, geo.modulus);
+
+ extra1 = EVEN((crop_height - height) / 2);
+ extra2 = crop_height - height - extra1;
+ geo.crop[0] += extra1;
+ geo.crop[1] += extra2;
+ extra1 = EVEN((crop_width - width) / 2);
+ extra2 = crop_width - width - extra1;
+ geo.crop[2] += extra1;
+ geo.crop[3] += extra2;
+ }
+ hb_value_t *ana_mode_value = hb_dict_get(preset, "PicturePAR");
+ if (hb_value_type(ana_mode_value) == HB_VALUE_TYPE_STRING)
+ {
+ const char *s = hb_value_get_string(ana_mode_value);
+ if (!strcasecmp(s, "none"))
+ geo.mode = 0;
+ else if (!strcasecmp(s, "strict"))
+ geo.mode = 1;
+ else if (!strcasecmp(s, "custom"))
+ geo.mode = 3;
+ else // default loose
+ geo.mode = 2;
+ }
+ else
+ {
+ geo.mode = hb_value_get_int(hb_dict_get(preset, "PicturePAR"));
+ }
+ keep_aspect = hb_value_get_bool(hb_dict_get(preset, "PictureKeepRatio"));
+ if (geo.mode == HB_ANAMORPHIC_STRICT || geo.mode == HB_ANAMORPHIC_LOOSE)
+ keep_aspect = 1;
+ geo.keep = keep_aspect * HB_KEEP_DISPLAY_ASPECT;
+ geo.itu_par = hb_value_get_bool(hb_dict_get(preset, "PictureItuPAR"));
+ geo.maxWidth = hb_value_get_int(hb_dict_get(preset, "PictureWidth"));
+ geo.maxHeight = hb_value_get_int(hb_dict_get(preset, "PictureHeight"));
+ geo.geometry = title->geometry;
+ int width = hb_value_get_int(hb_dict_get(preset, "PictureForceWidth"));
+ int height = hb_value_get_int(hb_dict_get(preset, "PictureForceHeight"));
+ if (width > 0)
+ geo.geometry.width = width;
+ else
+ geo.geometry.width -= geo.crop[2] + geo.crop[3];
+ if (height > 0)
+ geo.geometry.height = height;
+ else
+ geo.geometry.height -= geo.crop[0] + geo.crop[1];
+ if (geo.mode == HB_ANAMORPHIC_CUSTOM && !keep_aspect)
+ {
+ int dar_width;
+ dar_width = hb_value_get_int(hb_dict_get(preset, "PictureDARWidth"));
+ if (dar_width > 0)
+ {
+ geo.geometry.par.num = dar_width;
+ geo.geometry.par.num = geo.geometry.width;
+ }
+ else
+ {
+ geo.geometry.par.num =
+ hb_value_get_int(hb_dict_get(preset, "PicturePARWidth"));
+ geo.geometry.par.num =
+ hb_value_get_int(hb_dict_get(preset, "PicturePARHeight"));
+ }
+ }
+ hb_set_anamorphic_size2(&srcGeo, &geo, &resultGeo);
+ hb_dict_t *par_dict = hb_dict_get(job_dict, "PAR");
+ hb_dict_set(par_dict, "Num", hb_value_int(resultGeo.par.num));
+ hb_dict_set(par_dict, "Den", hb_value_int(resultGeo.par.den));
+ par_dict = NULL;
+
+ // Filters
+ hb_dict_t *filters_dict = hb_dict_get(job_dict, "Filters");
+ if (filters_dict == NULL)
+ {
+ filters_dict = hb_dict_init();
+ hb_dict_set(job_dict, "Filters", filters_dict);
+ }
+ hb_dict_set(filters_dict, "Grayscale", hb_value_xform(
+ hb_dict_get(preset, "VideoGrayScale"), HB_VALUE_TYPE_BOOL));
+ hb_value_array_t *filter_list = hb_dict_get(filters_dict, "FilterList");
+ if (filter_list == NULL)
+ {
+ filter_list = hb_value_array_init();
+ hb_dict_set(filters_dict, "FilterList", filter_list);
+ }
+
+ hb_dict_t *filter_dict;
+ char *filter_str;
+
+ // Setup scale filter
+ filter_str = hb_strdup_printf("%d:%d:%d:%d:%d:%d",
+ resultGeo.width, resultGeo.height,
+ geo.crop[0], geo.crop[1],
+ geo.crop[2], geo.crop[3]);
+
+ filter_dict = hb_dict_init();
+ hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_CROP_SCALE));
+ hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
+ free(filter_str);
+ hb_value_array_append(filter_list, filter_dict);
+
+ // Detelecine filter
+ hb_value_t *detel_val = hb_dict_get(preset, "PictureDetelecine");
+ if (detel_val != NULL)
+ {
+ const char *custom;
+ custom = hb_value_get_string(hb_dict_get(preset,
+ "PictureDetelecineCustom"));
+ if (hb_value_type(detel_val) == HB_VALUE_TYPE_STRING)
+ {
+ filter_str = hb_generate_filter_settings(
+ HB_FILTER_DETELECINE, hb_value_get_string(detel_val), custom);
+ }
+ else
+ {
+ filter_str = hb_generate_filter_settings_by_index(
+ HB_FILTER_DETELECINE, hb_value_get_int(detel_val), custom);
+ }
+ if (filter_str == NULL)
+ {
+ char *s = hb_value_get_string_xform(detel_val);
+ hb_error("Invalid detelecine filter settings (%s)", s);
+ free(s);
+ goto fail;
+ }
+ else if (filter_str != hb_filter_off)
+ {
+ filter_dict = hb_dict_init();
+ hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_DETELECINE));
+ hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
+ hb_value_array_append(filter_list, filter_dict);
+ free(filter_str);
+ }
+ }
+
+ // Decomb or deinterlace filters
+ int decomb_or_deint;
+ decomb_or_deint = hb_value_get_bool(hb_dict_get(preset,
+ "PictureDecombDeinterlace"));
+ hb_value_t *decomb_val = hb_dict_get(preset, "PictureDecomb");
+ if (decomb_or_deint && decomb_val != NULL)
+ {
+ const char *custom;
+ custom = hb_value_get_string(hb_dict_get(preset,
+ "PictureDecombCustom"));
+ if (hb_value_type(decomb_val) == HB_VALUE_TYPE_STRING)
+ {
+ filter_str = hb_generate_filter_settings(
+ HB_FILTER_DECOMB, hb_value_get_string(decomb_val), custom);
+ }
+ else
+ {
+ filter_str = hb_generate_filter_settings_by_index(
+ HB_FILTER_DECOMB, hb_value_get_int(decomb_val), custom);
+ }
+ if (filter_str == NULL)
+ {
+ char *s = hb_value_get_string_xform(decomb_val);
+ hb_error("Invalid decomb filter settings (%s)", s);
+ free(s);
+ goto fail;
+ }
+ else if (filter_str != hb_filter_off)
+ {
+ filter_dict = hb_dict_init();
+ hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_DECOMB));
+ hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
+ hb_value_array_append(filter_list, filter_dict);
+ free(filter_str);
+ }
+ }
+
+ hb_value_t *deint_val = hb_dict_get(preset, "PictureDeinterlace");
+ if (!decomb_or_deint && deint_val != NULL)
+ {
+ const char *custom;
+ custom = hb_value_get_string(hb_dict_get(preset,
+ "PictureDeinterlaceCustom"));
+ if (hb_value_type(deint_val) == HB_VALUE_TYPE_STRING)
+ {
+ filter_str = hb_generate_filter_settings(
+ HB_FILTER_DEINTERLACE, hb_value_get_string(deint_val), custom);
+ }
+ else
+ {
+ filter_str = hb_generate_filter_settings_by_index(
+ HB_FILTER_DEINTERLACE, hb_value_get_int(deint_val), custom);
+ }
+ if (filter_str == NULL)
+ {
+ char *s = hb_value_get_string_xform(deint_val);
+ hb_error("Invalid deinterlace filter settings (%s)", s);
+ free(s);
+ goto fail;
+ }
+ else if (filter_str != hb_filter_off)
+ {
+ filter_dict = hb_dict_init();
+ hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_DECOMB));
+ hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
+ hb_value_array_append(filter_list, filter_dict);
+ free(filter_str);
+ }
+ }
+
+ // Denoise filter
+ int denoise;
+ hb_value_t *denoise_value = hb_dict_get(preset, "PictureDenoiseFilter");
+ denoise = hb_value_type(denoise_value) == HB_VALUE_TYPE_STRING ? (
+ !strcasecmp(hb_value_get_string(denoise_value), "off") ? 0 :
+ !strcasecmp(hb_value_get_string(denoise_value), "nlmeans") ? 1 : 2) :
+ hb_value_get_int(denoise_value);
+
+ if (denoise != 0)
+ {
+ int filter_id = denoise == 1 ? HB_FILTER_NLMEANS : HB_FILTER_HQDN3D;
+ const char *denoise_preset, *denoise_tune;
+ denoise_preset = hb_value_get_string(
+ hb_dict_get(preset, "PictureDenoisePreset"));
+ if (denoise_preset != NULL)
+ {
+ if (strcasecmp(denoise_preset, "custom"))
+ denoise_tune = hb_value_get_string(
+ hb_dict_get(preset, "PictureDenoiseTune"));
+ else
+ denoise_tune = hb_value_get_string(
+ hb_dict_get(preset, "PictureDeinterlaceCustom"));
+
+ filter_str = hb_generate_filter_settings(
+ filter_id, denoise_preset, denoise_tune);
+ if (filter_str == NULL)
+ {
+ hb_error("Invalid denoise filter settings (%s%s%s)",
+ denoise_preset,
+ denoise_tune ? "," : "",
+ denoise_tune ? denoise_tune : "");
+ goto fail;
+ }
+ else if (filter_str != hb_filter_off)
+ {
+ filter_dict = hb_dict_init();
+ hb_dict_set(filter_dict, "ID", hb_value_int(filter_id));
+ hb_dict_set(filter_dict, "Settings",
+ hb_value_string(filter_str));
+ hb_value_array_append(filter_list, filter_dict);
+ free(filter_str);
+ }
+ }
+ }
+
+ // Deblock filter
+ char *deblock = hb_value_get_string_xform(
+ hb_dict_get(preset, "PictureDeblock"));
+ if (deblock != NULL)
+ {
+ filter_str = hb_generate_filter_settings(HB_FILTER_DEBLOCK,
+ deblock, NULL);
+ if (filter_str == NULL)
+ {
+ hb_error("Invalid deblock filter settings (%s)", deblock);
+ goto fail;
+ }
+ else if (filter_str != hb_filter_off)
+ {
+ filter_dict = hb_dict_init();
+ hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_DEBLOCK));
+ hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
+ hb_value_array_append(filter_list, filter_dict);
+ free(filter_str);
+ }
+ }
+ free(deblock);
+
+ // Rotate filter
+ char *rotate = hb_value_get_string_xform(
+ hb_dict_get(preset, "PictureRotate"));
+ if (rotate != NULL)
+ {
+ filter_str = hb_generate_filter_settings(HB_FILTER_ROTATE,
+ rotate, NULL);
+ if (filter_str == NULL)
+ {
+ hb_error("Invalid rotate filter settings (%s)", rotate);
+ goto fail;
+ }
+ else if (filter_str != hb_filter_off)
+ {
+ filter_dict = hb_dict_init();
+ hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_ROTATE));
+ hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
+ hb_value_array_append(filter_list, filter_dict);
+ free(filter_str);
+ }
+ }
+ free(rotate);
+
+ hb_value_t *fr_value = hb_dict_get(preset, "VideoFramerate");
+ int vrate_den = get_video_framerate(fr_value);
+ if (vrate_den < 0)
+ {
+ char *str = hb_value_get_string_xform(fr_value);
+ hb_error("Invalid video framerate (%s)", str);
+ free(str);
+ goto fail;
+ }
+
+ int fr_mode;
+ hb_value_t *fr_mode_value = hb_dict_get(preset, "VideoFramerateMode");
+ fr_mode = hb_value_type(fr_mode_value) == HB_VALUE_TYPE_STRING ? (
+ !strcasecmp(hb_value_get_string(fr_mode_value), "cfr") ? 1 :
+ !strcasecmp(hb_value_get_string(fr_mode_value), "pfr") ? 2 : 0) :
+ hb_value_get_int(fr_mode_value);
+
+ if (hb_value_get_bool(hb_dict_get(preset, "x264ZeroLatency")))
+ fr_mode = 1;
+
+ if (vrate_den == 0)
+ filter_str = hb_strdup_printf("%d", fr_mode);
+ else
+ filter_str = hb_strdup_printf("%d:%d:%d", fr_mode, 27000000, vrate_den);
+
+ filter_dict = hb_dict_init();
+ hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_VFR));
+ hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
+ hb_value_array_append(filter_list, filter_dict);
+ free(filter_str);
+
+ // Video encoder settings
+ hb_dict_t *video_dict = hb_dict_get(job_dict, "Video");
+ hb_value_t *color_value;
+ if ((color_value = hb_dict_get(preset, "VideoColorMatrixCode")) != NULL)
+ hb_dict_set(video_dict, "ColorMatrixCode", hb_value_dup(color_value));
+ hb_dict_set(video_dict, "Encoder", hb_value_dup(vcodec_value));
+ switch (vcodec)
+ {
+ case HB_VCODEC_X264:
+ {
+ if (hb_value_get_bool(
+ hb_dict_get(preset, "x264UseAdvancedOptions")))
+ {
+ hb_dict_set(video_dict, "Options",
+ hb_value_dup(hb_dict_get(preset, "x264Option")));
+ break;
+ }
+ }
+ // Falling through to next case...
+
+ case HB_VCODEC_X265:
+ {
+ hb_value_t *value, *array;
+ if ((value = hb_dict_get(preset, "VideoPreset")) != NULL)
+ hb_dict_set(video_dict, "Preset", hb_value_dup(value));
+ if ((value = hb_dict_get(preset, "VideoProfile")) != NULL)
+ hb_dict_set(video_dict, "Profile", hb_value_dup(value));
+ if ((value = hb_dict_get(preset, "VideoLevel")) != NULL)
+ hb_dict_set(video_dict, "Level", hb_value_dup(value));
+ if ((value = hb_dict_get(preset, "VideoOptionExtra")) != NULL)
+ hb_dict_set(video_dict, "Options", hb_value_dup(value));
+ array = hb_value_array_init();
+ if ((value = hb_dict_get(preset, "VideoTune")) != NULL)
+ hb_value_array_append(array, hb_value_dup(value));
+ if (vcodec == HB_VCODEC_X264)
+ {
+ if (hb_value_get_bool(hb_dict_get(preset, "x264FastDecode")))
+ hb_value_array_append(array, hb_value_string("fastdecode"));
+ if (hb_value_get_bool(hb_dict_get(preset, "x264ZeroLatency")))
+ hb_value_array_append(array, hb_value_string("zerolatency"));
+ }
+ if (hb_value_array_len(array) > 0)
+ hb_dict_set(video_dict, "Tune",
+ hb_value_xform(array, HB_VALUE_TYPE_STRING));
+ hb_value_decref(array);
+ } break;
+
+ case HB_VCODEC_FFMPEG_MPEG2:
+ case HB_VCODEC_FFMPEG_MPEG4:
+ case HB_VCODEC_FFMPEG_VP8:
+ {
+ hb_value_t *value;
+ if ((value = hb_dict_get(preset, "VideoOptionExtra")) != NULL)
+ hb_dict_set(video_dict, "Options", hb_value_dup(value));
+ } break;
+
+ case HB_VCODEC_THEORA:
+ default:
+ {
+ } break;
+ }
+ int vqtype = hb_value_get_int(hb_dict_get(preset, "VideoQualityType"));
+ if (vqtype == 2) // Constant quality
+ {
+ hb_dict_set(video_dict, "Quality",
+ hb_value_xform(hb_dict_get(preset, "VideoQualitySlider"),
+ HB_VALUE_TYPE_DOUBLE));
+ }
+ else if (vqtype == 1) // ABR
+ {
+ hb_dict_set(video_dict, "Bitrate",
+ hb_value_xform(hb_dict_get(preset, "VideoAvgBitrate"),
+ HB_VALUE_TYPE_INT));
+ hb_dict_set(video_dict, "TwoPass",
+ hb_value_xform(hb_dict_get(preset, "VideoTwoPass"),
+ HB_VALUE_TYPE_BOOL));
+ hb_dict_set(video_dict, "Turbo",
+ hb_value_xform(hb_dict_get(preset, "VideoTurboTwoPass"),
+ HB_VALUE_TYPE_BOOL));
+ }
+ else
+ {
+ hb_value_t *value = hb_dict_get(preset, "VideoQualitySlider");
+ if (value != NULL && hb_value_get_double(value) >= 0)
+ hb_dict_set(video_dict, "Quality",
+ hb_value_xform(value, HB_VALUE_TYPE_DOUBLE));
+ else
+ {
+ hb_dict_set(video_dict, "Bitrate",
+ hb_value_xform(hb_dict_get(preset, "VideoAvgBitrate"),
+ HB_VALUE_TYPE_INT));
+ hb_dict_set(video_dict, "TwoPass",
+ hb_value_xform(hb_dict_get(preset, "VideoTwoPass"),
+ HB_VALUE_TYPE_BOOL));
+ hb_dict_set(video_dict, "Turbo",
+ hb_value_xform(hb_dict_get(preset, "VideoTurboTwoPass"),
+ HB_VALUE_TYPE_BOOL));
+ }
+ }
+ hb_dict_t *qsv = hb_dict_get(video_dict, "QSV");
+ if (qsv == NULL)
+ {
+ qsv = hb_dict_init();
+ hb_dict_set(video_dict, "QSV", qsv);
+ }
+ hb_value_t *value;
+ if ((value = hb_dict_get(preset, "VideoQSVDecode")) != NULL)
+ {
+ hb_dict_set(qsv, "Decode",
+ hb_value_xform(value, HB_VALUE_TYPE_BOOL));
+ }
+ if ((value = hb_dict_get(preset, "VideoQSVAsyncDepth")) != NULL)
+ {
+ hb_dict_set(qsv, "AsyncDepth",
+ hb_value_xform(value, HB_VALUE_TYPE_INT));
+ }
+
+ if ((value = hb_dict_get(preset, "VideoScaler")) != NULL)
+ {
+ const char *s = hb_value_get_string(value);
+ if (strcasecmp(s, "opencl"))
+ {
+ hb_dict_set(video_dict, "OpenCL", hb_value_bool(1));
+ }
+ }
+ if ((value = hb_dict_get(preset, "VideoHWDecode")) != NULL)
+ {
+ hb_dict_set(video_dict, "HWDecode",
+ hb_value_xform(value, HB_VALUE_TYPE_BOOL));
+ }
+ video_dict = NULL;
+
+ // Audio settings
+ if (hb_preset_job_add_audio(h, title_index, preset, job_dict) != 0)
+ {
+ goto fail;
+ }
+
+ // Subtitle settings
+ if (hb_preset_job_add_subtitles(h, title_index, preset, job_dict) != 0)
+ {
+ goto fail;
+ }
+
+ return job_dict;
+
+fail:
+ hb_dict_free(&job_dict);
+ return NULL;
+}
+
+// Clean a dictionary of unwanted keys
+// Used to make sure only valid keys are in output presets
+static void
+dict_clean(hb_value_t *dict, hb_value_t *template)
+{
+ hb_value_t *tmp = hb_value_dup(dict);
+ hb_dict_iter_t iter;
+ const char *key;
+ hb_value_t *val;
+ hb_value_t *template_val;
+ hb_value_type_t template_type, val_type;
+ const char *preset_name = NULL;
+
+ val = hb_dict_get(dict, "PresetName");
+ if (val != NULL)
+ preset_name = hb_value_get_string(val);
+
+ for (iter = hb_dict_iter_init(tmp);
+ iter != HB_DICT_ITER_DONE;
+ iter = hb_dict_iter_next(tmp, iter))
+ {
+ key = hb_dict_iter_key(iter);
+ val = hb_dict_iter_value(iter);
+ val_type = hb_value_type(val);
+
+ template_val = hb_dict_get(template, key);
+ template_type = hb_value_type(template_val);
+ if (template_val == NULL)
+ {
+ // Unknown key. These can be keys used privately by the
+ // frontend. So don't make noise about them.
+ hb_dict_remove(dict, key);
+ }
+ else if (val_type != template_type)
+ {
+ if (val_type == HB_VALUE_TYPE_DICT ||
+ val_type == HB_VALUE_TYPE_ARRAY ||
+ template_type == HB_VALUE_TYPE_DICT ||
+ template_type == HB_VALUE_TYPE_ARRAY)
+ {
+ hb_error("Preset %s: Incompatible value types for key %s. "
+ "Dropping.", preset_name, key);
+ hb_dict_remove(dict, key);
+ }
+ else if (hb_value_is_number(val) &&
+ hb_value_is_number(template_val))
+ {
+ // Silently convert compatible numbers
+ hb_value_t *v;
+ v = hb_value_xform(val, template_type);
+ hb_dict_set(dict, key, v);
+ }
+ else
+ {
+ hb_value_t *v;
+ hb_error("Preset %s: Incorrect value type for key %s. "
+ "Converting.", preset_name, key);
+ v = hb_value_xform(val, template_type);
+ hb_dict_set(dict, key, v);
+ }
+ }
+ else if (val_type == HB_VALUE_TYPE_DICT &&
+ template_type == HB_VALUE_TYPE_DICT)
+ {
+ val = hb_dict_get(dict, key);
+ dict_clean(val, template_val);
+ }
+ else if (val_type == HB_VALUE_TYPE_ARRAY &&
+ template_type == HB_VALUE_TYPE_ARRAY &&
+ hb_value_array_len(template_val) > 0)
+ {
+ template_val = hb_value_array_get(template_val, 0);
+ if (hb_value_type(template_val) == HB_VALUE_TYPE_DICT)
+ {
+ val = hb_dict_get(dict, key);
+ int count = hb_value_array_len(val);
+ int ii;
+ for (ii = 0; ii < count; ii++)
+ {
+ hb_value_t *array_val;
+ array_val = hb_value_array_get(val, ii);
+ if (hb_value_type(array_val) == HB_VALUE_TYPE_DICT)
+ {
+ dict_clean(array_val, template_val);
+ }
+ }
+ }
+ }
+ }
+ hb_value_free(&tmp);
+}
+
+static void preset_clean(hb_value_t *preset, hb_value_t *template)
+{
+ dict_clean(preset, template);
+
+ // Check for proper "short name" values.
+ // Convert as necessary.
+ hb_value_t *val;
+ const char *preset_name = NULL;
+ int muxer;
+
+ val = hb_dict_get(preset, "PresetName");
+ if (val != NULL)
+ preset_name = hb_value_get_string(val);
+
+ val = hb_dict_get(preset, "FileFormat");
+ if (val != NULL)
+ {
+ const char *s, *mux;
+ s = hb_value_get_string(val);
+ muxer = hb_container_get_from_name(s);
+ if (muxer == HB_MUX_INVALID)
+ {
+ const hb_container_t *c = hb_container_get_next(NULL);
+ muxer = c->format;
+ hb_error("Preset %s: Invalid container (%s)", preset_name, s);
+ }
+ mux = hb_container_get_short_name(muxer);
+ val = hb_value_string(mux);
+ hb_dict_set(preset, "FileFormat", val);
+ }
+ val = hb_dict_get(preset, "VideoEncoder");
+ if (val != NULL)
+ {
+ const char *s, *enc;
+ int vcodec;
+ s = hb_value_get_string(val);
+ vcodec = hb_video_encoder_get_from_name(s);
+ if (vcodec == HB_VCODEC_INVALID)
+ {
+ vcodec = hb_video_encoder_get_default(muxer);
+ hb_error("Preset %s: Invalid video encoder (%s)", preset_name, s);
+ }
+ enc = hb_video_encoder_get_short_name(vcodec);
+ val = hb_value_string(enc);
+ hb_dict_set(preset, "VideoEncoder", val);
+ }
+ val = hb_dict_get(preset, "VideoFramerate");
+ if (val != NULL)
+ {
+ const char *s;
+ s = hb_value_get_string(val);
+ if (strcasecmp(s, "auto"))
+ {
+ int fr = hb_video_framerate_get_from_name(s);
+ if (fr < 0)
+ {
+ val = hb_value_string("auto");
+ hb_dict_set(preset, "VideoFramerate", val);
+ hb_error("Preset %s: Invalid video framerate (%s)",
+ preset_name, s);
+ }
+ }
+ }
+ val = hb_dict_get(preset, "AudioEncoderFallback");
+ if (val != NULL)
+ {
+ const char *s, *enc;
+ int acodec;
+ s = hb_value_get_string(val);
+ acodec = hb_audio_encoder_get_from_name(s);
+ if (acodec == HB_ACODEC_INVALID)
+ {
+ acodec = hb_audio_encoder_get_default(muxer);
+ hb_error("Preset %s: Invalid audio fallback encoder (%s)",
+ preset_name, s);
+ }
+ enc = hb_audio_encoder_get_short_name(acodec);
+ val = hb_value_string(enc);
+ hb_dict_set(preset, "AudioEncoderFallback", val);
+ }
+ hb_value_t *alist = hb_dict_get(preset, "AudioList");
+ int count = hb_value_array_len(alist);
+ int ii;
+ for (ii = 0; ii < count; ii++)
+ {
+ hb_value_t *adict = hb_value_array_get(alist, ii);
+ val = hb_dict_get(adict, "AudioEncoder");
+ if (val != NULL)
+ {
+ const char *s, *enc;
+ int acodec;
+ s = hb_value_get_string(val);
+ acodec = hb_audio_encoder_get_from_name(s);
+ if (acodec == HB_ACODEC_INVALID)
+ {
+ acodec = hb_audio_encoder_get_default(muxer);
+ hb_error("Preset %s: Invalid audio encoder (%s)",
+ preset_name, s);
+ }
+ enc = hb_audio_encoder_get_short_name(acodec);
+ val = hb_value_string(enc);
+ hb_dict_set(preset, "AudioEncoder", val);
+ }
+ val = hb_dict_get(adict, "AudioSamplerate");
+ if (val != NULL)
+ {
+ const char *s;
+ s = hb_value_get_string(val);
+ if (strcasecmp(s, "auto"))
+ {
+ int sr = hb_video_framerate_get_from_name(s);
+ if (sr < 0)
+ {
+ val = hb_value_string("auto");
+ hb_dict_set(preset, "AudioSamplerate", val);
+ hb_error("Preset %s: Invalid audio samplerate (%s)",
+ preset_name, s);
+ }
+ }
+ }
+ val = hb_dict_get(adict, "AudioMixdown");
+ if (val != NULL)
+ {
+ const char *s, *mix;
+ s = hb_value_get_string(val);
+ int mixdown = hb_mixdown_get_from_name(s);
+ if (mixdown == HB_INVALID_AMIXDOWN)
+ {
+ // work.c do_job() sanitizes NONE to default mixdown
+ mixdown = HB_AMIXDOWN_NONE;
+ hb_error("Preset %s: Invalid audio mixdown (%s)",
+ preset_name, s);
+ }
+ mix = hb_mixdown_get_short_name(mixdown);
+ val = hb_value_string(mix);
+ hb_dict_set(adict, "AudioMixdown", val);
+ }
+ }
+}
+
+static void presets_clean(hb_value_t *presets, hb_value_t *template)
+{
+ if (hb_value_type(presets) == HB_VALUE_TYPE_ARRAY)
+ {
+ // An array of presets, clean each one
+ int ii, count;
+ count = hb_value_array_len(presets);
+ for (ii = 0; ii < count; ii++)
+ {
+ hb_value_t *preset = hb_value_array_get(presets, ii);
+ preset_clean(preset, template);
+ }
+ }
+ else if (hb_value_type(presets) == HB_VALUE_TYPE_DICT &&
+ hb_dict_get(presets, "VersionMajor") != NULL)
+ {
+ // A packaged preset list
+ hb_value_t *list = hb_dict_get(presets, "PresetList");
+ presets_clean(list, template);
+ }
+ else if (hb_value_type(presets) == HB_VALUE_TYPE_DICT &&
+ hb_dict_get(presets, "PresetName") != NULL)
+ {
+ // An individual preset
+ preset_clean(presets, template);
+ }
+ else
+ {
+ hb_error("Error: invalid preset format in presets_clean()");
+ }
+}
+
+// Note that unpackage does not make any copies.
+// In one increases the reference count.
+static hb_value_t * preset_unpackage(hb_value_t *packaged_presets)
+{
+ // TODO: Verify compatible version number.
+ // Do any desired legacy translations.
+ if (hb_value_type(packaged_presets) == HB_VALUE_TYPE_ARRAY)
+ {
+ // Not packaged
+ hb_value_incref(packaged_presets);
+ return packaged_presets;
+ }
+ hb_value_t *presets = hb_dict_get(packaged_presets, "PresetList");
+ hb_value_incref(presets);
+ return presets;
+}
+
+static hb_value_t * preset_package(hb_value_t *presets)
+{
+ hb_dict_t *packaged_presets;
+ if (hb_dict_get(presets, "VersionMajor") == NULL)
+ {
+ // Preset is not packaged
+ packaged_presets = hb_dict_init();
+ hb_dict_set(packaged_presets, "VersionMajor",
+ hb_value_int(hb_preset_version_major));
+ hb_dict_set(packaged_presets, "VersionMinor",
+ hb_value_int(hb_preset_version_minor));
+ hb_dict_set(packaged_presets, "VersionMicro",
+ hb_value_int(hb_preset_version_micro));
+
+ // TODO: What else to we want in the preset containers header?
+ if (hb_value_type(presets) == HB_VALUE_TYPE_DICT)
+ {
+ hb_value_array_t *tmp = hb_value_array_init();
+ hb_value_array_append(tmp, presets);
+ presets = tmp;
+ }
+ hb_dict_t *tmp = hb_value_dup(presets);
+ presets_clean(tmp, hb_preset_template);
+ hb_dict_set(packaged_presets, "PresetList", tmp);
+ }
+ else
+ {
+ // Preset is already packaged
+ hb_dict_t *tmp = hb_value_dup(presets);
+ presets_clean(tmp, hb_preset_template);
+ packaged_presets = tmp;
+ }
+ return packaged_presets;
+}
+
+void hb_presets_builtin_init(void)
+{
+ hb_value_t * dict = hb_value_json(hb_builtin_presets_json);
+ hb_value_t * template = hb_dict_get(dict, "PresetTemplate");
+ hb_preset_version_major = hb_value_get_int(
+ hb_dict_get(template, "VersionMajor"));
+ hb_preset_version_minor = hb_value_get_int(
+ hb_dict_get(template, "VersionMinor"));
+ hb_preset_version_micro = hb_value_get_int(
+ hb_dict_get(template, "VersionMicro"));
+ hb_preset_template = hb_value_dup(hb_dict_get(template, "Preset"));
+
+ hb_presets_builtin = hb_value_dup(hb_dict_get(dict, "PresetBuiltin"));
+ hb_value_free(&dict);
+
+ // Make a dup, never change contents of hb_presets_builtin
+ hb_presets = hb_value_dup(hb_presets_builtin);
+ hb_presets_custom = hb_value_array_init();
+
+}
+
+int hb_presets_gui_init(void)
+{
+ char path[1024];
+ hb_value_t * dict = NULL;
+
+#if defined(HB_PRESET_JSON_FILE)
+ hb_get_user_config_filename(path, "%s", HB_PRESET_JSON_FILE);
+ dict = hb_value_read_json(path);
+ if (dict != NULL)
+ {
+ hb_value_t *preset = preset_unpackage(dict);
+ // Unpackaging does some validity checks and can fail
+ if (preset == NULL)
+ return -1;
+ int result = hb_presets_add(preset);
+ hb_value_free(&preset);
+ hb_value_free(&dict);
+ return result;
+ }
+#endif
+#if defined(HB_PRESET_PLIST_FILE)
+ if (dict == NULL)
+ {
+ hb_get_user_config_filename(path, "%s", HB_PRESET_PLIST_FILE);
+ dict = hb_plist_parse_file(path);
+ if (dict != NULL)
+ {
+ int result = hb_presets_add(dict);
+ hb_value_free(&dict);
+ return result;
+ }
+ }
+#endif
+ if (dict == NULL)
+ {
+ hb_error("Failed to load GUI presets file");
+#if defined(HB_PRESET_JSON_FILE)
+ hb_error("Attempted: %s", HB_PRESET_JSON_FILE);
+#endif
+#if defined(HB_PRESET_PLIST_FILE)
+ hb_error("Attempted: %s", HB_PRESET_PLIST_FILE);
+#endif
+ return -1;
+ }
+ return -1;
+}
+
+hb_value_t * hb_presets_builtin_get(void)
+{
+ return hb_value_dup(hb_presets_builtin);
+}
+
+char * hb_presets_builtin_get_json(void)
+{
+ char *json = hb_value_get_json(hb_presets_builtin);
+ return json;
+}
+
+static hb_value_t * preset_lookup(hb_value_t *list, const char *name,
+ int def, int folder, int recurse)
+{
+ int count = hb_value_array_len(list);
+ int ii;
+ for (ii = 0; ii < count; ii++)
+ {
+ int d;
+ const char *n;
+ hb_dict_t *preset_dict = hb_value_array_get(list, ii);
+ n = hb_value_get_string(hb_dict_get(preset_dict, "PresetName"));
+ d = hb_value_get_bool(hb_dict_get(preset_dict, "Default"));
+ if (hb_value_get_bool(hb_dict_get(preset_dict, "Folder")))
+ {
+ if (folder && !def && n != NULL && !strncmp(name, n, 80))
+ return preset_dict;
+
+ if (recurse)
+ {
+ hb_value_array_t *children;
+ children = hb_dict_get(preset_dict, "ChildrenArray");
+ if (children == NULL)
+ continue;
+ preset_dict = preset_lookup(children, name, def,
+ folder, recurse);
+ if (preset_dict != NULL)
+ return preset_dict;
+ }
+ }
+ else if (!folder)
+ {
+ if (!def && n != NULL && !strncmp(n, name, 80))
+ {
+ // preset is not a folder and we found a matching preset name
+ return preset_dict;
+ }
+ else if (def && d)
+ {
+ return preset_dict;
+ }
+ }
+ }
+ return NULL;
+}
+
+// Lookup a preset in the preset list. The "name" may contain '/'
+// separators to explicitely specify a preset within the preset lists
+// folder structure.
+//
+// If 'recurse' is specified, a recursive search for the first component
+// in the name will be performed.
+//
+// I assume that the actual preset name does not include any '/'
+hb_value_t * hb_preset_get(const char *name, int recurse)
+{
+ if (name == NULL || name[0] == 0)
+ {
+ // bad input.
+ return NULL;
+ }
+
+ char *tmp = strdup(name);
+ char *part, *next;
+ hb_value_t *list;
+
+ list = hb_presets;
+ part = tmp;
+ next = strchr(name, '/');
+ if (next == NULL)
+ {
+ // Only preset name was specified, so do a recursive search
+ hb_value_t *preset = preset_lookup(list, part, 0, 0, recurse);
+ free(tmp);
+ if (preset == NULL)
+ {
+ return NULL;
+ }
+ return hb_value_dup(preset);
+ }
+ // Found folder separator in name, do explicit path search
+ while (part)
+ {
+ *next = 0;
+ next++;
+ if (next[0] == 0)
+ {
+ // name ends in a folder separator '/'. Invalid input
+ free(tmp);
+ return NULL;
+ }
+
+ // We have a folder part. Lookup the folder.
+ // First try non-recursive so that we match top level folders first
+ hb_dict_t *folder = preset_lookup(list, part, 0, 1, 0);
+ if (folder == NULL && recurse)
+ {
+ // Try a recursive search for the folder
+ folder = preset_lookup(list, part, 0, 1, recurse);
+ }
+ if (folder == NULL)
+ {
+ // Not found
+ free(tmp);
+ return NULL;
+ }
+ list = hb_dict_get(folder, "ChildrenArray");
+ if (list == NULL)
+ {
+ // Folder found, but it has no children
+ free(tmp);
+ return NULL;
+ }
+ // Folder found, continue the search
+ part = next;
+ next = strchr(name, '/');
+ if (next == NULL)
+ {
+ // We have reached the final component of the path
+ // which is the preset name. Do a non-recursive search.
+ // If the preset is not in the specified folder, will
+ // return NULL
+ hb_value_t *preset = preset_lookup(list, part, 0, 0, 0);
+ free(tmp);
+ if (preset == NULL)
+ {
+ return NULL;
+ }
+ return hb_value_dup(preset);
+ }
+ }
+ // This should never be reached, but some compilers might complain
+ // without a final return.
+ free(tmp);
+ return NULL;
+}
+
+char * hb_preset_get_json(const char *name, int recurse)
+{
+ hb_value_t * preset;
+ char *json;
+ preset = hb_preset_get(name, recurse);
+ if (preset == NULL)
+ return NULL;
+ json = hb_value_get_json(preset);
+ hb_value_free(&preset);
+ return json;
+}
+
+static hb_dict_t * find_first_preset(hb_value_array_t *list)
+{
+ int count, ii;
+ count = hb_value_array_len(list);
+ for (ii = 0; ii < count; ii++)
+ {
+ hb_dict_t *preset_dict = hb_value_array_get(list, ii);
+ if (hb_value_get_bool(hb_dict_get(preset_dict, "Folder")))
+ {
+ hb_value_array_t *children;
+ children = hb_dict_get(preset_dict, "ChildrenArray");
+ if (children == NULL)
+ continue;
+ preset_dict = find_first_preset(children);
+ if (preset_dict != NULL)
+ return preset_dict;
+ }
+ else
+ {
+ return preset_dict;
+ }
+ }
+ return NULL;
+}
+
+hb_dict_t * hb_presets_get_default(void)
+{
+ // Look for preset with 'Default' flag set
+ hb_value_t *preset = preset_lookup(hb_presets, NULL, 1, 0, 1);
+ if (preset == NULL)
+ {
+ // Look for preset named 'Normal' flag set
+ preset = preset_lookup(hb_presets, "Normal", 0, 0, 1);
+ if (preset == NULL)
+ {
+ // Just grab the first preset available
+ preset = find_first_preset(hb_presets);
+ if (preset == NULL)
+ return NULL;
+ }
+ }
+ return hb_value_dup(preset);
+}
+
+char * hb_presets_get_default_json(void)
+{
+ hb_value_t *preset = preset_lookup(hb_presets, NULL, 1, 0, 1);
+ if (preset == NULL)
+ return NULL;
+ return hb_value_get_json(preset);
+}
+
+// Return:
+// 0 upon success
+// 1 if preset name could not be found
+int hb_presets_set_default(const char *name, int recurse)
+{
+ hb_value_t *preset = preset_lookup(hb_presets, NULL, 1, 0, 1);
+ if (preset != NULL)
+ {
+ // Mark old defalt as not
+ hb_dict_set(preset, "Default", hb_value_bool(0));
+ }
+ preset = preset_lookup(hb_presets, name, 0, 0, recurse);
+ if (preset == NULL)
+ return -1;
+ hb_dict_set(preset, "Default", hb_value_bool(1));
+ return 0;
+}
+
+int hb_presets_add(hb_value_t *preset)
+{
+ if (preset == NULL)
+ return -1;
+ // TODO: validity checking of input preset
+ if (hb_value_type(preset) == HB_VALUE_TYPE_DICT)
+ {
+ // A standalone preset or folder of presets. Add to preset array.
+ // Only allow custom presets to be added
+ if (hb_value_get_int(hb_dict_get(preset, "Type")) == 1)
+ hb_value_array_append(hb_presets_custom, hb_value_dup(preset));
+ }
+ else if (hb_value_type(preset) == HB_VALUE_TYPE_ARRAY)
+ {
+ // An array of presets. Add each element.
+ int count = hb_value_array_len(preset);
+ int ii;
+ for (ii = 0; ii < count; ii++)
+ {
+ hb_value_t *value = hb_value_array_get(preset, ii);
+ hb_value_array_append(hb_presets_custom, hb_value_dup(value));
+ }
+ }
+
+ // Reconstruct global list
+ hb_value_decref(hb_presets);
+ hb_presets = hb_value_dup(hb_presets_builtin);
+ // Append custom presets
+ int count = hb_value_array_len(hb_presets_custom);
+ int ii;
+ for (ii = 0; ii < count; ii++)
+ {
+ hb_value_t *value = hb_value_array_get(hb_presets_custom, ii);
+ // Only allow custom presets to be added
+ if (hb_value_get_int(hb_dict_get(value, "Type")) == 1)
+ hb_value_array_append(hb_presets, hb_value_dup(value));
+ }
+ return 0;
+}
+
+int hb_presets_add_json(const char *json)
+{
+ hb_value_t *packaged_preset = hb_value_json(json);
+ hb_value_t *preset = preset_unpackage(packaged_preset);
+ if (preset == NULL)
+ return -1;
+ int result = hb_presets_add(preset);
+ hb_value_free(&preset);
+ hb_value_free(&packaged_preset);
+ return result;
+}
+
+int hb_presets_add_file(const char *filename)
+{
+ hb_value_t *packaged_preset = hb_value_read_json(filename);
+ hb_value_t *preset = preset_unpackage(packaged_preset);
+ // Unpackaging does some validity checks and can fail
+ if (preset == NULL)
+ return -1;
+ int result = hb_presets_add(preset);
+ hb_value_free(&preset);
+ hb_value_free(&packaged_preset);
+ return result;
+}
+
+static int compare_str(const void *a, const void *b)
+{
+ return strncmp(*(const char**)a, *(const char**)b, PATH_MAX);
+}
+
+int hb_presets_add_path(char * path)
+{
+ hb_stat_t sb;
+ HB_DIR * dir;
+ struct dirent * entry;
+ char * filename;
+ int count, ii;
+ char ** files;
+ int result = -1;
+
+ if (hb_stat(path, &sb))
+ return -1;
+
+ if (S_ISREG(sb.st_mode))
+ {
+ return hb_presets_add_file(path);
+ }
+
+ if (!S_ISDIR(sb.st_mode))
+ return -1;
+
+ dir = hb_opendir(path);
+ if ( dir == NULL )
+ return -1;
+
+ // Count the total number of entries
+ count = 0;
+ while ((entry = hb_readdir(dir)))
+ {
+ count++;
+ }
+ files = malloc(count * sizeof(char*));
+
+ // Find all regular files
+ ii = 0;
+ hb_rewinddir(dir);
+ while ((entry = hb_readdir(dir)))
+ {
+ filename = hb_strdup_printf("%s" DIR_SEP_STR "%s", path, entry->d_name);
+ if (hb_stat(filename, &sb))
+ {
+ free(filename);
+ continue;
+ }
+
+ // Only load regular files
+ if (!S_ISREG(sb.st_mode))
+ {
+ free(filename);
+ continue;
+ }
+ // Only load files with .json extension
+ if (strcmp(".json", filename + strlen(filename) - 5))
+ {
+ free(filename);
+ continue;
+ }
+
+ files[ii++] = filename;
+ }
+ count = ii;
+
+ // Sort the files so presets get added in a consistent order
+ qsort(files, count, sizeof(char*), compare_str);
+
+ // Add preset files to preset list
+ for (ii = 0; ii < count; ii++)
+ {
+ int res = hb_presets_add_file(files[ii]);
+ // return success if any one of the files is successfully loaded
+ if (res == 0)
+ result = res;
+ }
+ hb_closedir( dir );
+ free(files);
+
+ return result;
+}
+
+hb_value_t * hb_presets_get(void)
+{
+ return hb_value_dup(hb_presets);
+}
+
+char * hb_presets_get_json(void)
+{
+ return hb_value_get_json(hb_presets);
+}
+
+int hb_preset_write_json(hb_value_t *preset, const char *path)
+{
+ hb_value_t *packaged_preset = preset_package(preset);
+ // Packaging does some validity checks and can fail
+ if (packaged_preset == NULL)
+ return -1;
+ int result = hb_value_write_json(packaged_preset, path);
+ hb_value_free(&packaged_preset);
+ return result;
+}
+
+char * hb_preset_package_json(hb_value_t *preset)
+{
+ hb_value_t *packaged_preset = preset_package(preset);
+ // Packaging does some validity checks and can fail
+ if (packaged_preset == NULL)
+ return NULL;
+ char *json = hb_value_get_json(packaged_preset);
+ hb_value_free(&packaged_preset);
+ return json;
+}
+
+void hb_presets_free(void)
+{
+ hb_value_free(&hb_preset_template);
+ hb_value_free(&hb_presets);
+ hb_value_free(&hb_presets_custom);
+ hb_value_free(&hb_presets_builtin);
+}
diff --git a/libhb/preset.h b/libhb/preset.h
new file mode 100644
index 000000000..5e7c460b6
--- /dev/null
+++ b/libhb/preset.h
@@ -0,0 +1,84 @@
+/* hb_preset.h
+
+ Copyright (c) 2003-2015 HandBrake Team
+ This file is part of the HandBrake source code
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License v2.
+ For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
+ */
+#if !defined(HB_PRESET_H)
+#define HB_PRESET_H
+
+#include "common.h"
+#include "hb_dict.h"
+
+// Initialize the hb_value_array_t that holds HandBrake builtin presets
+void hb_presets_builtin_init(void);
+
+// Load presets from GUI presets file if possible
+int hb_presets_gui_init(void);
+
+// Free all libhb presets
+void hb_presets_free(void);
+
+// Get list of HandBrake builtin presets as hb_value_array_t
+hb_value_t * hb_presets_builtin_get(void);
+
+// Get list of HandBrake builtin presets as json string
+char * hb_presets_builtin_get_json(void);
+
+// Register new presets with libhb from
+// hb_dict_t (single preset) or hb_value_array_t (list of presets)
+int hb_presets_add(hb_value_t *preset);
+
+// Register new presets with libhb from json string
+int hb_presets_add_json(const char *json);
+
+// Register new presets with libhb from json file
+int hb_presets_add_file(const char *filename);
+
+// Register new presets with libhb from json file(s)
+// path can be a directory, in which case all *.json files in the
+// directory will be added
+int hb_presets_add_path(char * path);
+
+// Get list of all presets registered with libhb as hb_value_array_t
+hb_value_t * hb_presets_get(void);
+
+// Get list of all presets registered with libhb as json string
+char * hb_presets_get_json(void);
+
+// Initialize a job from the given title and preset
+hb_dict_t * hb_preset_job_init(hb_handle_t *h, int title_index,
+ hb_dict_t *preset);
+int hb_preset_job_add_subtitles(hb_handle_t *h, int title_index,
+ hb_dict_t *preset, hb_dict_t *job_dict);
+int hb_preset_job_add_audio(hb_handle_t *h, int title_index,
+ hb_dict_t *preset, hb_dict_t *job_dict);
+
+// Lookup a preset in the preset list. The "name" may contain '/'
+// separators to explicitely specify a preset within the preset lists
+// folder structure.
+//
+// If 'recurse' is specified, a recursive search for the first component
+// in the name will be performed.
+//
+// I assume that the actual preset name does not include any '/'
+hb_value_t * hb_preset_get(const char *name, int recurse);
+char * hb_preset_get_json(const char *name, int recurse);
+
+// Recursively lookup the preset that is marked as 'Default'
+hb_dict_t * hb_presets_get_default(void);
+char * hb_presets_get_default_json(void);
+
+// Set the preset that is marked as 'Default'
+int hb_presets_set_default(const char *name, int recurse);
+
+// Package the provided preset (wrap in dict and add version etc)
+// and write to json file
+int hb_preset_write_json(hb_value_t *preset, const char *path);
+// Package the provided preset (wrap in dict and add version etc)
+// and return as json string
+char * hb_preset_package_json(hb_value_t *preset);
+
+#endif // HB_PRESET_H
diff --git a/libhb/preset_builtin.json b/libhb/preset_builtin.json
new file mode 100644
index 000000000..5bf5d4d8a
--- /dev/null
+++ b/libhb/preset_builtin.json
@@ -0,0 +1,855 @@
+ [
+ {
+ "ChildrenArray": [
+ {
+ "AudioAllowAACPass": 1,
+ "AudioAllowAC3Pass": 1,
+ "AudioAllowDTSHDPass": 1,
+ "AudioAllowDTSPass": 1,
+ "AudioAllowMP3Pass": 1,
+ "AudioEncoderFallback": "ac3",
+ "AudioList": [
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "aac",
+ "AudioMixdown": "dpl2",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ },
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "copy:ac3",
+ "AudioMixdown": "none",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ }
+ ],
+ "ChapterMarkers": 1,
+ "Default": 0,
+ "FileFormat": "mp4",
+ "Folder": false,
+ "Mp4HttpOptimize": 0,
+ "Mp4iPodCompatible": 0,
+ "PictureAutoCrop": 1,
+ "PictureBottomCrop": 0,
+ "PictureDeblock": 0,
+ "PictureDecomb": 0,
+ "PictureDecombCustom": "",
+ "PictureDecombDeinterlace": 1,
+ "PictureDeinterlace": 0,
+ "PictureDeinterlaceCustom": "",
+ "PictureDenoiseCustom": "",
+ "PictureDenoiseFilter": "off",
+ "PictureDetelecine": 0,
+ "PictureDetelecineCustom": "",
+ "PictureHeight": 576,
+ "PictureKeepRatio": 0,
+ "PictureLeftCrop": 0,
+ "PictureModulus": 2,
+ "PicturePAR": "loose",
+ "PictureRightCrop": 0,
+ "PictureTopCrop": 0,
+ "PictureWidth": 720,
+ "PresetDescription": "HandBrake's settings for compatibility with all Apple devices (including the iPod 6G and later). Includes Dolby Digital audio for surround sound.",
+ "PresetName": "Universal",
+ "Type": 0,
+ "UsesPictureFilters": 1,
+ "UsesPictureSettings": 1,
+ "VideoAvgBitrate": "2500",
+ "VideoEncoder": "x264",
+ "VideoFramerate": "30",
+ "VideoFramerateMode": "pfr",
+ "VideoGrayScale": 0,
+ "VideoLevel": "3.0",
+ "VideoOptionExtra": "",
+ "VideoPreset": "fast",
+ "VideoProfile": "baseline",
+ "VideoQualitySlider": 20.0,
+ "VideoQualityType": 2,
+ "VideoTune": "",
+ "VideoTurboTwoPass": 0,
+ "VideoTwoPass": 0,
+ "x264Option": "",
+ "x264UseAdvancedOptions": 0
+ },
+ {
+ "AudioAllowAACPass": 1,
+ "AudioAllowAC3Pass": 1,
+ "AudioAllowDTSHDPass": 1,
+ "AudioAllowDTSPass": 1,
+ "AudioAllowMP3Pass": 1,
+ "AudioEncoderFallback": "ac3",
+ "AudioList": [
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "aac",
+ "AudioMixdown": "dpl2",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ }
+ ],
+ "ChapterMarkers": 1,
+ "Default": 0,
+ "FileFormat": "mp4",
+ "Folder": false,
+ "Mp4HttpOptimize": 0,
+ "Mp4iPodCompatible": 1,
+ "PictureAutoCrop": 1,
+ "PictureBottomCrop": 0,
+ "PictureDeblock": 0,
+ "PictureDecomb": 0,
+ "PictureDecombCustom": "",
+ "PictureDecombDeinterlace": 1,
+ "PictureDeinterlace": 0,
+ "PictureDeinterlaceCustom": "",
+ "PictureDenoiseCustom": "",
+ "PictureDenoiseFilter": "off",
+ "PictureDetelecine": 0,
+ "PictureDetelecineCustom": "",
+ "PictureHeight": 240,
+ "PictureKeepRatio": 1,
+ "PictureLeftCrop": 0,
+ "PictureModulus": 2,
+ "PicturePAR": "off",
+ "PictureRightCrop": 0,
+ "PictureTopCrop": 0,
+ "PictureWidth": 320,
+ "PresetDescription": "HandBrake's settings for playback on the iPod with Video (all generations).",
+ "PresetName": "iPod",
+ "Type": 0,
+ "UsesPictureFilters": 1,
+ "UsesPictureSettings": 1,
+ "VideoAvgBitrate": "2500",
+ "VideoEncoder": "x264",
+ "VideoFramerate": "30",
+ "VideoFramerateMode": "pfr",
+ "VideoGrayScale": 0,
+ "VideoLevel": "1.3",
+ "VideoOptionExtra": "",
+ "VideoPreset": "medium",
+ "VideoProfile": "baseline",
+ "VideoQualitySlider": 22.0,
+ "VideoQualityType": 2,
+ "VideoTune": "",
+ "VideoTurboTwoPass": 0,
+ "VideoTwoPass": 0,
+ "x264Option": "",
+ "x264UseAdvancedOptions": 0
+ },
+ {
+ "AudioAllowAACPass": 1,
+ "AudioAllowAC3Pass": 1,
+ "AudioAllowDTSHDPass": 1,
+ "AudioAllowDTSPass": 1,
+ "AudioAllowMP3Pass": 1,
+ "AudioEncoderFallback": "ac3",
+ "AudioList": [
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "aac",
+ "AudioMixdown": "dpl2",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ }
+ ],
+ "ChapterMarkers": 1,
+ "Default": 0,
+ "FileFormat": "mp4",
+ "Folder": false,
+ "Mp4HttpOptimize": 0,
+ "Mp4iPodCompatible": 0,
+ "PictureAutoCrop": 1,
+ "PictureBottomCrop": 0,
+ "PictureDeblock": 0,
+ "PictureDecomb": 0,
+ "PictureDecombCustom": "",
+ "PictureDecombDeinterlace": 1,
+ "PictureDeinterlace": 0,
+ "PictureDeinterlaceCustom": "",
+ "PictureDenoiseCustom": "",
+ "PictureDenoiseFilter": "off",
+ "PictureDetelecine": 0,
+ "PictureDetelecineCustom": "",
+ "PictureHeight": 640,
+ "PictureKeepRatio": 0,
+ "PictureLeftCrop": 0,
+ "PictureModulus": 2,
+ "PicturePAR": "loose",
+ "PictureRightCrop": 0,
+ "PictureTopCrop": 0,
+ "PictureWidth": 960,
+ "PresetDescription": "HandBrake's settings for handheld iOS devices (iPhone 4, iPod touch 3G and later).",
+ "PresetName": "iPhone & iPod touch",
+ "Type": 0,
+ "UsesPictureFilters": 1,
+ "UsesPictureSettings": 1,
+ "VideoAvgBitrate": "2500",
+ "VideoEncoder": "x264",
+ "VideoFramerate": "30",
+ "VideoFramerateMode": "pfr",
+ "VideoGrayScale": 0,
+ "VideoLevel": "3.1",
+ "VideoOptionExtra": "",
+ "VideoPreset": "medium",
+ "VideoProfile": "high",
+ "VideoQualitySlider": 22.0,
+ "VideoQualityType": 2,
+ "VideoTune": "",
+ "VideoTurboTwoPass": 0,
+ "VideoTwoPass": 0,
+ "x264Option": "",
+ "x264UseAdvancedOptions": 0
+ },
+ {
+ "AudioAllowAACPass": 1,
+ "AudioAllowAC3Pass": 1,
+ "AudioAllowDTSHDPass": 1,
+ "AudioAllowDTSPass": 1,
+ "AudioAllowMP3Pass": 1,
+ "AudioEncoderFallback": "ac3",
+ "AudioList": [
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "aac",
+ "AudioMixdown": "dpl2",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ }
+ ],
+ "ChapterMarkers": 1,
+ "Default": 0,
+ "FileFormat": "mp4",
+ "Folder": false,
+ "Mp4HttpOptimize": 0,
+ "Mp4iPodCompatible": 0,
+ "PictureAutoCrop": 1,
+ "PictureBottomCrop": 0,
+ "PictureDeblock": 0,
+ "PictureDecomb": 0,
+ "PictureDecombCustom": "",
+ "PictureDecombDeinterlace": 1,
+ "PictureDeinterlace": 0,
+ "PictureDeinterlaceCustom": "",
+ "PictureDenoiseCustom": "",
+ "PictureDenoiseFilter": "off",
+ "PictureDetelecine": 0,
+ "PictureDetelecineCustom": "",
+ "PictureHeight": 720,
+ "PictureKeepRatio": 0,
+ "PictureLeftCrop": 0,
+ "PictureModulus": 2,
+ "PicturePAR": "loose",
+ "PictureRightCrop": 0,
+ "PictureTopCrop": 0,
+ "PictureWidth": 1280,
+ "PresetDescription": "HandBrake's settings for playback on the iPad (all generations).",
+ "PresetName": "iPad",
+ "Type": 0,
+ "UsesPictureFilters": 1,
+ "UsesPictureSettings": 1,
+ "VideoAvgBitrate": "2500",
+ "VideoEncoder": "x264",
+ "VideoFramerate": "30",
+ "VideoFramerateMode": "pfr",
+ "VideoGrayScale": 0,
+ "VideoLevel": "3.1",
+ "VideoOptionExtra": "",
+ "VideoPreset": "medium",
+ "VideoProfile": "high",
+ "VideoQualitySlider": 20.0,
+ "VideoQualityType": 2,
+ "VideoTune": "",
+ "VideoTurboTwoPass": 0,
+ "VideoTwoPass": 0,
+ "x264Option": "",
+ "x264UseAdvancedOptions": 0
+ },
+ {
+ "AudioAllowAACPass": 1,
+ "AudioAllowAC3Pass": 1,
+ "AudioAllowDTSHDPass": 1,
+ "AudioAllowDTSPass": 1,
+ "AudioAllowMP3Pass": 1,
+ "AudioEncoderFallback": "ac3",
+ "AudioList": [
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "aac",
+ "AudioMixdown": "dpl2",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ },
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "copy:ac3",
+ "AudioMixdown": "none",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ }
+ ],
+ "ChapterMarkers": 1,
+ "Default": 0,
+ "FileFormat": "mp4",
+ "Folder": false,
+ "Mp4HttpOptimize": 0,
+ "Mp4iPodCompatible": 0,
+ "PictureAutoCrop": 1,
+ "PictureBottomCrop": 0,
+ "PictureDeblock": 0,
+ "PictureDecomb": 0,
+ "PictureDecombCustom": "",
+ "PictureDecombDeinterlace": 1,
+ "PictureDeinterlace": 0,
+ "PictureDeinterlaceCustom": "",
+ "PictureDenoiseCustom": "",
+ "PictureDenoiseFilter": "off",
+ "PictureDetelecine": 0,
+ "PictureDetelecineCustom": "",
+ "PictureHeight": 720,
+ "PictureKeepRatio": 0,
+ "PictureLeftCrop": 0,
+ "PictureModulus": 2,
+ "PicturePAR": "loose",
+ "PictureRightCrop": 0,
+ "PictureTopCrop": 0,
+ "PictureWidth": 960,
+ "PresetDescription": "HandBrake's settings for the original AppleTV. Includes Dolby Digital audio for surround sound. Also compatible with iOS devices released since 2009.",
+ "PresetName": "AppleTV",
+ "Type": 0,
+ "UsesPictureFilters": 1,
+ "UsesPictureSettings": 1,
+ "VideoAvgBitrate": "2500",
+ "VideoEncoder": "x264",
+ "VideoFramerate": "30",
+ "VideoFramerateMode": "pfr",
+ "VideoGrayScale": 0,
+ "VideoLevel": "3.1",
+ "VideoOptionExtra": "qpmin=4:cabac=0:ref=2:b-pyramid=none:weightb=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500",
+ "VideoPreset": "medium",
+ "VideoProfile": "high",
+ "VideoQualitySlider": 20.0,
+ "VideoQualityType": 2,
+ "VideoTune": "",
+ "VideoTurboTwoPass": 0,
+ "VideoTwoPass": 0,
+ "x264Option": "",
+ "x264UseAdvancedOptions": 0
+ },
+ {
+ "AudioAllowAACPass": 1,
+ "AudioAllowAC3Pass": 1,
+ "AudioAllowDTSHDPass": 1,
+ "AudioAllowDTSPass": 1,
+ "AudioAllowMP3Pass": 1,
+ "AudioEncoderFallback": "ac3",
+ "AudioList": [
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "aac",
+ "AudioMixdown": "dpl2",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ },
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "copy:ac3",
+ "AudioMixdown": "none",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ }
+ ],
+ "ChapterMarkers": 1,
+ "Default": 0,
+ "FileFormat": "mp4",
+ "Folder": false,
+ "Mp4HttpOptimize": 0,
+ "Mp4iPodCompatible": 0,
+ "PictureAutoCrop": 1,
+ "PictureBottomCrop": 0,
+ "PictureDeblock": 0,
+ "PictureDecomb": 0,
+ "PictureDecombCustom": "",
+ "PictureDecombDeinterlace": 1,
+ "PictureDeinterlace": 0,
+ "PictureDeinterlaceCustom": "",
+ "PictureDenoiseCustom": "",
+ "PictureDenoiseFilter": "off",
+ "PictureDetelecine": 0,
+ "PictureDetelecineCustom": "",
+ "PictureHeight": 720,
+ "PictureKeepRatio": 0,
+ "PictureLeftCrop": 0,
+ "PictureModulus": 2,
+ "PicturePAR": "loose",
+ "PictureRightCrop": 0,
+ "PictureTopCrop": 0,
+ "PictureWidth": 1280,
+ "PresetDescription": "HandBrake's settings for the second-generation AppleTV. Includes Dolby Digital audio for surround sound. NOT compatible with the original AppleTV.",
+ "PresetName": "AppleTV 2",
+ "Type": 0,
+ "UsesPictureFilters": 1,
+ "UsesPictureSettings": 1,
+ "VideoAvgBitrate": "2500",
+ "VideoEncoder": "x264",
+ "VideoFramerate": "30",
+ "VideoFramerateMode": "pfr",
+ "VideoGrayScale": 0,
+ "VideoLevel": "3.1",
+ "VideoOptionExtra": "",
+ "VideoPreset": "medium",
+ "VideoProfile": "high",
+ "VideoQualitySlider": 20.0,
+ "VideoQualityType": 2,
+ "VideoTune": "",
+ "VideoTurboTwoPass": 0,
+ "VideoTwoPass": 0,
+ "x264Option": "",
+ "x264UseAdvancedOptions": 0
+ },
+ {
+ "AudioAllowAACPass": 1,
+ "AudioAllowAC3Pass": 1,
+ "AudioAllowDTSHDPass": 1,
+ "AudioAllowDTSPass": 1,
+ "AudioAllowMP3Pass": 1,
+ "AudioEncoderFallback": "ac3",
+ "AudioList": [
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "aac",
+ "AudioMixdown": "dpl2",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ },
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "copy:ac3",
+ "AudioMixdown": "none",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ }
+ ],
+ "ChapterMarkers": 1,
+ "Default": 0,
+ "FileFormat": "mp4",
+ "Folder": false,
+ "Mp4HttpOptimize": 0,
+ "Mp4iPodCompatible": 0,
+ "PictureAutoCrop": 1,
+ "PictureBottomCrop": 0,
+ "PictureDeblock": 0,
+ "PictureDecomb": 3,
+ "PictureDecombCustom": "",
+ "PictureDecombDeinterlace": 1,
+ "PictureDeinterlace": 0,
+ "PictureDeinterlaceCustom": "",
+ "PictureDenoiseCustom": "",
+ "PictureDenoiseFilter": "off",
+ "PictureDetelecine": 0,
+ "PictureDetelecineCustom": "",
+ "PictureHeight": 1080,
+ "PictureKeepRatio": 0,
+ "PictureLeftCrop": 0,
+ "PictureModulus": 2,
+ "PicturePAR": "loose",
+ "PictureRightCrop": 0,
+ "PictureTopCrop": 0,
+ "PictureWidth": 1920,
+ "PresetDescription": "HandBrake's settings for the third-generation AppleTV. Includes Dolby Digital audio for surround sound. NOT compatible with the original AppleTV. May stutter on the second-generation AppleTV.",
+ "PresetName": "AppleTV 3",
+ "Type": 0,
+ "UsesPictureFilters": 1,
+ "UsesPictureSettings": 1,
+ "VideoAvgBitrate": "2500",
+ "VideoEncoder": "x264",
+ "VideoFramerate": "30",
+ "VideoFramerateMode": "pfr",
+ "VideoGrayScale": 0,
+ "VideoLevel": "4.0",
+ "VideoOptionExtra": "",
+ "VideoPreset": "medium",
+ "VideoProfile": "high",
+ "VideoQualitySlider": 20.0,
+ "VideoQualityType": 2,
+ "VideoTune": "",
+ "VideoTurboTwoPass": 0,
+ "VideoTwoPass": 0,
+ "x264Option": "",
+ "x264UseAdvancedOptions": 0
+ },
+ {
+ "AudioAllowAACPass": 1,
+ "AudioAllowAC3Pass": 1,
+ "AudioAllowDTSHDPass": 1,
+ "AudioAllowDTSPass": 1,
+ "AudioAllowMP3Pass": 1,
+ "AudioEncoderFallback": "ac3",
+ "AudioList": [
+ {
+ "AudioBitrate": "128",
+ "AudioEncoder": "aac",
+ "AudioMixdown": "dpl2",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ }
+ ],
+ "ChapterMarkers": 0,
+ "Default": 0,
+ "FileFormat": "mp4",
+ "Folder": false,
+ "Mp4HttpOptimize": 0,
+ "Mp4iPodCompatible": 0,
+ "PictureAutoCrop": 1,
+ "PictureBottomCrop": 0,
+ "PictureDeblock": 0,
+ "PictureDecomb": 0,
+ "PictureDecombCustom": "",
+ "PictureDecombDeinterlace": 1,
+ "PictureDeinterlace": 0,
+ "PictureDeinterlaceCustom": "",
+ "PictureDenoiseCustom": "",
+ "PictureDenoiseFilter": "off",
+ "PictureDetelecine": 0,
+ "PictureDetelecineCustom": "",
+ "PictureHeight": 576,
+ "PictureKeepRatio": 0,
+ "PictureLeftCrop": 0,
+ "PictureModulus": 2,
+ "PicturePAR": "loose",
+ "PictureRightCrop": 0,
+ "PictureTopCrop": 0,
+ "PictureWidth": 720,
+ "PresetDescription": "HandBrake's settings for midrange devices running Android 2.3 or later.",
+ "PresetName": "Android",
+ "Type": 0,
+ "UsesPictureFilters": 1,
+ "UsesPictureSettings": 1,
+ "VideoAvgBitrate": "2500",
+ "VideoEncoder": "x264",
+ "VideoFramerate": "30",
+ "VideoFramerateMode": "pfr",
+ "VideoGrayScale": 0,
+ "VideoLevel": "3.0",
+ "VideoOptionExtra": "",
+ "VideoPreset": "medium",
+ "VideoProfile": "main",
+ "VideoQualitySlider": 22.0,
+ "VideoQualityType": 2,
+ "VideoTune": "",
+ "VideoTurboTwoPass": 0,
+ "VideoTwoPass": 0,
+ "x264Option": "",
+ "x264UseAdvancedOptions": 0
+ },
+ {
+ "AudioAllowAACPass": 1,
+ "AudioAllowAC3Pass": 1,
+ "AudioAllowDTSHDPass": 1,
+ "AudioAllowDTSPass": 1,
+ "AudioAllowMP3Pass": 1,
+ "AudioEncoderFallback": "ac3",
+ "AudioList": [
+ {
+ "AudioBitrate": "128",
+ "AudioEncoder": "aac",
+ "AudioMixdown": "dpl2",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ }
+ ],
+ "ChapterMarkers": 0,
+ "Default": 0,
+ "FileFormat": "mp4",
+ "Folder": false,
+ "Mp4HttpOptimize": 0,
+ "Mp4iPodCompatible": 0,
+ "PictureAutoCrop": 1,
+ "PictureBottomCrop": 0,
+ "PictureDeblock": 0,
+ "PictureDecomb": 0,
+ "PictureDecombCustom": "",
+ "PictureDecombDeinterlace": 1,
+ "PictureDeinterlace": 0,
+ "PictureDeinterlaceCustom": "",
+ "PictureDenoiseCustom": "",
+ "PictureDenoiseFilter": "off",
+ "PictureDetelecine": 0,
+ "PictureDetelecineCustom": "",
+ "PictureHeight": 720,
+ "PictureKeepRatio": 0,
+ "PictureLeftCrop": 0,
+ "PictureModulus": 2,
+ "PicturePAR": "loose",
+ "PictureRightCrop": 0,
+ "PictureTopCrop": 0,
+ "PictureWidth": 1280,
+ "PresetDescription": "HandBrake's preset for tablets running Android 2.3 or later.",
+ "PresetName": "Android Tablet",
+ "Type": 0,
+ "UsesPictureFilters": 1,
+ "UsesPictureSettings": 1,
+ "VideoAvgBitrate": "2500",
+ "VideoEncoder": "x264",
+ "VideoFramerate": "30",
+ "VideoFramerateMode": "pfr",
+ "VideoGrayScale": 0,
+ "VideoLevel": "3.1",
+ "VideoOptionExtra": "",
+ "VideoPreset": "medium",
+ "VideoProfile": "main",
+ "VideoQualitySlider": 22.0,
+ "VideoQualityType": 2,
+ "VideoTune": "",
+ "VideoTurboTwoPass": 0,
+ "VideoTwoPass": 0,
+ "x264Option": "",
+ "x264UseAdvancedOptions": 0
+ },
+ {
+ "AudioAllowAACPass": 1,
+ "AudioAllowAC3Pass": 1,
+ "AudioAllowDTSHDPass": 1,
+ "AudioAllowDTSPass": 1,
+ "AudioAllowMP3Pass": 1,
+ "AudioEncoderFallback": "ac3",
+ "AudioList": [
+ {
+ "AudioBitrate": "128",
+ "AudioEncoder": "aac",
+ "AudioMixdown": "dpl2",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ }
+ ],
+ "ChapterMarkers": 0,
+ "Default": 0,
+ "FileFormat": "mp4",
+ "Folder": false,
+ "Mp4HttpOptimize": 0,
+ "Mp4iPodCompatible": 0,
+ "PictureAutoCrop": 1,
+ "PictureBottomCrop": 0,
+ "PictureDeblock": 0,
+ "PictureDecomb": 0,
+ "PictureDecombCustom": "",
+ "PictureDecombDeinterlace": 1,
+ "PictureDeinterlace": 0,
+ "PictureDeinterlaceCustom": "",
+ "PictureDenoiseCustom": "",
+ "PictureDenoiseFilter": "off",
+ "PictureDetelecine": 0,
+ "PictureDetelecineCustom": "",
+ "PictureHeight": 720,
+ "PictureKeepRatio": 1,
+ "PictureLeftCrop": 0,
+ "PictureModulus": 2,
+ "PicturePAR": "off",
+ "PictureRightCrop": 0,
+ "PictureTopCrop": 0,
+ "PictureWidth": 1280,
+ "PresetDescription": "HandBrake's preset for Windows Phone 8 devices",
+ "PresetName": "Windows Phone 8",
+ "Type": 0,
+ "UsesPictureFilters": 1,
+ "UsesPictureSettings": 1,
+ "VideoAvgBitrate": "2500",
+ "VideoEncoder": "x264",
+ "VideoFramerate": "30",
+ "VideoFramerateMode": "pfr",
+ "VideoGrayScale": 0,
+ "VideoLevel": "3.1",
+ "VideoOptionExtra": "",
+ "VideoPreset": "medium",
+ "VideoProfile": "main",
+ "VideoQualitySlider": 22.0,
+ "VideoQualityType": 2,
+ "VideoTune": "",
+ "VideoTurboTwoPass": 0,
+ "VideoTwoPass": 0,
+ "x264Option": "",
+ "x264UseAdvancedOptions": 0
+ }
+ ],
+ "Default": 0,
+ "Folder": true,
+ "PresetName": "Devices",
+ "Type": 0
+ },
+ {
+ "ChildrenArray": [
+ {
+ "AudioAllowAACPass": 1,
+ "AudioAllowAC3Pass": 1,
+ "AudioAllowDTSHDPass": 1,
+ "AudioAllowDTSPass": 1,
+ "AudioAllowMP3Pass": 1,
+ "AudioEncoderFallback": "ac3",
+ "AudioList": [
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "aac",
+ "AudioMixdown": "dpl2",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ }
+ ],
+ "ChapterMarkers": 1,
+ "Default": 1,
+ "FileFormat": "mp4",
+ "Folder": false,
+ "Mp4HttpOptimize": 0,
+ "Mp4iPodCompatible": 0,
+ "PictureAutoCrop": 1,
+ "PictureBottomCrop": 0,
+ "PictureDeblock": 0,
+ "PictureDecomb": 0,
+ "PictureDecombCustom": "",
+ "PictureDecombDeinterlace": 1,
+ "PictureDeinterlace": 0,
+ "PictureDeinterlaceCustom": "",
+ "PictureDenoiseCustom": "",
+ "PictureDenoiseFilter": "off",
+ "PictureDetelecine": 0,
+ "PictureDetelecineCustom": "",
+ "PictureHeight": 0,
+ "PictureKeepRatio": 0,
+ "PictureLeftCrop": 0,
+ "PictureModulus": 2,
+ "PicturePAR": "loose",
+ "PictureRightCrop": 0,
+ "PictureTopCrop": 0,
+ "PictureWidth": 0,
+ "PresetDescription": "HandBrake's normal, default settings.",
+ "PresetName": "Normal",
+ "Type": 0,
+ "UsesPictureFilters": 1,
+ "UsesPictureSettings": 1,
+ "VideoAvgBitrate": "2500",
+ "VideoEncoder": "x264",
+ "VideoFramerate": "auto",
+ "VideoFramerateMode": "vfr",
+ "VideoGrayScale": 0,
+ "VideoLevel": "4.0",
+ "VideoOptionExtra": "",
+ "VideoPreset": "veryfast",
+ "VideoProfile": "main",
+ "VideoQualitySlider": 20.0,
+ "VideoQualityType": 2,
+ "VideoTune": "",
+ "VideoTurboTwoPass": 0,
+ "VideoTwoPass": 0,
+ "x264Option": "",
+ "x264UseAdvancedOptions": 0
+ },
+ {
+ "AudioAllowAACPass": 1,
+ "AudioAllowAC3Pass": 1,
+ "AudioAllowDTSHDPass": 1,
+ "AudioAllowDTSPass": 1,
+ "AudioAllowMP3Pass": 1,
+ "AudioEncoderFallback": "ac3",
+ "AudioList": [
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "aac",
+ "AudioMixdown": "dpl2",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ },
+ {
+ "AudioBitrate": "160",
+ "AudioEncoder": "copy:ac3",
+ "AudioMixdown": "none",
+ "AudioSamplerate": "auto",
+ "AudioTrack": 1,
+ "AudioTrackDRCSlider": 0.0,
+ "AudioTrackGainSlider": 0.0
+ }
+ ],
+ "ChapterMarkers": 1,
+ "Default": 0,
+ "FileFormat": "mp4",
+ "Folder": false,
+ "Mp4HttpOptimize": 0,
+ "Mp4iPodCompatible": 0,
+ "PictureAutoCrop": 1,
+ "PictureBottomCrop": 0,
+ "PictureDeblock": 0,
+ "PictureDecomb": 2,
+ "PictureDecombCustom": "",
+ "PictureDecombDeinterlace": 1,
+ "PictureDeinterlace": 0,
+ "PictureDeinterlaceCustom": "",
+ "PictureDenoiseCustom": "",
+ "PictureDenoiseFilter": "off",
+ "PictureDetelecine": 0,
+ "PictureDetelecineCustom": "",
+ "PictureHeight": 0,
+ "PictureKeepRatio": 0,
+ "PictureLeftCrop": 0,
+ "PictureModulus": 2,
+ "PicturePAR": "loose",
+ "PictureRightCrop": 0,
+ "PictureTopCrop": 0,
+ "PictureWidth": 0,
+ "PresetDescription": "HandBrake's general-purpose preset for High Profile H.264 video.",
+ "PresetName": "High Profile",
+ "Type": 0,
+ "UsesPictureFilters": 1,
+ "UsesPictureSettings": 1,
+ "VideoAvgBitrate": "2500",
+ "VideoEncoder": "x264",
+ "VideoFramerate": "auto",
+ "VideoFramerateMode": "vfr",
+ "VideoGrayScale": 0,
+ "VideoLevel": "4.1",
+ "VideoOptionExtra": "",
+ "VideoPreset": "medium",
+ "VideoProfile": "high",
+ "VideoQualitySlider": 20.0,
+ "VideoQualityType": 2,
+ "VideoTune": "",
+ "VideoTurboTwoPass": 0,
+ "VideoTwoPass": 0,
+ "x264Option": "",
+ "x264UseAdvancedOptions": 0
+ }
+ ],
+ "Default": 0,
+ "Folder": true,
+ "PresetName": "Regular",
+ "Type": 0
+ }
+ ]
diff --git a/libhb/preset_template.json b/libhb/preset_template.json
new file mode 100644
index 000000000..30449e437
--- /dev/null
+++ b/libhb/preset_template.json
@@ -0,0 +1,104 @@
+ {
+ "AudioAllowMP3Pass": false,
+ "AudioAllowAACPass": false,
+ "AudioAllowAC3Pass": true,
+ "AudioAllowDTSPass": false,
+ "AudioAllowDTSHDPass": false,
+ "AudioAllowEAC3Pass": false,
+ "AudioAllowFLACPass": false,
+ "AudioAllowTRUEHDPass": false,
+ "AudioCopyMask": [
+ ],
+ "AudioEncoderFallback": "ac3",
+ "AudioLanguageList": [
+ "und"
+ ],
+ "AudioList": [
+ {
+ "AudioBitrate": "192",
+ "AudioCompressionLevel": -1.0,
+ "AudioDitherMethod": "auto",
+ "AudioEncoder": "copy:ac3",
+ "AudioMixdown": "dpl2",
+ "AudioNormalizeMixLevel": false,
+ "AudioSamplerate": "auto",
+ "AudioTrackQualityEnable": false,
+ "AudioTrackQuality": -1.0,
+ "AudioTrackGainSlider": 0.0,
+ "AudioTrackDRCSlider": 0.0
+ }
+ ],
+ "AudioSecondaryEncoderMode": true,
+ "AudioTrackSelectionBehavior": "first",
+ "ChapterMarkers": true,
+ "Default": false,
+ "FileFormat": "mp4",
+ "Folder": false,
+ "Mp4HttpOptimize": false,
+ "Mp4iPodCompatible": false,
+ "PictureAutoCrop": true,
+ "PictureBottomCrop": 0,
+ "PictureLeftCrop": 0,
+ "PictureRightCrop": 0,
+ "PictureTopCrop": 0,
+ "PictureDARWidth": 0,
+ "PictureDeblock": 0,
+ "PictureDecomb": "off",
+ "PictureDecombCustom": "",
+ "PictureDecombDeinterlace": true,
+ "PictureDeinterlace": "off",
+ "PictureDeinterlaceCustom": "",
+ "PictureDenoiseCustom": "",
+ "PictureDenoiseFilter": "off",
+ "PictureDenoisePreset": "medium",
+ "PictureDenoiseTune": "none",
+ "PictureDetelecine": "off",
+ "PictureDetelecineCustom": "",
+ "PictureItuPAR": false,
+ "PictureKeepRatio": true,
+ "PictureLooseCrop": false,
+ "PictureModulus": 2,
+ "PicturePAR": "loose",
+ "PicturePARWidth": 853,
+ "PicturePARHeight": 720,
+ "PictureRotate": 0,
+ "PictureWidth": 0,
+ "PictureHeight": 0,
+ "PictureForceHeight": 0,
+ "PictureForceWidth": 0,
+ "PresetDescription": "",
+ "PresetName": "Name Missing",
+ "Type": 1,
+ "UsesPictureFilters": true,
+ "UsesPictureSettings": 2,
+ "SubtitleAddCC": false,
+ "SubtitleAddForeignAudioSearch": false,
+ "SubtitleAddForeignAudioSubtitle": false,
+ "SubtitleBurnBehavior": "none",
+ "SubtitleBurnBDSub": false,
+ "SubtitleBurnDVDSub": false,
+ "SubtitleLanguageList": [
+ ],
+ "SubtitleTrackSelectionBehavior": "none",
+ "VideoAvgBitrate": 1800,
+ "VideoColorMatrixCode": 0,
+ "VideoEncoder": "x264",
+ "VideoFramerate": "auto",
+ "VideoFramerateMode": "vfr",
+ "VideoGrayScale": false,
+ "VideoHWDecode": false,
+ "VideoScaler": "swscale",
+ "VideoPreset": "medium",
+ "VideoTune": "none",
+ "VideoProfile": "auto",
+ "VideoLevel": "auto",
+ "VideoOptionExtra": "",
+ "VideoQualityType": 2,
+ "VideoQualitySlider": 20.0,
+ "VideoQSVDecode": false,
+ "VideoQSVAsyncDepth": 4,
+ "VideoTwoPass": false,
+ "VideoTurboTwoPass": false,
+ "x264Option": "",
+ "x264UseAdvancedOptions": false
+ }