WinGui: Fix another instance of the Caliburn vs Json.net sillyness where objects...
[HandBrake.git] / libhb / preset.c
blobec0c9b26ca354877749e65859352afec2e7a6b45
1 /* preset.c
3 Copyright (c) 2003-2015 HandBrake Team
4 This file is part of the HandBrake source code
5 Homepage: <http://handbrake.fr/>.
6 It may be used under the terms of the GNU General Public License v2.
7 For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
8 */
10 #include "builtin_presets.h"
11 #include "hb.h"
12 #include "hb_dict.h"
13 #include "plist.h"
15 #if defined(SYS_LINUX)
16 #define HB_PRESET_PLIST_FILE "ghb/presets"
17 #define HB_PRESET_JSON_FILE "ghb/presets.json"
18 #elif defined(SYS_MINGW)
19 #define HB_PRESET_PLIST_FILE "HandBrake\\user_presets.xml"
20 #define HB_PRESET_JSON_FILE "HandBrake\\user_presets.json"
21 #elif defined(SYS_DARWIN)
22 #define HB_PRESET_PLIST_FILE "HandBrake/UserPresets.plist"
23 #define HB_PRESET_JSON_FILE "HandBrake/UserPresets.json"
24 #endif
26 int hb_preset_version_major;
27 int hb_preset_version_minor;
28 int hb_preset_version_micro;
30 static hb_value_t *hb_preset_template = NULL;
31 static hb_value_t *hb_presets = NULL;
32 static hb_value_t *hb_presets_builtin = NULL;
34 static void preset_clean(hb_value_t *preset, hb_value_t *template);
35 static void preset_import(hb_value_t *preset, int major, int minor, int micro);
37 enum
39 PRESET_DO_SUCCESS,
40 PRESET_DO_FAIL,
41 PRESET_DO_PARTIAL,
42 PRESET_DO_NEXT,
43 PRESET_DO_SKIP,
44 PRESET_DO_DELETE,
45 PRESET_DO_DONE
48 typedef struct
50 hb_preset_index_t path;
51 } preset_do_context_t;
53 typedef struct
55 preset_do_context_t do_ctx;
56 hb_value_t *template;
57 } preset_clean_context_t;
59 typedef struct
61 preset_do_context_t do_ctx;
62 int major;
63 int minor;
64 int micro;
65 } preset_import_context_t;
67 typedef struct
69 preset_do_context_t do_ctx;
70 const char *name;
71 int recurse;
72 int last_match_idx;
73 } preset_search_context_t;
75 typedef int (*preset_do_f)(hb_value_t *preset, preset_do_context_t *ctx);
77 static int preset_cmp_idx(hb_value_t *preset, int idx, const char *name)
79 const char *next, *preset_name;
80 int ii, len;
82 // Strip leading '/'
83 if (name[0] == '/')
84 name++;
86 // Find the part of the "name" path we want to match.
87 for (ii = 0; ii < idx; ii++)
89 next = strchr(name, '/');
90 if (next == NULL)
91 return PRESET_DO_SKIP;
92 next++;
93 name = next;
96 // Find the end of the part we want to match
97 next = strchr(name, '/');
98 if (next != NULL)
99 len = next - name;
100 else
101 len = strlen(name);
102 if (len <= 0)
103 return PRESET_DO_SKIP;
105 preset_name = hb_value_get_string(hb_dict_get(preset, "PresetName"));
106 if (strlen(preset_name) > len)
107 len = strlen(preset_name);
109 // If match found and it's the last component of the "name", success!
110 if (!strncmp(name, preset_name, len))
112 if (name[len] == 0)
113 return PRESET_DO_SUCCESS;
114 else
115 return PRESET_DO_PARTIAL;
117 return PRESET_DO_NEXT;
120 static int do_preset_search(hb_value_t *preset, preset_do_context_t *do_ctx)
122 preset_search_context_t *ctx = (preset_search_context_t*)do_ctx;
123 int idx, result;
125 idx = ctx->do_ctx.path.depth - 1;
126 if (ctx->last_match_idx >= 0 && idx > ctx->last_match_idx)
128 // If there was a previous partial match, try to continue the match
129 idx -= ctx->last_match_idx;
132 result = preset_cmp_idx(preset, idx, ctx->name);
133 if (ctx->recurse && result == PRESET_DO_SKIP)
135 result = preset_cmp_idx(preset, 0, ctx->name);
136 ctx->last_match_idx = idx;
138 if (result == PRESET_DO_PARTIAL)
140 return PRESET_DO_NEXT;
142 else
144 ctx->last_match_idx = -1;
147 return result;
150 static int do_preset_import(hb_value_t *preset, preset_do_context_t *do_ctx)
152 preset_import_context_t *ctx = (preset_import_context_t*)do_ctx;
153 preset_import(preset, ctx->major, ctx->minor, ctx->micro);
154 return PRESET_DO_NEXT;
157 static int do_preset_clean(hb_value_t *preset, preset_do_context_t *do_ctx)
159 preset_clean_context_t *ctx = (preset_clean_context_t*)do_ctx;
160 preset_clean(preset, ctx->template);
161 return PRESET_DO_NEXT;
164 static int do_delete_builtin(hb_value_t *preset, preset_do_context_t *ctx)
166 if (hb_value_get_int(hb_dict_get(preset, "Type")) == 0)
167 return PRESET_DO_DELETE;
168 return PRESET_DO_NEXT;
171 static int do_clear_default(hb_value_t *preset, preset_do_context_t *ctx)
173 hb_dict_set(preset, "Default", hb_value_bool(0));
174 return PRESET_DO_NEXT;
177 static int do_find_default(hb_value_t *preset, preset_do_context_t *ctx)
179 if (!hb_value_get_bool(hb_dict_get(preset, "Folder")) &&
180 hb_value_get_bool(hb_dict_get(preset, "Default")))
182 return PRESET_DO_SUCCESS;
184 return PRESET_DO_NEXT;
187 static int presets_do(preset_do_f do_func, hb_value_t *preset,
188 preset_do_context_t *ctx)
190 int result;
191 hb_value_t *next;
193 if (hb_value_type(preset) == HB_VALUE_TYPE_ARRAY)
195 // An array of presets, clean each one
196 int ii;
198 for (ii = 0; ii < hb_value_array_len(preset); )
200 ctx->path.index[ctx->path.depth-1] = ii;
201 next = hb_value_array_get(preset, ii);
202 result = presets_do(do_func, next, ctx);
203 if (result == PRESET_DO_DELETE)
205 hb_value_array_remove(preset, ii);
206 continue;
208 ii++;
209 if (result == PRESET_DO_SKIP)
210 return PRESET_DO_NEXT;
211 if (result != PRESET_DO_NEXT)
212 return result;
214 return PRESET_DO_NEXT;
216 else if (hb_value_type(preset) == HB_VALUE_TYPE_DICT &&
217 hb_dict_get(preset, "VersionMajor") != NULL)
219 // A packaged preset list
220 next = hb_dict_get(preset, "PresetList");
221 return presets_do(do_func, next, ctx);
223 else if (hb_value_type(preset) == HB_VALUE_TYPE_DICT &&
224 hb_value_get_bool(hb_dict_get(preset, "Folder")))
226 // Perform do_func on the folder...
227 result = do_func(preset, ctx);
228 if (result != PRESET_DO_NEXT)
229 return result;
231 // Then perform preset action on the children of the folder
232 ctx->path.depth++;
233 next = hb_dict_get(preset, "ChildrenArray");
234 result = presets_do(do_func, next, ctx);
235 if (result == PRESET_DO_SUCCESS)
236 return result;
237 ctx->path.depth--;
238 return result;
240 else if (hb_value_type(preset) == HB_VALUE_TYPE_DICT &&
241 hb_dict_get(preset, "PresetName") != NULL)
243 // An individual, non-folder, preset
244 return do_func(preset, ctx);
246 else
248 hb_error("Error: invalid preset format in presets_do()");
249 return PRESET_DO_NEXT;
251 return PRESET_DO_DONE;
254 hb_preset_index_t* hb_preset_index_init(const int *index, int depth)
256 hb_preset_index_t *path;
257 path = malloc(sizeof(hb_preset_index_t));
258 path->depth = depth;
259 if (index != NULL)
260 memcpy(path->index, index, depth * sizeof(int));
261 return path;
264 hb_preset_index_t* hb_preset_index_dup(const hb_preset_index_t *path)
266 if (path == NULL)
267 return NULL;
268 return hb_preset_index_init(path->index, path->depth);
271 void hb_preset_index_append(hb_preset_index_t *dst,
272 const hb_preset_index_t *src)
274 int ii;
275 for (ii = 0; ii < src->depth &&
276 dst->depth < HB_MAX_PRESET_FOLDER_DEPTH; ii++, dst->depth++)
278 dst->index[dst->depth] = src->index[ii];
282 static int get_job_mux(hb_dict_t *job_dict)
284 int mux;
286 hb_dict_t *dest_dict = hb_dict_get(job_dict, "Destination");
287 hb_value_t *mux_value = hb_dict_get(dest_dict, "Mux");
288 if (hb_value_type(mux_value) == HB_VALUE_TYPE_STRING)
290 mux = hb_container_get_from_name(hb_value_get_string(mux_value));
291 if (mux == 0)
292 mux = hb_container_get_from_extension(
293 hb_value_get_string(mux_value));
295 else
297 mux = hb_value_get_int(mux_value);
299 hb_container_t *container = hb_container_get_from_format(mux);
300 if (container == NULL)
302 char *str = hb_value_get_string_xform(mux_value);
303 hb_error("Invalid container (%s)", str);
304 free(str);
305 return HB_MUX_INVALID;
307 return mux;
310 static hb_value_t* get_audio_copy_mask(const hb_dict_t * preset, int *mask)
312 int copy_mask = 0;
313 hb_value_array_t *out_copy_mask, *in_copy_mask;
315 if (mask != NULL)
316 *mask = 0;
317 in_copy_mask = hb_dict_get(preset, "AudioCopyMask");
318 out_copy_mask = hb_value_array_init();
319 if (in_copy_mask != NULL)
321 int count, ii;
322 count = hb_value_array_len(in_copy_mask);
323 for (ii = 0; ii < count; ii++)
325 int codec;
326 hb_value_t *value;
327 value = hb_value_array_get(in_copy_mask, ii);
328 if (hb_value_type(value) == HB_VALUE_TYPE_STRING)
330 char *tmp = NULL;
331 const char * s = hb_value_get_string(value);
332 // Only codecs that start with 'copy:' can be copied
333 if (strncmp(s, "copy:", 5))
335 s = tmp = hb_strdup_printf("copy:%s", s);
337 codec = hb_audio_encoder_get_from_name(s);
338 if (codec == 0)
340 hb_error("Invalid audio codec in autopassthru copy mask (%s)", s);
341 hb_error("Codec name is invalid or can not be copied");
342 free(tmp);
343 hb_value_free(&out_copy_mask);
344 return NULL;
346 free(tmp);
348 else
350 codec = hb_value_get_int(value);
352 hb_value_array_append(out_copy_mask, hb_value_string(
353 hb_audio_encoder_get_short_name(codec)));
354 copy_mask |= codec;
357 if (mask != NULL)
358 *mask = copy_mask;
359 return out_copy_mask;
362 static hb_dict_t * source_audio_track_used(hb_dict_t *track_dict, int track)
364 // Kind of hacky, but keys must be strings
365 char key[8];
366 snprintf(key, sizeof(key), "%d", track);
368 hb_dict_t *used = hb_dict_get(track_dict, key);
369 if (used == NULL)
371 used = hb_dict_init();
372 hb_dict_set(track_dict, key, used);
374 return used;
377 // Find a source audio track matching given language
378 static int find_audio_track(const hb_title_t *title,
379 const char *lang, int start)
381 hb_audio_config_t * audio;
382 int ii, count;
384 count = hb_list_count(title->list_audio);
385 for (ii = start; ii < count; ii++)
387 audio = hb_list_audio_config_item(title->list_audio, ii);
388 // Ignore secondary audio types
389 if ((audio->lang.type == HB_AUDIO_TYPE_NONE ||
390 audio->lang.type == HB_AUDIO_TYPE_NORMAL) &&
391 (!strcmp(lang, audio->lang.iso639_2) || !strcmp(lang, "und")))
393 return ii;
396 return -1;
399 static int validate_audio_encoders(const hb_dict_t *preset)
401 hb_value_array_t * encoder_list = hb_dict_get(preset, "AudioList");
402 int count = hb_value_array_len(encoder_list);
403 int ii;
404 for (ii = 0; ii < count; ii++)
406 hb_value_t *audio_dict = hb_value_array_get(encoder_list, ii);
407 hb_value_t *value;
408 int codec, mix, sr;
409 value = hb_dict_get(audio_dict, "AudioEncoder");
410 if (hb_value_type(value) == HB_VALUE_TYPE_STRING)
412 codec = hb_audio_encoder_get_from_name(hb_value_get_string(value));
414 else
416 codec = hb_value_get_int(value);
418 if (hb_audio_encoder_get_from_codec(codec) == NULL)
420 char *str = hb_value_get_string_xform(value);
421 hb_error("Invalid audio encoder (%s)", str);
422 free(str);
423 return -1;
426 value = hb_dict_get(audio_dict, "AudioMixdown");
427 if (hb_value_type(value) == HB_VALUE_TYPE_STRING)
429 mix = hb_audio_encoder_get_from_name(hb_value_get_string(value));
431 else
433 mix = hb_value_get_int(value);
435 if (hb_mixdown_get_from_mixdown(mix) == NULL)
437 char *str = hb_value_get_string_xform(value);
438 hb_error("Invalid audio mixdown (%s)", str);
439 free(str);
440 return -1;
443 value = hb_dict_get(audio_dict, "AudioSamplerate");
444 if (hb_value_type(value) == HB_VALUE_TYPE_STRING)
446 const char *str = hb_value_get_string(value);
447 if (!strcasecmp(str, "source") ||
448 !strcasecmp(str, "auto") ||
449 !strcasecmp(str, "same as source"))
451 sr = 0;
453 else
455 sr = hb_audio_samplerate_get_from_name(str);
458 else
460 sr = hb_value_get_int(value);
462 if (sr != 0 && hb_audio_samplerate_get_name(sr) == NULL)
464 char *str = hb_value_get_string_xform(value);
465 hb_error("Invalid audio samplerate (%s)", str);
466 free(str);
467 return -1;
470 return 0;
473 static int sanitize_audio_codec(int in_codec, int out_codec,
474 int copy_mask, int fallback, int mux)
476 int codec = out_codec;
477 if (out_codec == HB_ACODEC_AUTO_PASS)
479 codec = hb_autopassthru_get_encoder(in_codec, copy_mask, fallback, mux);
481 else if ((out_codec & HB_ACODEC_PASS_FLAG) &&
482 !(in_codec & out_codec & HB_ACODEC_PASS_MASK))
484 codec = hb_audio_encoder_get_fallback_for_passthru(out_codec);
485 if (codec == 0)
486 codec = fallback;
489 // Check that encoder is valid for mux
490 const hb_encoder_t *encoder = NULL;
491 while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL)
493 if (encoder->codec == codec &&
494 !(encoder->muxers & mux))
496 codec = hb_audio_encoder_get_default(mux);
497 break;
500 if (codec == 0)
501 codec = hb_audio_encoder_get_default(mux);
502 return codec;
505 static void add_audio_for_lang(hb_value_array_t *list, const hb_dict_t *preset,
506 hb_title_t *title, int mux, int copy_mask,
507 int fallback, const char *lang,
508 int behavior, int mode, hb_dict_t *track_dict)
510 hb_value_array_t * encoder_list = hb_dict_get(preset, "AudioList");
511 int count = hb_value_array_len(encoder_list);
512 int track = find_audio_track(title, lang, 0);
513 int current_mode = 0;
514 while (track >= 0)
516 char key[8];
517 snprintf(key, sizeof(key), "%d", track);
519 count = current_mode ? 1 : count;
520 int ii;
521 for (ii = 0; ii < count; ii++)
523 // Check if this source track has already been added using these
524 // same encoder settings. If so, continue to next track.
525 hb_dict_t *used = source_audio_track_used(track_dict, ii);
526 if (hb_value_get_bool(hb_dict_get(used, key)))
527 continue;
528 hb_dict_set(used, key, hb_value_bool(1));
530 // Create new audio output track settings
531 hb_dict_t *audio_dict = hb_dict_init();
532 hb_value_t *acodec_value;
533 hb_dict_t *encoder_dict = hb_value_array_get(encoder_list, ii);
534 int out_codec;
536 acodec_value = hb_dict_get(encoder_dict, "AudioEncoder");
537 if (hb_value_type(acodec_value) == HB_VALUE_TYPE_STRING)
539 out_codec = hb_audio_encoder_get_from_name(
540 hb_value_get_string(acodec_value));
542 else
544 out_codec = hb_value_get_int(acodec_value);
546 // Save the encoder value before sanitizing. This value is
547 // useful to the frontends.
548 hb_dict_set(audio_dict, "PresetEncoder",
549 hb_value_string(hb_audio_encoder_get_short_name(out_codec)));
551 hb_audio_config_t *aconfig;
552 aconfig = hb_list_audio_config_item(title->list_audio, track);
553 out_codec = sanitize_audio_codec(aconfig->in.codec, out_codec,
554 copy_mask, fallback, mux);
555 hb_dict_set(audio_dict, "Track", hb_value_int(track));
556 hb_dict_set(audio_dict, "Encoder", hb_value_string(
557 hb_audio_encoder_get_short_name(out_codec)));
558 if (hb_dict_get(encoder_dict, "AudioTrackName") != NULL)
560 hb_dict_set(audio_dict, "Name", hb_value_dup(
561 hb_dict_get(encoder_dict, "AudioTrackName")));
563 if (!(out_codec & HB_ACODEC_PASS_FLAG))
565 if (hb_dict_get(encoder_dict, "AudioTrackGainSlider") != NULL)
567 hb_dict_set(audio_dict, "Gain", hb_value_dup(
568 hb_dict_get(encoder_dict, "AudioTrackGainSlider")));
570 if (hb_dict_get(encoder_dict, "AudioTrackDRCSlider") != NULL)
572 hb_dict_set(audio_dict, "DRC", hb_value_dup(
573 hb_dict_get(encoder_dict, "AudioTrackDRCSlider")));
575 if (hb_dict_get(encoder_dict, "AudioMixdown") != NULL)
577 hb_dict_set(audio_dict, "Mixdown", hb_value_dup(
578 hb_dict_get(encoder_dict, "AudioMixdown")));
580 if (hb_dict_get(encoder_dict, "AudioNormalizeMixLevel") != NULL)
582 hb_dict_set(audio_dict, "NormalizeMixLevel", hb_value_dup(
583 hb_dict_get(encoder_dict, "AudioNormalizeMixLevel")));
585 if (hb_dict_get(encoder_dict, "AudioDitherMethod") != NULL)
587 hb_dict_set(audio_dict, "DitherMethod", hb_value_dup(
588 hb_dict_get(encoder_dict, "AudioDitherMethod")));
590 if (hb_dict_get(encoder_dict, "AudioSamplerate") != NULL)
592 hb_dict_set(audio_dict, "Samplerate", hb_value_dup(
593 hb_dict_get(encoder_dict, "AudioSamplerate")));
595 if (hb_dict_get(encoder_dict, "AudioCompressionLevel") != NULL)
597 hb_dict_set(audio_dict, "CompressionLevel", hb_value_dup(
598 hb_dict_get(encoder_dict, "AudioCompressionLevel")));
600 if (hb_value_get_bool(hb_dict_get(encoder_dict,
601 "AudioTrackQualityEnable")))
603 hb_dict_set(audio_dict, "Quality", hb_value_xform(
604 hb_dict_get(encoder_dict, "AudioTrackQuality"),
605 HB_VALUE_TYPE_DOUBLE));
607 else
609 hb_dict_set(audio_dict, "Bitrate", hb_value_xform(
610 hb_dict_get(encoder_dict, "AudioBitrate"),
611 HB_VALUE_TYPE_INT));
614 hb_value_array_append(list, audio_dict);
616 if (behavior == 2)
617 track = find_audio_track(title, lang, track + 1);
618 else
619 break;
623 // This function assumes that Mux has already been initialized in
624 // the job_dict
625 int hb_preset_job_add_audio(hb_handle_t *h, int title_index,
626 const hb_dict_t *preset, hb_dict_t *job_dict)
628 hb_title_t *title = hb_find_title_by_index(h, title_index);
629 if (title == NULL)
631 // Can't create audio track list without knowing source audio tracks
632 hb_error("Invalid title index (%d)", title_index);
633 return -1;
635 if (hb_list_count(title->list_audio) <= 0)
637 // Source has no audio
638 return 0;
641 int mux = get_job_mux(job_dict);
642 if (mux == HB_MUX_INVALID)
644 return -1;
647 hb_dict_t *audio_dict = hb_dict_get(job_dict, "Audio");
648 if (audio_dict == NULL)
650 audio_dict = hb_dict_init();
651 hb_dict_set(job_dict, "Audio", audio_dict);
653 int copy_mask;
654 hb_value_t *copy_mask_array = get_audio_copy_mask(preset, &copy_mask);
655 if (copy_mask_array == NULL)
657 return -1;
659 int fallback = 0;
660 hb_dict_set(audio_dict, "CopyMask", copy_mask_array);
661 hb_value_t *fallback_value = hb_dict_get(preset, "AudioEncoderFallback");
662 if (fallback_value != NULL)
664 hb_dict_set(audio_dict, "FallbackEncoder",
665 hb_value_dup(fallback_value));
666 if (hb_value_type(fallback_value) == HB_VALUE_TYPE_STRING)
668 const char * s = hb_value_get_string(fallback_value);
669 fallback = hb_audio_encoder_get_from_name(s);
670 if (fallback == 0)
672 hb_error("Invalid fallback audio codec (%s)", s);
673 return -1;
676 else
678 fallback = hb_value_get_int(fallback_value);
681 if (validate_audio_encoders(preset) < 0)
682 return -1;
684 hb_value_array_t *list = hb_dict_get(audio_dict, "AudioList");
685 if (list == NULL)
687 list = hb_value_array_init();
688 hb_dict_set(audio_dict, "AudioList", list);
691 int behavior = 1; // default first
692 const char *s;
693 s = hb_value_get_string(hb_dict_get(preset, "AudioTrackSelectionBehavior"));
694 if (s != NULL)
696 if (!strcasecmp(s, "none"))
697 return 0;
698 else if (!strcasecmp(s, "all"))
699 behavior = 2;
702 // Create hash that is used to track which tracks have been already added
703 // We do not want to add the same track with the same settings twice
704 hb_dict_t *track_dict = hb_dict_init();
706 // Add tracks for all languages in the language list
707 int mode;
708 hb_value_array_t *lang_list = hb_dict_get(preset, "AudioLanguageList");
709 mode = hb_value_get_bool(hb_dict_get(preset, "AudioSecondaryEncoderMode"));
710 int count = hb_value_array_len(lang_list);
711 int ii;
712 for (ii = 0; ii < count; ii++)
714 const char *lang;
715 lang = hb_value_get_string(hb_value_array_get(lang_list, ii));
716 add_audio_for_lang(list, preset, title, mux, copy_mask, fallback,
717 lang, behavior, mode, track_dict);
719 // If no audios found, try "und" language option
720 if (hb_value_array_len(list) <= 0)
722 add_audio_for_lang(list, preset, title, mux, copy_mask, fallback,
723 "und", behavior, mode, track_dict);
725 hb_dict_free(&track_dict);
726 return 0;
729 // Find a source audio track matching given language
730 static int find_subtitle_track(const hb_title_t *title,
731 const char *lang, int start)
733 hb_subtitle_t * subtitle;
734 int ii, count;
736 count = hb_list_count(title->list_subtitle);
737 for (ii = start; ii < count; ii++)
739 subtitle = hb_list_item(title->list_subtitle, ii);
740 if (!strcmp(lang, subtitle->iso639_2) || !strcmp(lang, "und"))
742 return ii;
745 return -1;
748 static void add_subtitle(hb_value_array_t *list, int track,
749 int make_default, int force, int burn)
751 hb_dict_t *subtitle_dict = hb_dict_init();
752 hb_dict_set(subtitle_dict, "Track", hb_value_int(track));
753 hb_dict_set(subtitle_dict, "Default", hb_value_bool(make_default));
754 hb_dict_set(subtitle_dict, "Forced", hb_value_bool(force));
755 hb_dict_set(subtitle_dict, "Burn", hb_value_bool(burn));
756 hb_value_array_append(list, subtitle_dict);
759 typedef struct subtitle_behavior_s
761 int one;
762 int burn_foreign;
763 int make_default;
764 int burn_first;
765 int burn_dvd;
766 int burn_bd;
767 int one_burned;
768 uint8_t *used;
769 } subtitle_behavior_t;
771 static void add_subtitle_for_lang(hb_value_array_t *list, hb_title_t *title,
772 int mux, const char *lang,
773 subtitle_behavior_t *behavior)
775 int t;
776 t = find_subtitle_track(title, lang, 0);
777 for (t = find_subtitle_track(title, lang, 0);
778 t >= 0;
779 t = behavior->one ? -1 : find_subtitle_track(title, lang, t + 1))
781 if (behavior->used[t])
783 if (behavior->one)
784 break;
785 continue;
787 int burn, make_default;
788 hb_subtitle_t *subtitle;
789 subtitle = hb_list_item(title->list_subtitle, t);
790 burn = !behavior->one_burned &&
791 ((subtitle->source == VOBSUB && behavior->burn_dvd) ||
792 (subtitle->source == PGSSUB && behavior->burn_bd) ||
793 !hb_subtitle_can_pass(subtitle->source, mux) ||
794 behavior->burn_first || behavior->burn_foreign);
795 make_default = !burn && behavior->make_default;
796 behavior->burn_first &= !burn;
797 behavior->one_burned |= burn;
798 behavior->used[t] = 1;
799 add_subtitle(list, t, make_default, 0 /*!force*/, burn);
803 // This function assumes that the AudioList and Mux have already been
804 // initialized in the job_dict
805 int hb_preset_job_add_subtitles(hb_handle_t *h, int title_index,
806 const hb_dict_t *preset, hb_dict_t *job_dict)
808 hb_title_t *title = hb_find_title_by_index(h, title_index);
809 if (title == NULL)
811 // Can't create subtitle track list without knowing source
812 hb_error("Invalid title index (%d)", title_index);
813 return -1;
816 int mux = get_job_mux(job_dict);
817 if (mux == HB_MUX_INVALID)
819 return -1;
822 // Get the language of the first audio output track
823 // Needed for subtitle track selection
824 hb_dict_t *audio_dict = hb_dict_get(job_dict, "Audio");
825 hb_value_array_t *audio_list = hb_dict_get(audio_dict, "AudioList");
826 const char *first_audio_lang = NULL;
827 if (hb_value_array_len(audio_list) > 0)
829 int track;
830 hb_value_t *audio = hb_value_array_get(audio_list, 0);
831 track = hb_value_get_int(hb_dict_get(audio, "Track"));
832 if (hb_list_count(title->list_audio) > track)
834 hb_audio_config_t *aconfig;
835 aconfig = hb_list_audio_config_item(title->list_audio, track);
836 if (aconfig != NULL)
837 first_audio_lang = aconfig->lang.iso639_2;
841 int source_subtitle_count = hb_list_count(title->list_subtitle);
842 if (source_subtitle_count == 0)
843 return 0;
845 hb_dict_t *subtitle_dict = hb_dict_get(job_dict, "Subtitle");
846 if (subtitle_dict == NULL)
848 subtitle_dict = hb_dict_init();
849 hb_dict_set(job_dict, "Subtitle", subtitle_dict);
851 hb_value_array_t *list = hb_dict_get(subtitle_dict, "SubtitleList");
852 if (list == NULL)
854 list = hb_value_array_init();
855 hb_dict_set(subtitle_dict, "SubtitleList", list);
858 int track_behavior = 0; // default no subtitles
859 int burn_behavior = 0;
860 int burn_foreign;
862 struct subtitle_behavior_s behavior;
863 behavior.one = 0;
864 behavior.burn_foreign = 0;
865 behavior.make_default = 0;
866 behavior.burn_first = 0;
867 behavior.burn_dvd = 0;
868 behavior.burn_bd = 0;
869 behavior.one_burned = 0;
870 // Create array that is used to track which tracks have been already added
871 // We do not want to add the same track with the same settings twice
872 behavior.used = calloc(source_subtitle_count, sizeof(*behavior.used));
874 // Since this function can be called multiple times, we need to
875 // initialize the "used" array from the existing subtitles in the list.
876 int count, ii;
877 count = hb_value_array_len(list);
878 for (ii = 0; ii < count; ii++)
880 hb_value_t *sub = hb_value_array_get(list, ii);
881 int track = hb_value_get_int(hb_dict_get(sub, "Track"));
882 behavior.used[track] = 1;
885 const char *s;
886 s = hb_value_get_string(hb_dict_get(preset,
887 "SubtitleTrackSelectionBehavior"));
888 if (s != NULL)
890 if (!strcasecmp(s, "first"))
891 track_behavior = 1;
892 else if (!strcasecmp(s, "all"))
893 track_behavior = 2;
896 s = hb_value_get_string(hb_dict_get(preset, "SubtitleBurnBehavior"));
897 if (s != NULL)
899 if (!strcasecmp(s, "foreign"))
900 burn_behavior = 1;
901 else if (!strcasecmp(s, "first"))
902 burn_behavior = 2;
903 else if (!strcasecmp(s, "foreign_first"))
904 burn_behavior = 3;
907 behavior.burn_dvd = hb_value_get_int(hb_dict_get(preset,
908 "SubtitleBurnDVDSub"));
909 behavior.burn_bd = hb_value_get_int(hb_dict_get(preset,
910 "SubtitleBurnBDSub"));
912 burn_foreign = burn_behavior == 1 || burn_behavior == 3;
913 behavior.burn_first = burn_behavior == 2 || burn_behavior == 3;
915 int foreign_audio_search, foreign_first_audio;
916 foreign_audio_search = hb_value_get_bool(hb_dict_get(preset,
917 "SubtitleAddForeignAudioSearch"));
918 foreign_first_audio = hb_value_get_bool(hb_dict_get(preset,
919 "SubtitleAddForeignAudioSubtitle"));
922 // Add tracks for all languages in the language list
923 hb_value_array_t *lang_list = hb_dict_get(preset, "SubtitleLanguageList");
924 count = hb_value_array_len(lang_list);
925 const char *pref_lang = "und";
926 if (count > 0)
928 pref_lang = hb_value_get_string(hb_value_array_get(lang_list, 0));
930 if (!strcmp(pref_lang, "und"))
932 foreign_audio_search = foreign_first_audio = 0;
935 int track;
936 if (first_audio_lang != NULL &&
937 foreign_first_audio && strncmp(first_audio_lang, pref_lang, 4))
939 // First audio lang does not match the preferred subittle lang.
940 // Preset says to add pref lang subtitle.
941 // Foreign audio search is not necessary since entire audio track
942 // is foreign.
943 foreign_audio_search = 0;
944 behavior.one = 1;
945 behavior.burn_foreign = burn_foreign;
946 behavior.make_default = 1;
947 add_subtitle_for_lang(list, title, mux, pref_lang, &behavior);
950 hb_dict_t *search_dict = hb_dict_get(subtitle_dict, "Search");
951 if (search_dict == NULL)
953 search_dict = hb_dict_init();
954 hb_dict_set(subtitle_dict, "Search", search_dict);
956 if (first_audio_lang != NULL &&
957 foreign_audio_search && !strncmp(first_audio_lang, pref_lang, 4))
959 // First audio lang matches the preferred subittle lang.
960 // Preset says to add search for foreign audio subtitles.
961 int burn = burn_foreign || behavior.burn_first;
962 // If not burning, make this the default track.
963 hb_dict_set(search_dict, "Enable", hb_value_bool(1));
964 hb_dict_set(search_dict, "Default", hb_value_bool(!burn));
965 hb_dict_set(search_dict, "Forced", hb_value_bool(1));
966 hb_dict_set(search_dict, "Burn", hb_value_bool(burn));
968 else
970 hb_dict_set(search_dict, "Enable", hb_value_bool(0));
973 if (track_behavior > 0)
975 int ii;
976 behavior.one = track_behavior == 1;
977 behavior.burn_foreign = 0;
978 behavior.make_default = 0;
979 for (ii = 0; ii < count; ii++)
981 const char *lang;
982 lang = hb_value_get_string(hb_value_array_get(lang_list, ii));
983 add_subtitle_for_lang(list, title, mux, lang, &behavior);
985 if (count <= 0)
987 add_subtitle_for_lang(list, title, mux, "und", &behavior);
991 if (hb_value_get_bool(hb_dict_get(preset, "SubtitleAddCC")))
993 // Add Closed Caption track
994 for (track = 0; track < source_subtitle_count; track++)
996 if (behavior.used[track])
998 continue;
1000 hb_subtitle_t *subtitle = hb_list_item(title->list_subtitle, track);
1001 if (subtitle->source == CC608SUB || subtitle->source == CC708SUB)
1003 int burn;
1004 burn = !behavior.one_burned &&
1005 (!hb_subtitle_can_pass(subtitle->source, mux) ||
1006 behavior.burn_first);
1007 behavior.used[track] = 1;
1008 behavior.one_burned |= burn;
1009 add_subtitle(list, track, 0 /*default*/, 0 /*!force*/, burn);
1010 break;
1014 free(behavior.used);
1016 return 0;
1019 static int get_video_framerate(hb_value_t *rate_value)
1021 int rate = 0;
1022 if (hb_value_type(rate_value) != HB_VALUE_TYPE_STRING)
1024 double d;
1025 d = hb_value_get_double(rate_value);
1026 if (d != 0 && d <= 600)
1028 // Assume the value is an actual framerate and compute
1029 // 27Mhz based denominator
1030 rate = (int)(27000000 / d);
1032 else
1034 // Assume the value is a 27Mhz based denominator
1035 rate = (int)d;
1038 else
1040 const char *rate_name = hb_value_get_string(rate_value);
1041 if (strcasecmp(rate_name, "source") &&
1042 strcasecmp(rate_name, "auto") &&
1043 strcasecmp(rate_name, "same as source"))
1045 rate = hb_video_framerate_get_from_name(rate_name);
1046 if (rate < 0)
1048 // No matching rate found. Error out.
1049 rate = -1;
1053 return rate;
1056 int hb_preset_apply_filters(const hb_dict_t *preset, hb_dict_t *job_dict)
1058 hb_value_t *filters_dict, *filter_list, *filter_dict;
1059 char *filter_str;
1061 // Create new filters
1062 filters_dict = hb_dict_init();
1063 hb_dict_set(job_dict, "Filters", filters_dict);
1064 filter_list = hb_value_array_init();
1065 hb_dict_set(filters_dict, "FilterList", filter_list);
1067 hb_dict_set(filters_dict, "Grayscale", hb_value_xform(
1068 hb_dict_get(preset, "VideoGrayScale"), HB_VALUE_TYPE_BOOL));
1070 // Detelecine filter
1071 hb_value_t *detel_val = hb_dict_get(preset, "PictureDetelecine");
1072 if (detel_val != NULL)
1074 const char *custom;
1075 custom = hb_value_get_string(hb_dict_get(preset,
1076 "PictureDetelecineCustom"));
1077 if (hb_value_type(detel_val) == HB_VALUE_TYPE_STRING)
1079 filter_str = hb_generate_filter_settings(
1080 HB_FILTER_DETELECINE, hb_value_get_string(detel_val), custom);
1082 else
1084 filter_str = hb_generate_filter_settings_by_index(
1085 HB_FILTER_DETELECINE, hb_value_get_int(detel_val), custom);
1087 if (filter_str == NULL)
1089 char *s = hb_value_get_string_xform(detel_val);
1090 hb_error("Invalid detelecine filter settings (%s)", s);
1091 free(s);
1092 return -1;
1094 else if (filter_str != hb_filter_off)
1096 filter_dict = hb_dict_init();
1097 hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_DETELECINE));
1098 hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
1099 hb_value_array_append(filter_list, filter_dict);
1100 free(filter_str);
1104 // Decomb or deinterlace filters
1105 int decomb_or_deint;
1106 decomb_or_deint = hb_value_get_bool(hb_dict_get(preset,
1107 "PictureDecombDeinterlace"));
1108 hb_value_t *decomb_val = hb_dict_get(preset, "PictureDecomb");
1109 if (decomb_or_deint && decomb_val != NULL)
1111 const char *custom;
1112 custom = hb_value_get_string(hb_dict_get(preset,
1113 "PictureDecombCustom"));
1114 if (hb_value_type(decomb_val) == HB_VALUE_TYPE_STRING)
1116 filter_str = hb_generate_filter_settings(
1117 HB_FILTER_DECOMB, hb_value_get_string(decomb_val), custom);
1119 else
1121 filter_str = hb_generate_filter_settings_by_index(
1122 HB_FILTER_DECOMB, hb_value_get_int(decomb_val), custom);
1124 if (filter_str == NULL)
1126 char *s = hb_value_get_string_xform(decomb_val);
1127 hb_error("Invalid decomb filter settings (%s)", s);
1128 free(s);
1129 return -1;
1131 else if (filter_str != hb_filter_off)
1133 filter_dict = hb_dict_init();
1134 hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_DECOMB));
1135 hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
1136 hb_value_array_append(filter_list, filter_dict);
1137 free(filter_str);
1141 hb_value_t *deint_val = hb_dict_get(preset, "PictureDeinterlace");
1142 if (!decomb_or_deint && deint_val != NULL)
1144 const char *custom;
1145 custom = hb_value_get_string(hb_dict_get(preset,
1146 "PictureDeinterlaceCustom"));
1147 if (hb_value_type(deint_val) == HB_VALUE_TYPE_STRING)
1149 filter_str = hb_generate_filter_settings(
1150 HB_FILTER_DEINTERLACE, hb_value_get_string(deint_val), custom);
1152 else
1154 filter_str = hb_generate_filter_settings_by_index(
1155 HB_FILTER_DEINTERLACE, hb_value_get_int(deint_val), custom);
1157 if (filter_str == NULL)
1159 char *s = hb_value_get_string_xform(deint_val);
1160 hb_error("Invalid deinterlace filter settings (%s)", s);
1161 free(s);
1162 return -1;
1164 else if (filter_str != hb_filter_off)
1166 filter_dict = hb_dict_init();
1167 hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_DEINTERLACE));
1168 hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
1169 hb_value_array_append(filter_list, filter_dict);
1170 free(filter_str);
1174 // Denoise filter
1175 int denoise;
1176 hb_value_t *denoise_value = hb_dict_get(preset, "PictureDenoiseFilter");
1177 denoise = hb_value_type(denoise_value) == HB_VALUE_TYPE_STRING ? (
1178 !strcasecmp(hb_value_get_string(denoise_value), "off") ? 0 :
1179 !strcasecmp(hb_value_get_string(denoise_value), "nlmeans") ? 1 : 2) :
1180 hb_value_get_int(denoise_value);
1182 if (denoise != 0)
1184 int filter_id = denoise == 1 ? HB_FILTER_NLMEANS : HB_FILTER_HQDN3D;
1185 const char *denoise_preset, *denoise_tune;
1186 denoise_preset = hb_value_get_string(
1187 hb_dict_get(preset, "PictureDenoisePreset"));
1188 if (denoise_preset != NULL)
1190 if (strcasecmp(denoise_preset, "custom"))
1191 denoise_tune = hb_value_get_string(
1192 hb_dict_get(preset, "PictureDenoiseTune"));
1193 else
1194 denoise_tune = hb_value_get_string(
1195 hb_dict_get(preset, "PictureDenoiseCustom"));
1197 filter_str = hb_generate_filter_settings(
1198 filter_id, denoise_preset, denoise_tune);
1199 if (filter_str == NULL)
1201 hb_error("Invalid denoise filter settings (%s%s%s)",
1202 denoise_preset,
1203 denoise_tune ? "," : "",
1204 denoise_tune ? denoise_tune : "");
1205 return -1;
1207 else if (filter_str != hb_filter_off)
1209 filter_dict = hb_dict_init();
1210 hb_dict_set(filter_dict, "ID", hb_value_int(filter_id));
1211 hb_dict_set(filter_dict, "Settings",
1212 hb_value_string(filter_str));
1213 hb_value_array_append(filter_list, filter_dict);
1214 free(filter_str);
1219 // Deblock filter
1220 char *deblock = hb_value_get_string_xform(
1221 hb_dict_get(preset, "PictureDeblock"));
1222 if (deblock != NULL)
1224 filter_str = hb_generate_filter_settings(HB_FILTER_DEBLOCK,
1225 deblock, NULL);
1226 if (filter_str == NULL)
1228 hb_error("Invalid deblock filter settings (%s)", deblock);
1229 return -1;
1231 else if (filter_str != hb_filter_off)
1233 filter_dict = hb_dict_init();
1234 hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_DEBLOCK));
1235 hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
1236 hb_value_array_append(filter_list, filter_dict);
1237 free(filter_str);
1240 free(deblock);
1242 // Rotate filter
1243 char *rotate = hb_value_get_string_xform(
1244 hb_dict_get(preset, "PictureRotate"));
1245 if (rotate != NULL)
1247 filter_str = hb_generate_filter_settings(HB_FILTER_ROTATE,
1248 rotate, NULL);
1249 if (filter_str == NULL)
1251 hb_error("Invalid rotate filter settings (%s)", rotate);
1252 return -1;
1254 else if (filter_str != hb_filter_off)
1256 filter_dict = hb_dict_init();
1257 hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_ROTATE));
1258 hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
1259 hb_value_array_append(filter_list, filter_dict);
1260 free(filter_str);
1263 free(rotate);
1265 hb_value_t *fr_value = hb_dict_get(preset, "VideoFramerate");
1266 int vrate_den = get_video_framerate(fr_value);
1267 if (vrate_den < 0)
1269 char *str = hb_value_get_string_xform(fr_value);
1270 hb_error("Invalid video framerate (%s)", str);
1271 free(str);
1272 return -1;
1275 int fr_mode;
1276 hb_value_t *fr_mode_value = hb_dict_get(preset, "VideoFramerateMode");
1277 fr_mode = hb_value_type(fr_mode_value) == HB_VALUE_TYPE_STRING ? (
1278 !strcasecmp(hb_value_get_string(fr_mode_value), "cfr") ? 1 :
1279 !strcasecmp(hb_value_get_string(fr_mode_value), "pfr") ? 2 : 0) :
1280 hb_value_get_int(fr_mode_value);
1282 if (vrate_den == 0)
1283 filter_str = hb_strdup_printf("%d", fr_mode);
1284 else
1285 filter_str = hb_strdup_printf("%d:%d:%d", fr_mode, 27000000, vrate_den);
1287 filter_dict = hb_dict_init();
1288 hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_VFR));
1289 hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
1290 hb_value_array_append(filter_list, filter_dict);
1291 free(filter_str);
1293 return 0;
1296 int hb_preset_apply_video(const hb_dict_t *preset, hb_dict_t *job_dict)
1298 hb_dict_t *dest_dict, *video_dict, *qsv;
1299 hb_value_t *value, *vcodec_value, *color_value;
1300 int mux, vcodec, vqtype;
1301 hb_encoder_t *encoder;
1303 dest_dict = hb_dict_get(job_dict, "Destination");
1304 mux = hb_container_get_from_name(hb_value_get_string(
1305 hb_dict_get(dest_dict, "Mux")));
1306 vcodec_value = hb_dict_get(preset, "VideoEncoder");
1307 if (hb_value_type(vcodec_value) == HB_VALUE_TYPE_STRING)
1309 vcodec = hb_video_encoder_get_from_name(
1310 hb_value_get_string(vcodec_value));
1312 else
1314 vcodec = hb_value_get_int(vcodec_value);
1316 encoder = hb_video_encoder_get_from_codec(vcodec);
1317 if (encoder == NULL)
1319 char *str = hb_value_get_string_xform(vcodec_value);
1320 hb_error("Invalid video encoder (%s)", str);
1321 free(str);
1322 return -1;
1324 if (!(encoder->muxers & mux))
1326 hb_error("Incompatible video encoder (%s) for muxer (%s)",
1327 hb_video_encoder_get_name(vcodec),
1328 hb_container_get_name(mux));
1329 return -1;
1332 video_dict = hb_dict_get(job_dict, "Video");
1333 hb_dict_set(video_dict, "Encoder", hb_value_string(encoder->short_name));
1335 if ((color_value = hb_dict_get(preset, "VideoColorMatrixCode")) != NULL)
1336 hb_dict_set(video_dict, "ColorMatrixCode", hb_value_dup(color_value));
1337 hb_dict_set(video_dict, "Encoder", hb_value_dup(vcodec_value));
1339 if (vcodec == HB_VCODEC_X264 &&
1340 hb_value_get_bool(hb_dict_get(preset, "x264UseAdvancedOptions")))
1342 hb_dict_set(video_dict, "Options",
1343 hb_value_dup(hb_dict_get(preset, "x264Option")));
1345 else
1347 if ((value = hb_dict_get(preset, "VideoPreset")) != NULL)
1348 hb_dict_set(video_dict, "Preset", hb_value_dup(value));
1349 if ((value = hb_dict_get(preset, "VideoProfile")) != NULL)
1350 hb_dict_set(video_dict, "Profile", hb_value_dup(value));
1351 if ((value = hb_dict_get(preset, "VideoLevel")) != NULL)
1352 hb_dict_set(video_dict, "Level", hb_value_dup(value));
1353 if ((value = hb_dict_get(preset, "VideoTune")) != NULL)
1354 hb_dict_set(video_dict, "Tune", hb_value_dup(value));
1355 if ((value = hb_dict_get(preset, "VideoOptionExtra")) != NULL)
1356 hb_dict_set(video_dict, "Options", hb_value_dup(value));
1359 vqtype = hb_value_get_int(hb_dict_get(preset, "VideoQualityType"));
1360 if (vqtype == 2) // Constant quality
1362 hb_dict_set(video_dict, "Quality",
1363 hb_value_xform(hb_dict_get(preset, "VideoQualitySlider"),
1364 HB_VALUE_TYPE_DOUBLE));
1365 hb_dict_set(video_dict, "Bitrate", hb_value_int(-1));
1367 else if (vqtype == 1) // ABR
1369 hb_dict_set(video_dict, "Bitrate",
1370 hb_value_xform(hb_dict_get(preset, "VideoAvgBitrate"),
1371 HB_VALUE_TYPE_INT));
1372 hb_dict_set(video_dict, "TwoPass",
1373 hb_value_xform(hb_dict_get(preset, "VideoTwoPass"),
1374 HB_VALUE_TYPE_BOOL));
1375 hb_dict_set(video_dict, "Turbo",
1376 hb_value_xform(hb_dict_get(preset, "VideoTurboTwoPass"),
1377 HB_VALUE_TYPE_BOOL));
1378 hb_dict_set(video_dict, "Quality", hb_value_double(-1.0));
1380 else
1382 value = hb_dict_get(preset, "VideoQualitySlider");
1383 if (value != NULL && hb_value_get_double(value) >= 0)
1385 hb_dict_set(video_dict, "Quality",
1386 hb_value_xform(value, HB_VALUE_TYPE_DOUBLE));
1387 hb_dict_set(video_dict, "Bitrate", hb_value_int(-1));
1389 else
1391 hb_dict_set(video_dict, "Bitrate",
1392 hb_value_xform(hb_dict_get(preset, "VideoAvgBitrate"),
1393 HB_VALUE_TYPE_INT));
1394 hb_dict_set(video_dict, "TwoPass",
1395 hb_value_xform(hb_dict_get(preset, "VideoTwoPass"),
1396 HB_VALUE_TYPE_BOOL));
1397 hb_dict_set(video_dict, "Turbo",
1398 hb_value_xform(hb_dict_get(preset, "VideoTurboTwoPass"),
1399 HB_VALUE_TYPE_BOOL));
1400 hb_dict_set(video_dict, "Quality", hb_value_double(-1.0));
1403 qsv = hb_dict_get(video_dict, "QSV");
1404 if (qsv == NULL)
1406 qsv = hb_dict_init();
1407 hb_dict_set(video_dict, "QSV", qsv);
1409 if ((value = hb_dict_get(preset, "VideoQSVDecode")) != NULL)
1411 hb_dict_set(qsv, "Decode",
1412 hb_value_xform(value, HB_VALUE_TYPE_BOOL));
1414 if ((value = hb_dict_get(preset, "VideoQSVAsyncDepth")) != NULL)
1416 hb_dict_set(qsv, "AsyncDepth",
1417 hb_value_xform(value, HB_VALUE_TYPE_INT));
1420 if ((value = hb_dict_get(preset, "VideoScaler")) != NULL)
1422 const char *s = hb_value_get_string(value);
1423 if (!strcasecmp(s, "opencl"))
1425 hb_dict_set(video_dict, "OpenCL", hb_value_bool(1));
1428 if ((value = hb_dict_get(preset, "VideoHWDecode")) != NULL)
1430 hb_dict_set(video_dict, "HWDecode",
1431 hb_value_xform(value, HB_VALUE_TYPE_BOOL));
1434 return 0;
1437 int hb_preset_apply_mux(const hb_dict_t *preset, hb_dict_t *job_dict)
1439 hb_value_t *mux_value = hb_dict_get(preset, "FileFormat");
1440 int mux;
1441 if (hb_value_type(mux_value) == HB_VALUE_TYPE_STRING)
1443 mux = hb_container_get_from_name(hb_value_get_string(mux_value));
1444 if (mux == 0)
1445 mux = hb_container_get_from_extension(
1446 hb_value_get_string(mux_value));
1448 else
1450 mux = hb_value_get_int(mux_value);
1452 hb_container_t *container = hb_container_get_from_format(mux);
1453 if (container == NULL)
1455 char *str = hb_value_get_string_xform(mux_value);
1456 hb_error("Invalid container (%s)", str);
1457 free(str);
1458 return -1;
1461 hb_dict_t *dest_dict = hb_dict_get(job_dict, "Destination");
1462 hb_dict_set(dest_dict, "Mux", hb_value_string(container->short_name));
1464 if (mux & HB_MUX_MASK_MP4)
1466 hb_dict_t *mp4_dict = hb_dict_init();
1467 hb_dict_set(mp4_dict, "Mp4Optimize",
1468 hb_value_xform(hb_dict_get(preset, "Mp4HttpOptimize"),
1469 HB_VALUE_TYPE_BOOL));
1470 hb_dict_set(mp4_dict, "IpodAtom",
1471 hb_value_xform(hb_dict_get(preset, "Mp4iPodCompatible"),
1472 HB_VALUE_TYPE_BOOL));
1473 hb_dict_set(dest_dict, "Mp4Options", mp4_dict);
1476 return 0;
1479 int hb_preset_apply_title(hb_handle_t *h, int title_index,
1480 const hb_dict_t *preset, hb_dict_t *job_dict)
1482 // Apply preset settings that requires the title
1483 hb_title_t *title = hb_find_title_by_index(h, title_index);
1484 if (title == NULL)
1485 return -1;
1487 int chapters;
1488 chapters = hb_value_get_bool(hb_dict_get(preset, "ChapterMarkers"));
1489 if (title != NULL && hb_list_count(title->list_chapter) <= 1)
1490 chapters = 0;
1492 // Set "Destination" settings in job
1493 hb_dict_t *dest_dict = hb_dict_get(job_dict, "Destination");
1494 hb_dict_set(dest_dict, "ChapterMarkers", hb_value_bool(chapters));
1496 hb_dict_t *filters_dict = hb_dict_get(job_dict, "Filters");
1497 hb_value_array_t *filter_list = hb_dict_get(filters_dict, "FilterList");
1499 // Calculate default job geometry settings
1500 hb_geometry_t srcGeo, resultGeo;
1501 hb_geometry_settings_t geo;
1502 int keep_aspect;
1504 srcGeo = title->geometry;
1505 if (!hb_value_get_bool(hb_dict_get(preset, "PictureAutoCrop")))
1507 geo.crop[0] = hb_value_get_int(hb_dict_get(preset, "PictureTopCrop"));
1508 geo.crop[1] = hb_value_get_int(hb_dict_get(preset, "PictureBottomCrop"));
1509 geo.crop[2] = hb_value_get_int(hb_dict_get(preset, "PictureLeftCrop"));
1510 geo.crop[3] = hb_value_get_int(hb_dict_get(preset, "PictureRightCrop"));
1512 else
1514 memcpy(geo.crop, title->crop, sizeof(geo.crop));
1516 geo.modulus = hb_value_get_int(hb_dict_get(preset, "PictureModulus"));
1517 if (geo.modulus < 2)
1518 geo.modulus = 2;
1519 if (hb_value_get_bool(hb_dict_get(preset, "PictureLooseCrop")))
1521 // Crop a few extra pixels to avoid scaling to fit Modulus
1522 int extra1, extra2, crop_width, crop_height, width, height;
1524 crop_width = srcGeo.width - geo.crop[2] - geo.crop[3];
1525 crop_height = srcGeo.height - geo.crop[0] - geo.crop[1];
1526 width = MULTIPLE_MOD_DOWN(crop_width, geo.modulus);
1527 height = MULTIPLE_MOD_DOWN(crop_height, geo.modulus);
1529 extra1 = EVEN((crop_height - height) / 2);
1530 extra2 = crop_height - height - extra1;
1531 geo.crop[0] += extra1;
1532 geo.crop[1] += extra2;
1533 extra1 = EVEN((crop_width - width) / 2);
1534 extra2 = crop_width - width - extra1;
1535 geo.crop[2] += extra1;
1536 geo.crop[3] += extra2;
1538 hb_value_t *ana_mode_value = hb_dict_get(preset, "PicturePAR");
1539 if (hb_value_type(ana_mode_value) == HB_VALUE_TYPE_STRING)
1541 const char *s = hb_value_get_string(ana_mode_value);
1542 if (!strcasecmp(s, "none"))
1543 geo.mode = 0;
1544 else if (!strcasecmp(s, "strict"))
1545 geo.mode = 1;
1546 else if (!strcasecmp(s, "custom"))
1547 geo.mode = 3;
1548 else // default loose
1549 geo.mode = 2;
1551 else
1553 geo.mode = hb_value_get_int(hb_dict_get(preset, "PicturePAR"));
1555 keep_aspect = hb_value_get_bool(hb_dict_get(preset, "PictureKeepRatio"));
1556 if (geo.mode == HB_ANAMORPHIC_STRICT || geo.mode == HB_ANAMORPHIC_LOOSE)
1557 keep_aspect = 1;
1558 geo.keep = keep_aspect * HB_KEEP_DISPLAY_ASPECT;
1559 geo.itu_par = hb_value_get_bool(hb_dict_get(preset, "PictureItuPAR"));
1560 geo.maxWidth = hb_value_get_int(hb_dict_get(preset, "PictureWidth"));
1561 geo.maxHeight = hb_value_get_int(hb_dict_get(preset, "PictureHeight"));
1562 geo.geometry = title->geometry;
1563 int width = hb_value_get_int(hb_dict_get(preset, "PictureForceWidth"));
1564 int height = hb_value_get_int(hb_dict_get(preset, "PictureForceHeight"));
1565 if (width > 0)
1567 geo.geometry.width = width;
1568 geo.keep |= HB_KEEP_WIDTH;
1570 else
1572 geo.geometry.width -= geo.crop[2] + geo.crop[3];
1574 if (height > 0)
1576 geo.geometry.height = height;
1577 geo.keep |= HB_KEEP_HEIGHT;
1579 else
1581 geo.geometry.height -= geo.crop[0] + geo.crop[1];
1583 if (geo.mode == HB_ANAMORPHIC_CUSTOM && !keep_aspect)
1585 int dar_width;
1586 dar_width = hb_value_get_int(hb_dict_get(preset, "PictureDARWidth"));
1587 if (dar_width > 0)
1589 geo.geometry.par.num = dar_width;
1590 geo.geometry.par.den = geo.geometry.width;
1592 else
1594 geo.geometry.par.num =
1595 hb_value_get_int(hb_dict_get(preset, "PicturePARWidth"));
1596 geo.geometry.par.den =
1597 hb_value_get_int(hb_dict_get(preset, "PicturePARHeight"));
1600 hb_set_anamorphic_size2(&srcGeo, &geo, &resultGeo);
1601 hb_dict_t *par_dict = hb_dict_get(job_dict, "PAR");
1602 hb_dict_set(par_dict, "Num", hb_value_int(resultGeo.par.num));
1603 hb_dict_set(par_dict, "Den", hb_value_int(resultGeo.par.den));
1604 par_dict = NULL;
1606 hb_dict_t *filter_dict;
1607 char *filter_str;
1609 // Setup scale filter
1610 filter_str = hb_strdup_printf("%d:%d:%d:%d:%d:%d",
1611 resultGeo.width, resultGeo.height,
1612 geo.crop[0], geo.crop[1],
1613 geo.crop[2], geo.crop[3]);
1615 filter_dict = hb_dict_init();
1616 hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_CROP_SCALE));
1617 hb_dict_set(filter_dict, "Settings", hb_value_string(filter_str));
1618 free(filter_str);
1619 hb_value_array_append(filter_list, filter_dict);
1621 // Audio settings
1622 if (hb_preset_job_add_audio(h, title_index, preset, job_dict) != 0)
1624 goto fail;
1627 // Subtitle settings
1628 if (hb_preset_job_add_subtitles(h, title_index, preset, job_dict) != 0)
1630 goto fail;
1632 return 0;
1634 fail:
1635 return -1;
1639 * Initialize an hb_job_t and return a hb_dict_t representation of the job.
1640 * This dict will have key/value pairs compatible with json jobs.
1641 * @param h - Pointer to hb_handle_t instance that contains the
1642 * specified title_index
1643 * @param title_index - Index of hb_title_t to use for job initialization.
1644 * Index comes from title->index or "Index" key
1645 * in json representation of a title.
1646 * @param preset - Preset to initialize job with
1648 hb_dict_t* hb_preset_job_init(hb_handle_t *h, int title_index,
1649 const hb_dict_t *preset)
1651 hb_title_t *title = hb_find_title_by_index(h, title_index);
1652 if (title == NULL)
1654 hb_error("Invalid title index (%d)", title_index);
1655 return NULL;
1658 hb_job_t *job = hb_job_init(title);
1659 hb_dict_t *job_dict = hb_job_to_dict(job);
1660 hb_job_close(&job);
1662 if (hb_preset_apply_mux(preset, job_dict) < 0)
1663 goto fail;
1665 if (hb_preset_apply_video(preset, job_dict) < 0)
1666 goto fail;
1668 if (hb_preset_apply_filters(preset, job_dict) < 0)
1669 goto fail;
1671 if (hb_preset_apply_title(h, title_index, preset, job_dict) < 0)
1672 goto fail;
1674 return job_dict;
1676 fail:
1677 hb_value_free(&job_dict);
1678 return NULL;
1681 // Clean a dictionary of unwanted keys
1682 // Used to make sure only valid keys are in output presets
1683 static void
1684 dict_clean(hb_value_t *dict, hb_value_t *template)
1686 hb_value_t *tmp = hb_value_dup(dict);
1687 hb_dict_iter_t iter;
1688 const char *key;
1689 hb_value_t *val;
1690 hb_value_t *template_val;
1691 hb_value_type_t template_type, val_type;
1692 const char *preset_name = NULL;
1694 val = hb_dict_get(dict, "PresetName");
1695 if (val != NULL)
1696 preset_name = hb_value_get_string(val);
1698 // Remove keys that are not in the template and translate compatible
1699 // data types to the types used in the template.
1700 for (iter = hb_dict_iter_init(tmp);
1701 iter != HB_DICT_ITER_DONE;
1702 iter = hb_dict_iter_next(tmp, iter))
1704 key = hb_dict_iter_key(iter);
1705 val = hb_dict_iter_value(iter);
1706 val_type = hb_value_type(val);
1708 template_val = hb_dict_get(template, key);
1709 template_type = hb_value_type(template_val);
1710 if (template_val == NULL)
1712 // Unknown key. These can be keys used privately by the
1713 // frontend. So don't make noise about them.
1714 hb_dict_remove(dict, key);
1716 else if (val_type != template_type)
1718 if (val_type == HB_VALUE_TYPE_DICT ||
1719 val_type == HB_VALUE_TYPE_ARRAY ||
1720 template_type == HB_VALUE_TYPE_DICT ||
1721 template_type == HB_VALUE_TYPE_ARRAY)
1723 hb_error("Preset %s: Incompatible value types for key %s. "
1724 "Dropping.", preset_name, key);
1725 hb_dict_remove(dict, key);
1727 else if (hb_value_is_number(val) &&
1728 hb_value_is_number(template_val))
1730 // Silently convert compatible numbers
1731 hb_value_t *v;
1732 v = hb_value_xform(val, template_type);
1733 hb_dict_set(dict, key, v);
1735 else
1737 hb_value_t *v;
1738 hb_error("Preset %s: Incorrect value type for key %s. "
1739 "Converting.", preset_name, key);
1740 v = hb_value_xform(val, template_type);
1741 hb_dict_set(dict, key, v);
1744 else if (val_type == HB_VALUE_TYPE_DICT &&
1745 template_type == HB_VALUE_TYPE_DICT)
1747 val = hb_dict_get(dict, key);
1748 dict_clean(val, template_val);
1750 else if (val_type == HB_VALUE_TYPE_ARRAY &&
1751 template_type == HB_VALUE_TYPE_ARRAY &&
1752 hb_value_array_len(template_val) > 0)
1754 template_val = hb_value_array_get(template_val, 0);
1755 if (hb_value_type(template_val) == HB_VALUE_TYPE_DICT)
1757 val = hb_dict_get(dict, key);
1758 int count = hb_value_array_len(val);
1759 int ii;
1760 for (ii = 0; ii < count; ii++)
1762 hb_value_t *array_val;
1763 array_val = hb_value_array_get(val, ii);
1764 if (hb_value_type(array_val) == HB_VALUE_TYPE_DICT)
1766 dict_clean(array_val, template_val);
1772 hb_value_free(&tmp);
1774 if (!hb_value_get_bool(hb_dict_get(dict, "Folder")))
1776 // Add key/value pairs that are in the template but not in the dict.
1777 for (iter = hb_dict_iter_init(template);
1778 iter != HB_DICT_ITER_DONE;
1779 iter = hb_dict_iter_next(template, iter))
1781 key = hb_dict_iter_key(iter);
1782 template_val = hb_dict_iter_value(iter);
1783 val = hb_dict_get(dict, key);
1784 if (val == NULL)
1786 hb_dict_set(dict, key, hb_value_dup(template_val));
1792 static void preset_clean(hb_value_t *preset, hb_value_t *template)
1794 dict_clean(preset, template);
1796 // Check for proper "short name" values.
1797 // Convert as necessary.
1798 hb_value_t *val;
1799 const char *preset_name = NULL;
1800 int muxer;
1802 val = hb_dict_get(preset, "PresetName");
1803 if (val != NULL)
1804 preset_name = hb_value_get_string(val);
1806 val = hb_dict_get(preset, "FileFormat");
1807 if (val != NULL)
1809 const char *s, *mux;
1810 s = hb_value_get_string(val);
1811 muxer = hb_container_get_from_name(s);
1812 if (muxer == HB_MUX_INVALID)
1814 const hb_container_t *c = hb_container_get_next(NULL);
1815 muxer = c->format;
1816 hb_error("Preset %s: Invalid container (%s)", preset_name, s);
1818 mux = hb_container_get_short_name(muxer);
1819 val = hb_value_string(mux);
1820 hb_dict_set(preset, "FileFormat", val);
1822 else
1824 const hb_container_t *c = hb_container_get_next(NULL);
1825 muxer = c->format;
1827 val = hb_dict_get(preset, "VideoEncoder");
1828 if (val != NULL)
1830 const char *s, *enc;
1831 int vcodec;
1832 s = hb_value_get_string(val);
1833 vcodec = hb_video_encoder_get_from_name(s);
1834 if (vcodec == HB_VCODEC_INVALID)
1836 vcodec = hb_video_encoder_get_default(muxer);
1837 hb_error("Preset %s: Invalid video encoder (%s)", preset_name, s);
1839 enc = hb_video_encoder_get_short_name(vcodec);
1840 val = hb_value_string(enc);
1841 hb_dict_set(preset, "VideoEncoder", val);
1843 val = hb_dict_get(preset, "VideoFramerate");
1844 if (val != NULL)
1846 const char *s;
1847 s = hb_value_get_string(val);
1848 if (strcasecmp(s, "auto"))
1850 int fr = hb_video_framerate_get_from_name(s);
1851 if (fr < 0)
1853 if (strcasecmp(s, "same as source"))
1855 hb_error("Preset %s: Invalid video framerate (%s)",
1856 preset_name, s);
1858 val = hb_value_string("auto");
1859 hb_dict_set(preset, "VideoFramerate", val);
1863 val = hb_dict_get(preset, "AudioEncoderFallback");
1864 if (val != NULL)
1866 const char *s, *enc;
1867 int acodec;
1868 s = hb_value_get_string(val);
1869 acodec = hb_audio_encoder_get_from_name(s);
1870 if (acodec == HB_ACODEC_INVALID)
1872 acodec = hb_audio_encoder_get_default(muxer);
1873 hb_error("Preset %s: Invalid audio fallback encoder (%s)",
1874 preset_name, s);
1876 enc = hb_audio_encoder_get_short_name(acodec);
1877 val = hb_value_string(enc);
1878 hb_dict_set(preset, "AudioEncoderFallback", val);
1880 hb_value_t *alist = hb_dict_get(preset, "AudioList");
1881 int count = hb_value_array_len(alist);
1882 int ii;
1883 for (ii = 0; ii < count; ii++)
1885 hb_value_t *adict = hb_value_array_get(alist, ii);
1886 val = hb_dict_get(adict, "AudioEncoder");
1887 if (val != NULL)
1889 const char *s, *enc;
1890 int acodec;
1891 s = hb_value_get_string(val);
1892 acodec = hb_audio_encoder_get_from_name(s);
1893 if (acodec == HB_ACODEC_INVALID)
1895 acodec = hb_audio_encoder_get_default(muxer);
1896 hb_error("Preset %s: Invalid audio encoder (%s)",
1897 preset_name, s);
1899 enc = hb_audio_encoder_get_short_name(acodec);
1900 val = hb_value_string(enc);
1901 hb_dict_set(adict, "AudioEncoder", val);
1903 val = hb_dict_get(adict, "AudioSamplerate");
1904 if (val != NULL)
1906 const char *s;
1907 s = hb_value_get_string(val);
1908 if (strcasecmp(s, "auto"))
1910 int sr = hb_audio_samplerate_get_from_name(s);
1911 if (sr < 0)
1913 hb_error("Preset %s: Invalid audio samplerate (%s)",
1914 preset_name, s);
1915 val = hb_value_string("auto");
1916 hb_dict_set(adict, "AudioSamplerate", val);
1920 val = hb_dict_get(adict, "AudioMixdown");
1921 if (val != NULL)
1923 const char *s, *mix;
1924 s = hb_value_get_string(val);
1925 int mixdown = hb_mixdown_get_from_name(s);
1926 if (mixdown == HB_INVALID_AMIXDOWN)
1928 // work.c do_job() sanitizes NONE to default mixdown
1929 mixdown = HB_AMIXDOWN_NONE;
1930 hb_error("Preset %s: Invalid audio mixdown (%s)",
1931 preset_name, s);
1933 mix = hb_mixdown_get_short_name(mixdown);
1934 val = hb_value_string(mix);
1935 hb_dict_set(adict, "AudioMixdown", val);
1940 static void presets_clean(hb_value_t *presets, hb_value_t *template)
1942 preset_clean_context_t ctx;
1943 ctx.do_ctx.path.depth = 1;
1944 ctx.template = template;
1945 presets_do(do_preset_clean, presets, (preset_do_context_t*)&ctx);
1948 void hb_presets_clean(hb_value_t *preset)
1950 presets_clean(preset, hb_preset_template);
1953 static const char* import_indexed_filter(int filter_id, int index)
1955 hb_filter_param_t *filter_presets;
1956 filter_presets = hb_filter_param_get_presets(filter_id);
1958 int ii;
1959 for (ii = 0; filter_presets[ii].name != NULL; ii++)
1961 if (filter_presets[ii].index == index)
1962 break;
1964 return filter_presets[ii].short_name;
1967 static void import_decomb(hb_value_t *preset)
1969 hb_value_t *val = hb_dict_get(preset, "PictureDecomb");
1970 if (hb_value_is_number(val))
1972 const char *s;
1973 int index = hb_value_get_int(val);
1974 s = import_indexed_filter(HB_FILTER_DECOMB, index);
1975 if (s != NULL)
1977 hb_dict_set(preset, "PictureDecomb", hb_value_string(s));
1979 else
1981 hb_error("Invalid decomb index %d", index);
1982 hb_dict_set(preset, "PictureDecomb", hb_value_string("off"));
1987 static void import_deint(hb_value_t *preset)
1989 hb_value_t *val = hb_dict_get(preset, "PictureDeinterlace");
1990 if (hb_value_is_number(val))
1992 const char *s;
1993 int index = hb_value_get_int(val);
1994 s = import_indexed_filter(HB_FILTER_DEINTERLACE, index);
1995 if (s != NULL)
1997 hb_dict_set(preset, "PictureDeinterlace", hb_value_string(s));
1999 else
2001 hb_error("Invalid deinterlace index %d", index);
2002 hb_dict_set(preset, "PictureDeinterlace", hb_value_string("off"));
2007 static void import_detel(hb_value_t *preset)
2009 hb_value_t *val = hb_dict_get(preset, "PictureDetelecine");
2010 if (hb_value_is_number(val))
2012 const char *s;
2013 int index = hb_value_get_int(val);
2014 s = import_indexed_filter(HB_FILTER_DETELECINE, index);
2015 if (s != NULL)
2017 hb_dict_set(preset, "PictureDetelecine", hb_value_string(s));
2019 else
2021 hb_error("Invalid detelecine index %d", index);
2022 hb_dict_set(preset, "PictureDetelecine", hb_value_string("off"));
2027 static void import_denoise(hb_value_t *preset)
2029 hb_value_t *val = hb_dict_get(preset, "PictureDenoise");
2030 if (hb_value_is_number(val))
2032 const char *s;
2033 int index = hb_value_get_int(val);
2034 s = import_indexed_filter(HB_FILTER_HQDN3D, index);
2035 if (s != NULL)
2037 hb_dict_set(preset, "PictureDenoiseFilter",
2038 hb_value_string("hqdn3d"));
2039 hb_dict_set(preset, "PictureDenoisePreset", hb_value_string(s));
2041 else
2043 if (index != 0)
2044 hb_error("Invalid denoise index %d", index);
2045 hb_dict_set(preset, "PictureDenoiseFilter", hb_value_string("off"));
2050 static void import_pic(hb_value_t *preset)
2052 if (hb_value_get_bool(hb_dict_get(preset, "UsesMaxPictureSettings")))
2054 // UsesMaxPictureSettings was deprecated
2055 hb_dict_set(preset, "UsesPictureSettings", hb_value_int(2));
2058 hb_value_t *val = hb_dict_get(preset, "PicturePAR");
2059 if (hb_value_is_number(val))
2061 const char *s;
2062 int pic_par = hb_value_get_int(val);
2063 switch (pic_par)
2065 default:
2066 case 0:
2067 s = "off";
2068 break;
2069 case 1:
2070 s = "strict";
2071 break;
2072 case 2:
2073 s = "loose";
2074 break;
2075 case 3:
2076 s = "custom";
2077 break;
2079 hb_dict_set(preset, "PicturePAR", hb_value_string(s));
2083 static void import_audio(hb_value_t *preset)
2085 hb_value_t *copy = hb_dict_get(preset, "AudioCopyMask");
2086 if (copy != NULL)
2087 return;
2089 copy = hb_value_array_init();
2090 hb_dict_set(preset, "AudioCopyMask", copy);
2091 if (hb_value_get_bool(hb_dict_get(preset, "AudioAllowMP3Pass")))
2092 hb_value_array_append(copy, hb_value_string("copy:mp3"));
2093 if (hb_value_get_bool(hb_dict_get(preset, "AudioAllowAACPass")))
2094 hb_value_array_append(copy, hb_value_string("copy:aac"));
2095 if (hb_value_get_bool(hb_dict_get(preset, "AudioAllowAC3Pass")))
2096 hb_value_array_append(copy, hb_value_string("copy:ac3"));
2097 if (hb_value_get_bool(hb_dict_get(preset, "AudioAllowDTSPass")))
2098 hb_value_array_append(copy, hb_value_string("copy:dts"));
2099 if (hb_value_get_bool(hb_dict_get(preset, "AudioAllowDTSHDPass")))
2100 hb_value_array_append(copy, hb_value_string("copy:dtshd"));
2101 if (hb_value_get_bool(hb_dict_get(preset, "AudioAllowEAC3Pass")))
2102 hb_value_array_append(copy, hb_value_string("copy:eac3"));
2103 if (hb_value_get_bool(hb_dict_get(preset, "AudioAllowFLACPass")))
2104 hb_value_array_append(copy, hb_value_string("copy:flac"));
2105 if (hb_value_get_bool(hb_dict_get(preset, "AudioAllowTRUEHDPass")))
2106 hb_value_array_append(copy, hb_value_string("copy:truehd"));
2109 static void import_video(hb_value_t *preset)
2111 hb_value_t *val;
2113 if ((val = hb_dict_get(preset, "x264Preset")) != NULL)
2114 hb_dict_set(preset, "VideoPreset", hb_value_dup(val));
2115 if ((val = hb_dict_get(preset, "x264Tune")) != NULL)
2116 hb_dict_set(preset, "VideoTune", hb_value_dup(val));
2117 if ((val = hb_dict_get(preset, "h264Profile")) != NULL)
2118 hb_dict_set(preset, "VideoProfile", hb_value_dup(val));
2119 if ((val = hb_dict_get(preset, "h264Level")) != NULL)
2120 hb_dict_set(preset, "VideoLevel", hb_value_dup(val));
2121 if ((val = hb_dict_get(preset, "x264OptionExtra")) != NULL)
2122 hb_dict_set(preset, "VideoOptionExtra", hb_value_dup(val));
2124 // Remove invalid "none" tune from VideoTune. Frontends should
2125 // be removing this before saving a preset.
2126 if ((val = hb_dict_get(preset, "VideoTune")) != NULL)
2128 const char *tune;
2129 tune = hb_value_get_string(val);
2130 // "none" is not a valid tune, but is used by HandBrake
2131 // to indicate no tune options.
2132 if (tune != NULL && !strncasecmp(tune, "none", 4))
2134 tune += 4;
2135 if (tune[0] == ',')
2137 tune++;
2140 if (tune != NULL)
2142 hb_dict_set(preset, "VideoTune", hb_value_string(tune));
2146 if (hb_value_get_int(hb_dict_get(preset, "VideoQualityType")) == 0)
2148 // Target size no longer supported
2149 hb_dict_set(preset, "VideoQualityType", hb_value_int(1));
2152 if (hb_value_get_bool(hb_dict_get(preset, "VideoFrameratePFR")))
2154 hb_dict_set(preset, "VideoFramerateMode", hb_value_string("pfr"));
2156 else if (hb_value_get_bool(hb_dict_get(preset, "VideoFramerateCFR")))
2158 hb_dict_set(preset, "VideoFramerateMode", hb_value_string("cfr"));
2160 else if (hb_value_get_bool(hb_dict_get(preset, "VideoFramerateVFR")))
2162 hb_dict_set(preset, "VideoFramerateMode", hb_value_string("vfr"));
2165 const char *enc;
2166 int codec;
2167 enc = hb_value_get_string(hb_dict_get(preset, "VideoEncoder"));
2168 codec = hb_video_encoder_get_from_name(enc);
2169 if (codec & HB_VCODEC_FFMPEG_MASK)
2171 if ((val = hb_dict_get(preset, "lavcOption")) != NULL)
2172 hb_dict_set(preset, "VideoOptionExtra", hb_value_dup(val));
2176 static void preset_import(hb_value_t *preset, int major, int minor, int micro)
2178 if (!hb_value_get_bool(hb_dict_get(preset, "Folder")))
2180 if (major == 0 && minor == 0 && micro == 0)
2182 // Convert legacy presets (before versioning introduced)
2183 import_video(preset);
2184 import_pic(preset);
2185 import_audio(preset);
2186 import_decomb(preset);
2187 import_deint(preset);
2188 import_detel(preset);
2189 import_denoise(preset);
2191 preset_clean(preset, hb_preset_template);
2195 int hb_presets_version(hb_value_t *preset, int *major, int *minor, int *micro)
2197 *major = 0; *minor = 0; *micro = 0;
2198 if (hb_value_type(preset) == HB_VALUE_TYPE_DICT)
2200 // Is this a single preset or a packaged collection of presets?
2201 hb_value_t *val = hb_dict_get(preset, "PresetName");
2202 if (val == NULL)
2204 val = hb_dict_get(preset, "VersionMajor");
2205 if (val != NULL)
2207 *major = hb_value_get_int(hb_dict_get(preset, "VersionMajor"));
2208 *minor = hb_value_get_int(hb_dict_get(preset, "VersionMinor"));
2209 *micro = hb_value_get_int(hb_dict_get(preset, "VersionMicro"));
2210 return 0;
2214 return -1;
2217 void hb_presets_import(hb_value_t *preset)
2219 preset_import_context_t ctx;
2221 ctx.do_ctx.path.depth = 1;
2222 hb_presets_version(preset, &ctx.major, &ctx.minor, &ctx.micro);
2223 presets_do(do_preset_import, preset, (preset_do_context_t*)&ctx);
2226 char * hb_presets_import_json(const char *json)
2228 hb_value_t * dict = hb_value_json(json);
2229 if (dict == NULL)
2230 return NULL;
2232 hb_presets_import(dict);
2233 char * result = hb_value_get_json(dict);
2234 hb_value_free(&dict);
2235 return result;
2238 char * hb_presets_clean_json(const char *json)
2240 hb_value_t * dict = hb_value_json(json);
2241 if (dict == NULL)
2242 return NULL;
2244 presets_clean(dict, hb_preset_template);
2245 char * result = hb_value_get_json(dict);
2246 hb_value_free(&dict);
2247 return result;
2250 // Note that unpackage does not make any copies.
2251 // In one increases the reference count.
2252 static hb_value_t * presets_unpackage(const hb_value_t *packaged_presets)
2254 // Do any legacy translations.
2255 hb_value_t *tmp = hb_value_dup(packaged_presets);
2256 hb_presets_import(tmp);
2257 if (hb_value_type(tmp) == HB_VALUE_TYPE_ARRAY)
2259 // Not packaged
2260 return tmp;
2262 if (hb_dict_get(tmp, "PresetName") != NULL)
2264 // Bare single preset
2265 return tmp;
2267 hb_value_t *presets = hb_dict_get(tmp, "PresetList");
2268 hb_value_incref(presets);
2269 hb_value_free(&tmp);
2270 return presets;
2273 static hb_value_t * presets_package(const hb_value_t *presets)
2275 hb_dict_t *packaged_presets;
2276 if (hb_value_type(presets) != HB_VALUE_TYPE_DICT ||
2277 hb_dict_get(presets, "VersionMajor") == NULL)
2279 // Preset is not packaged
2280 packaged_presets = hb_dict_init();
2281 hb_dict_set(packaged_presets, "VersionMajor",
2282 hb_value_int(hb_preset_version_major));
2283 hb_dict_set(packaged_presets, "VersionMinor",
2284 hb_value_int(hb_preset_version_minor));
2285 hb_dict_set(packaged_presets, "VersionMicro",
2286 hb_value_int(hb_preset_version_micro));
2288 // TODO: What else do we want in the preset containers header?
2289 hb_dict_t *tmp = hb_value_dup(presets);
2290 if (hb_value_type(presets) == HB_VALUE_TYPE_DICT)
2292 hb_value_array_t *array = hb_value_array_init();
2293 hb_value_array_append(array, tmp);
2294 tmp = array;
2296 presets_clean(tmp, hb_preset_template);
2297 hb_dict_set(packaged_presets, "PresetList", tmp);
2299 else
2301 // Preset is already packaged
2302 hb_dict_t *tmp = hb_value_dup(presets);
2303 presets_clean(tmp, hb_preset_template);
2304 packaged_presets = tmp;
2306 return packaged_presets;
2309 void hb_presets_builtin_init(void)
2311 hb_value_t * dict = hb_value_json(hb_builtin_presets_json);
2312 hb_value_t * template = hb_dict_get(dict, "PresetTemplate");
2313 hb_preset_version_major = hb_value_get_int(
2314 hb_dict_get(template, "VersionMajor"));
2315 hb_preset_version_minor = hb_value_get_int(
2316 hb_dict_get(template, "VersionMinor"));
2317 hb_preset_version_micro = hb_value_get_int(
2318 hb_dict_get(template, "VersionMicro"));
2319 hb_preset_template = hb_value_dup(hb_dict_get(template, "Preset"));
2321 hb_presets_builtin = hb_value_dup(hb_dict_get(dict, "PresetBuiltin"));
2322 hb_presets_clean(hb_presets_builtin);
2324 hb_presets = hb_value_array_init();
2325 hb_value_free(&dict);
2328 void hb_presets_current_version(int *major, int* minor, int *micro)
2330 *major = hb_preset_version_major;
2331 *minor = hb_preset_version_minor;
2332 *micro = hb_preset_version_micro;
2335 int hb_presets_gui_init(void)
2337 char path[1024];
2338 hb_value_t * dict = NULL;
2340 #if defined(HB_PRESET_JSON_FILE)
2341 hb_get_user_config_filename(path, "%s", HB_PRESET_JSON_FILE);
2342 dict = hb_value_read_json(path);
2343 #endif
2344 #if defined(HB_PRESET_PLIST_FILE)
2345 if (dict == NULL)
2347 hb_get_user_config_filename(path, "%s", HB_PRESET_PLIST_FILE);
2348 dict = hb_plist_parse_file(path);
2350 #endif
2351 if (dict == NULL)
2353 hb_error("Failed to load GUI presets file");
2354 #if defined(HB_PRESET_JSON_FILE)
2355 hb_error("Attempted: %s", HB_PRESET_JSON_FILE);
2356 #endif
2357 #if defined(HB_PRESET_PLIST_FILE)
2358 hb_error("Attempted: %s", HB_PRESET_PLIST_FILE);
2359 #endif
2360 return -1;
2362 else
2364 preset_do_context_t ctx;
2365 ctx.path.depth = 1;
2366 presets_do(do_delete_builtin, dict, &ctx);
2367 int result = hb_presets_add(dict);
2368 hb_value_free(&dict);
2369 return result;
2371 return -1;
2374 hb_value_t * hb_presets_builtin_get(void)
2376 return hb_value_dup(hb_presets_builtin);
2379 char * hb_presets_builtin_get_json(void)
2381 char *json = hb_value_get_json(hb_presets_builtin);
2382 return json;
2385 // Lookup a preset in the preset list. The "name" may contain '/'
2386 // separators to explicitely specify a preset within the preset lists
2387 // folder structure.
2389 // If 'recurse' is specified, a recursive search for the first component
2390 // in the name will be performed.
2392 // I assume that the actual preset name does not include any '/'
2394 // A reference to the preset is returned
2395 static hb_preset_index_t * preset_lookup_path(const char *name, int recurse)
2397 preset_search_context_t ctx;
2398 int result;
2400 ctx.do_ctx.path.depth = 1;
2401 ctx.name = name;
2402 ctx.recurse = recurse;
2403 ctx.last_match_idx = -1;
2404 result = presets_do(do_preset_search, hb_presets,
2405 (preset_do_context_t*)&ctx);
2406 if (result != PRESET_DO_SUCCESS)
2407 ctx.do_ctx.path.depth = 0;
2409 return hb_preset_index_dup(&ctx.do_ctx.path);
2412 // Lookup a preset in the preset list. The "name" may contain '/'
2413 // separators to explicitely specify a preset within the preset lists
2414 // folder structure.
2416 // If 'recurse' is specified, a recursive search for the first component
2417 // in the name will be performed.
2419 // I assume that the actual preset name does not include any '/'
2421 // A copy of the preset is returned
2422 hb_preset_index_t * hb_preset_search_index(const char *name, int recurse)
2424 return preset_lookup_path(name, recurse);
2427 hb_value_t * hb_preset_search(const char *name, int recurse)
2429 hb_preset_index_t *path = preset_lookup_path(name, recurse);
2430 hb_value_t *preset = hb_preset_get(path);
2431 free(path);
2432 return preset;
2435 char * hb_preset_search_json(const char *name, int recurse)
2437 hb_value_t * preset;
2438 char *json;
2439 preset = hb_preset_search(name, recurse);
2440 if (preset == NULL)
2441 return NULL;
2442 json = hb_value_get_json(preset);
2443 return json;
2446 static hb_preset_index_t * lookup_default_index(hb_value_t *list)
2448 preset_do_context_t ctx;
2449 int result;
2451 ctx.path.depth = 1;
2452 result = presets_do(do_find_default, list, &ctx);
2453 if (result != PRESET_DO_SUCCESS)
2454 ctx.path.depth = 0;
2455 return hb_preset_index_dup(&ctx.path);
2458 hb_preset_index_t * hb_presets_get_default_index(void)
2460 hb_preset_index_t *path = lookup_default_index(hb_presets);
2461 return path;
2464 hb_dict_t * hb_presets_get_default(void)
2466 hb_preset_index_t *path = hb_presets_get_default_index();
2467 return hb_preset_get(path);
2470 char * hb_presets_get_default_json(void)
2472 // Look for default preset
2473 hb_value_t *def = hb_presets_get_default();
2474 return hb_value_get_json(def);
2477 void hb_presets_clear_default()
2479 preset_do_context_t ctx;
2480 ctx.path.depth = 1;
2481 presets_do(do_clear_default, hb_presets, &ctx);
2484 void hb_presets_builtin_update(void)
2486 preset_do_context_t ctx;
2487 hb_preset_index_t *path;
2488 hb_value_t *builtin;
2489 int ii;
2491 ctx.path.depth = 1;
2492 presets_do(do_delete_builtin, hb_presets, &ctx);
2494 builtin = hb_value_dup(hb_presets_builtin);
2495 path = lookup_default_index(hb_presets);
2496 if (path != NULL && path->depth != 0)
2498 // The "Default" preset is an existing custom preset.
2499 // Clear the default preset in builtins
2500 ctx.path.depth = 1;
2501 presets_do(do_clear_default, builtin, &ctx);
2503 free(path);
2505 for (ii = hb_value_array_len(builtin) - 1; ii >= 0; ii--)
2507 hb_value_t *dict;
2508 dict = hb_value_array_get(builtin, ii);
2509 hb_value_incref(dict);
2510 hb_value_array_insert(hb_presets, 0, dict);
2512 hb_value_free(&builtin);
2515 int hb_presets_add(hb_value_t *preset)
2517 hb_preset_index_t *path;
2518 int added = 0;
2520 if (preset == NULL)
2521 return -1;
2523 preset = presets_unpackage(preset);
2524 if (preset == NULL)
2525 return -1;
2527 path = lookup_default_index(preset);
2528 if (path != NULL && path->depth != 0)
2530 // There is a "Default" preset in the preset(s) being added.
2531 // Clear any existing default preset.
2532 hb_presets_clear_default();
2534 free(path);
2536 int index = hb_value_array_len(hb_presets);
2537 if (hb_value_type(preset) == HB_VALUE_TYPE_DICT)
2539 // A standalone preset or folder of presets. Add to preset array.
2540 hb_value_array_append(hb_presets, hb_value_dup(preset));
2541 added++;
2543 else if (hb_value_type(preset) == HB_VALUE_TYPE_ARRAY)
2545 // An array of presets. Add each element.
2546 int count = hb_value_array_len(preset);
2547 int ii;
2548 for (ii = 0; ii < count; ii++)
2550 hb_value_t *value = hb_value_array_get(preset, ii);
2551 hb_value_array_append(hb_presets, hb_value_dup(value));
2552 added++;
2556 hb_value_free(&preset);
2557 if (added == 0)
2559 return -1;
2562 return index;
2565 int hb_presets_add_json(const char *json)
2567 hb_value_t *preset = hb_value_json(json);
2568 if (preset == NULL)
2569 return -1;
2570 int result = hb_presets_add(preset);
2571 hb_value_free(&preset);
2572 return result;
2575 int hb_presets_version_file(const char *filename,
2576 int *major, int *minor, int *micro)
2578 int result;
2580 hb_value_t *preset = hb_value_read_json(filename);
2581 if (preset == NULL)
2582 preset = hb_plist_parse_file(filename);
2583 if (preset == NULL)
2584 return -1;
2586 result = hb_presets_version(preset, major, minor, micro);
2587 hb_value_free(&preset);
2589 return result;
2592 hb_value_t* hb_presets_read_file(const char *filename)
2594 hb_value_t *preset = hb_value_read_json(filename);
2595 if (preset == NULL)
2596 preset = hb_plist_parse_file(filename);
2597 if (preset == NULL)
2598 return NULL;
2600 return preset;
2603 char * hb_presets_read_file_json(const char *filename)
2605 char *result;
2606 hb_value_t *preset = hb_value_read_json(filename);
2607 if (preset == NULL)
2608 preset = hb_plist_parse_file(filename);
2609 if (preset == NULL)
2610 return NULL;
2612 result = hb_value_get_json(preset);
2613 return result;
2616 int hb_presets_add_file(const char *filename)
2618 hb_value_t *preset = hb_value_read_json(filename);
2619 if (preset == NULL)
2620 preset = hb_plist_parse_file(filename);
2621 if (preset == NULL)
2622 return -1;
2624 int result = hb_presets_add(preset);
2625 hb_value_free(&preset);
2627 return result;
2630 static int compare_str(const void *a, const void *b)
2632 return strncmp(*(const char**)a, *(const char**)b, PATH_MAX);
2635 int hb_presets_add_path(char * path)
2637 hb_stat_t sb;
2638 HB_DIR * dir;
2639 struct dirent * entry;
2640 char * filename;
2641 int count, ii;
2642 char ** files;
2643 int result = -1;
2645 if (hb_stat(path, &sb))
2646 return -1;
2648 if (S_ISREG(sb.st_mode))
2650 return hb_presets_add_file(path);
2653 if (!S_ISDIR(sb.st_mode))
2654 return -1;
2656 dir = hb_opendir(path);
2657 if ( dir == NULL )
2658 return -1;
2660 // Count the total number of entries
2661 count = 0;
2662 while ((entry = hb_readdir(dir)))
2664 count++;
2666 files = malloc(count * sizeof(char*));
2668 // Find all regular files
2669 ii = 0;
2670 hb_rewinddir(dir);
2671 while ((entry = hb_readdir(dir)))
2673 filename = hb_strdup_printf("%s" DIR_SEP_STR "%s", path, entry->d_name);
2674 if (hb_stat(filename, &sb))
2676 free(filename);
2677 continue;
2680 // Only load regular files
2681 if (!S_ISREG(sb.st_mode))
2683 free(filename);
2684 continue;
2686 // Only load files with .json extension
2687 if (strcmp(".json", filename + strlen(filename) - 5))
2689 free(filename);
2690 continue;
2693 files[ii++] = filename;
2695 count = ii;
2697 // Sort the files so presets get added in a consistent order
2698 qsort(files, count, sizeof(char*), compare_str);
2700 // Add preset files to preset list
2701 for (ii = 0; ii < count; ii++)
2703 int res = hb_presets_add_file(files[ii]);
2704 // return success if any one of the files is successfully loaded
2705 if (res >= 0)
2706 result = res;
2708 hb_closedir( dir );
2709 free(files);
2711 return result;
2714 hb_value_t * hb_presets_get(void)
2716 return hb_presets;
2719 char * hb_presets_get_json(void)
2721 char * result;
2722 hb_value_t *presets = hb_presets_get();
2723 result = hb_value_get_json(presets);
2724 return result;
2727 int hb_presets_write_json(const hb_value_t *preset, const char *path)
2729 hb_value_t *packaged_preset = presets_package(preset);
2730 // Packaging does some validity checks and can fail
2731 if (packaged_preset == NULL)
2732 return -1;
2733 int result = hb_value_write_json(packaged_preset, path);
2734 hb_value_free(&packaged_preset);
2735 return result;
2738 char * hb_presets_package_json(const hb_value_t *preset)
2740 hb_value_t *packaged_preset = presets_package(preset);
2741 // Packaging does some validity checks and can fail
2742 if (packaged_preset == NULL)
2743 return NULL;
2744 char *out_json = hb_value_get_json(packaged_preset);
2745 hb_value_free(&packaged_preset);
2746 return out_json;
2749 char * hb_presets_json_package(const char *in_json)
2751 hb_value_t *preset = hb_value_json(in_json);
2752 hb_value_t *packaged_preset = presets_package(preset);
2753 // Packaging does some validity checks and can fail
2754 if (packaged_preset == NULL)
2755 return NULL;
2756 char *out_json = hb_value_get_json(packaged_preset);
2757 hb_value_free(&packaged_preset);
2758 hb_value_free(&preset);
2759 return out_json;
2762 void hb_presets_free(void)
2764 hb_value_free(&hb_preset_template);
2765 hb_value_free(&hb_presets);
2766 hb_value_free(&hb_presets_builtin);
2769 hb_value_t *
2770 hb_presets_get_folder_children(const hb_preset_index_t *path)
2772 int ii, count, folder;
2773 hb_value_t *dict;
2775 if (path == NULL)
2776 return hb_presets;
2778 hb_value_t *presets = hb_presets;
2779 for (ii = 0; ii < path->depth; ii++)
2781 count = hb_value_array_len(presets);
2782 if (path->index[ii] >= count) return NULL;
2783 dict = hb_value_array_get(presets, path->index[ii]);
2784 folder = hb_value_get_bool(hb_dict_get(dict, "Folder"));
2785 if (!folder)
2786 break;
2787 presets = hb_dict_get(dict, "ChildrenArray");
2789 if (ii < path->depth)
2790 return NULL;
2791 return presets;
2794 hb_value_t *
2795 hb_preset_get(const hb_preset_index_t *path)
2797 hb_value_t *folder = NULL;
2799 if (path == NULL || path->depth <= 0)
2800 return NULL;
2802 hb_preset_index_t folder_path = *path;
2803 folder_path.depth--;
2804 folder = hb_presets_get_folder_children(&folder_path);
2805 if (folder)
2807 if (hb_value_array_len(folder) <= path->index[path->depth-1])
2809 hb_error("hb_preset_get: not found");
2811 else
2813 return hb_value_array_get(folder, path->index[path->depth-1]);
2816 else
2818 hb_error("hb_preset_get: not found");
2820 return NULL;
2824 hb_preset_set(const hb_preset_index_t *path, const hb_value_t *dict)
2826 hb_value_t *folder = NULL;
2828 if (dict == NULL || path == NULL || path->depth <= 0)
2829 return -1;
2831 hb_preset_index_t folder_path = *path;
2832 folder_path.depth--;
2833 folder = hb_presets_get_folder_children(&folder_path);
2834 if (folder)
2836 if (hb_value_array_len(folder) <= path->index[path->depth-1])
2838 hb_error("hb_preset_replace: not found");
2839 return -1;
2841 else
2843 hb_value_t *dup = hb_value_dup(dict);
2844 presets_clean(dup, hb_preset_template);
2845 hb_value_array_set(folder, path->index[path->depth-1], dup);
2848 else
2850 hb_error("hb_preset_replace: not found");
2851 return -1;
2853 return 0;
2856 int hb_preset_insert(const hb_preset_index_t *path, const hb_value_t *dict)
2858 hb_value_t *folder = NULL;
2860 if (dict == NULL || path == NULL || path->depth < 0)
2861 return -1;
2863 int index = path->index[path->depth - 1];
2864 hb_preset_index_t folder_path = *path;
2865 folder_path.depth--;
2866 folder = hb_presets_get_folder_children(&folder_path);
2867 if (folder)
2869 hb_value_t *dup = hb_value_dup(dict);
2870 presets_clean(dup, hb_preset_template);
2871 if (hb_value_array_len(folder) <= index)
2873 index = hb_value_array_len(folder);
2874 hb_value_array_append(folder, dup);
2876 else
2878 hb_value_array_insert(folder, index, dup);
2881 else
2883 hb_error("hb_preset_insert: not found");
2884 return -1;
2886 return index;
2889 int hb_preset_append(const hb_preset_index_t *path, const hb_value_t *dict)
2891 hb_value_t *folder = NULL;
2893 if (dict == NULL)
2894 return -1;
2896 folder = hb_presets_get_folder_children(path);
2897 if (folder)
2899 int index;
2900 hb_value_t *dup = hb_value_dup(dict);
2901 presets_clean(dup, hb_preset_template);
2902 index = hb_value_array_len(folder);
2903 hb_value_array_append(folder, dup);
2904 return index;
2906 else
2908 hb_error("hb_preset_append: not found");
2909 return -1;
2911 return 0;
2915 hb_preset_delete(const hb_preset_index_t *path)
2917 hb_value_t *folder = NULL;
2919 if (path == NULL)
2920 return -1;
2922 hb_preset_index_t folder_path = *path;
2923 folder_path.depth--;
2924 folder = hb_presets_get_folder_children(&folder_path);
2925 if (folder)
2927 if (hb_value_array_len(folder) <= path->index[path->depth-1])
2929 hb_error("hb_preset_delete: not found");
2930 return -1;
2932 else
2934 hb_value_array_remove(folder, path->index[path->depth-1]);
2937 else
2939 hb_error("hb_preset_delete: not found");
2940 return -1;
2942 return 0;
2945 int hb_preset_move(const hb_preset_index_t *src_path,
2946 const hb_preset_index_t *dst_path)
2948 hb_value_t *src_folder = NULL;
2949 hb_value_t *dst_folder = NULL;
2951 hb_preset_index_t src_folder_path = *src_path;
2952 hb_preset_index_t dst_folder_path = *dst_path;
2953 src_folder_path.depth--;
2954 dst_folder_path.depth--;
2955 src_folder = hb_presets_get_folder_children(&src_folder_path);
2956 dst_folder = hb_presets_get_folder_children(&dst_folder_path);
2957 if (src_folder == NULL || dst_folder == NULL)
2959 hb_error("hb_preset_move: not found");
2960 return -1;
2963 hb_value_t *dict;
2964 int src_index, dst_index;
2966 src_index = src_path->index[src_path->depth-1];
2967 dst_index = dst_path->index[src_path->depth-1];
2968 dict = hb_value_array_get(src_folder, src_index);
2969 hb_value_incref(dict);
2970 hb_value_array_remove(src_folder, src_index);
2972 // Be careful about indexes in the case that they are in the same folder
2973 if (src_folder == dst_folder && src_index < dst_index)
2974 dst_index--;
2975 if (hb_value_array_len(dst_folder) <= dst_index)
2976 hb_value_array_append(dst_folder, dict);
2977 else
2978 hb_value_array_insert(dst_folder, dst_index, dict);
2980 return 0;