diff options
-rw-r--r-- | gtk/src/audiohandler.c | 42 | ||||
-rw-r--r-- | gtk/src/hb-backend.c | 1029 | ||||
-rw-r--r-- | gtk/src/hb-backend.h | 4 | ||||
-rw-r--r-- | gtk/src/presets.c | 126 | ||||
-rw-r--r-- | libhb/common.c | 1850 | ||||
-rw-r--r-- | libhb/common.h | 210 | ||||
-rw-r--r-- | libhb/encavcodec.c | 8 | ||||
-rw-r--r-- | libhb/scan.c | 7 | ||||
-rw-r--r-- | libhb/work.c | 205 | ||||
-rw-r--r-- | macosx/Controller.m | 360 | ||||
-rw-r--r-- | macosx/HBAudio.m | 134 | ||||
-rw-r--r-- | macosx/HBAudioController.m | 64 | ||||
-rw-r--r-- | macosx/HBPreviewController.m | 4 | ||||
-rw-r--r-- | macosx/HBSubtitles.m | 2 | ||||
-rwxr-xr-x | scripts/manicure.rb | 22 | ||||
-rw-r--r-- | test/test.c | 440 |
16 files changed, 2631 insertions, 1876 deletions
diff --git a/gtk/src/audiohandler.c b/gtk/src/audiohandler.c index 2a7b619e4..72f58bfa7 100644 --- a/gtk/src/audiohandler.c +++ b/gtk/src/audiohandler.c @@ -63,12 +63,13 @@ ghb_select_audio_codec(gint mux, hb_audio_config_t *aconfig, gint acodec, gint f return hb_autopassthru_get_encoder(in_codec, copy_mask, fallback, mux); } - gint ii; // Sanitize fallback - for (ii = 0; ii < hb_audio_encoders_count; ii++) + const hb_encoder_t *enc; + for (enc = hb_audio_encoder_get_next(NULL); enc != NULL; + enc = hb_audio_encoder_get_next(enc)) { - if (hb_audio_encoders[ii].encoder == fallback && - !(hb_audio_encoders[ii].muxers & mux)) + if (enc->codec == fallback && + !(enc->muxers & mux)) { if ( mux == HB_MUX_MKV ) fallback = HB_ACODEC_LAME; @@ -82,10 +83,11 @@ ghb_select_audio_codec(gint mux, hb_audio_config_t *aconfig, gint acodec, gint f { return fallback; } - for (ii = 0; ii < hb_audio_encoders_count; ii++) + for (enc = hb_audio_encoder_get_next(NULL); enc != NULL; + enc = hb_audio_encoder_get_next(enc)) { - if (hb_audio_encoders[ii].encoder == acodec && - !(hb_audio_encoders[ii].muxers & mux)) + if (enc->codec == acodec && + !(enc->muxers & mux)) { return fallback; } @@ -198,7 +200,7 @@ ghb_sanitize_audio(GValue *settings, GValue *asettings) { if (mix == 0) mix = ghb_get_best_mix( aconfig, select_acodec, 0); - bitrate = hb_get_best_audio_bitrate(select_acodec, bitrate, sr, mix); + bitrate = hb_audio_bitrate_get_best(select_acodec, bitrate, sr, mix); ghb_settings_set_string(asettings, "AudioMixdown", ghb_lookup_combo_string("AudioMixdown", ghb_int_value(mix))); } @@ -206,7 +208,7 @@ ghb_sanitize_audio(GValue *settings, GValue *asettings) ghb_lookup_combo_string("AudioBitrate", ghb_int_value(bitrate))); ghb_settings_take_value(asettings, "AudioEncoderActual", - ghb_lookup_acodec_value(select_acodec)); + ghb_lookup_audio_encoder_value(select_acodec)); } void @@ -283,25 +285,25 @@ ghb_adjust_audio_rate_combos(signal_user_data_t *ud) { if (mix == 0) mix = ghb_get_best_mix( aconfig, select_acodec, 0); - bitrate = hb_get_best_audio_bitrate(select_acodec, bitrate, sr, mix); + bitrate = hb_audio_bitrate_get_best(select_acodec, bitrate, sr, mix); ghb_ui_update(ud, "AudioMixdown", ghb_int64_value(mix)); } if (!codec_defined_bitrate) { int low, high; mix = ghb_get_best_mix( aconfig, select_acodec, mix); - hb_get_audio_bitrate_limits(select_acodec, sr, mix, &low, &high); + hb_audio_bitrate_get_limits(select_acodec, sr, mix, &low, &high); ghb_set_bitrate_opts (ud->builder, low, high, -1); } ghb_ui_update(ud, "AudioBitrate", ghb_int64_value(bitrate)); ghb_settings_take_value(ud->settings, "AudioEncoderActual", - ghb_lookup_acodec_value(select_acodec)); + ghb_lookup_audio_encoder_value(select_acodec)); GValue *asettings = get_selected_asettings(ud); if (asettings) { ghb_settings_take_value(asettings, "AudioEncoderActual", - ghb_lookup_acodec_value(select_acodec)); + ghb_lookup_audio_encoder_value(select_acodec)); } ghb_audio_list_refresh_selected(ud); ghb_check_dependency(ud, NULL, "AudioEncoderActual"); @@ -423,7 +425,7 @@ ghb_set_pref_audio_settings(gint titleindex, GValue *settings) ghb_settings_set_string(asettings, "AudioEncoder", ghb_lookup_combo_string("AudioEncoder", ghb_int_value(acodec))); ghb_settings_set_value(asettings, "AudioEncoderActual", - ghb_lookup_acodec_value(select_acodec)); + ghb_lookup_audio_encoder_value(select_acodec)); ghb_settings_set_value(asettings, "AudioTrackQualityEnable", enable_qual); ghb_settings_set_double(asettings, "AudioTrackQuality", quality); @@ -687,7 +689,7 @@ audio_codec_changed_cb(GtkWidget *widget, signal_user_data_t *ud) track = ghb_settings_combo_int(ud->settings, "AudioTrack"); if (sr) { - sr = ghb_find_closest_audio_rate(sr); + sr = ghb_find_closest_audio_samplerate(sr); } ghb_ui_update(ud, "AudioSamplerate", ghb_int64_value(sr)); @@ -698,7 +700,7 @@ audio_codec_changed_cb(GtkWidget *widget, signal_user_data_t *ud) sr = aconfig ? aconfig->in.samplerate : 48000; } mix_code = ghb_get_best_mix( aconfig, acodec_code, mix_code); - br = hb_get_best_audio_bitrate(acodec_code, br, sr, mix_code); + br = hb_audio_bitrate_get_best(acodec_code, br, sr, mix_code); ghb_ui_update(ud, "AudioBitrate", ghb_int64_value(br)); ghb_ui_update(ud, "AudioMixdown", ghb_int64_value(mix_code)); @@ -717,8 +719,8 @@ audio_codec_changed_cb(GtkWidget *widget, signal_user_data_t *ud) float low, high, gran, defval; int dir; - hb_get_audio_quality_limits(acodec_code, &low, &high, &gran, &dir); - defval = hb_get_default_audio_quality(acodec_code); + hb_audio_quality_get_limits(acodec_code, &low, &high, &gran, &dir); + defval = hb_audio_quality_get_default(acodec_code); GtkScaleButton *sb; GtkAdjustment *adj; sb = GTK_SCALE_BUTTON(GHB_WIDGET(ud->builder, "AudioTrackQuality")); @@ -843,7 +845,7 @@ char * ghb_format_quality( const char *prefix, int codec, double quality ) { float low, high, gran; int dir; - hb_get_audio_quality_limits(codec, &low, &high, &gran, &dir); + hb_audio_quality_get_limits(codec, &low, &high, &gran, &dir); int digits = 0; float tmp = gran; @@ -868,7 +870,7 @@ quality_widget_changed_cb(GtkWidget *widget, gdouble quality, signal_user_data_t float low, high, gran; int dir; int codec = ghb_settings_combo_int(ud->settings, "AudioEncoderActual"); - hb_get_audio_quality_limits(codec, &low, &high, &gran, &dir); + hb_audio_quality_get_limits(codec, &low, &high, &gran, &dir); if (dir) { // Quality values are inverted diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c index 689c3b7e0..b3f82067c 100644 --- a/gtk/src/hb-backend.c +++ b/gtk/src/hb-backend.c @@ -173,17 +173,6 @@ combo_opts_t vqual_granularity_opts = d_vqual_granularity_opts }; -static options_map_t d_container_opts[] = -{ - {"MKV", "mkv", HB_MUX_MKV, "mkv"}, - {"MP4", "mp4", HB_MUX_MP4, "mp4"}, -}; -combo_opts_t container_opts = -{ - sizeof(d_container_opts)/sizeof(options_map_t), - d_container_opts -}; - static options_map_t d_detel_opts[] = { {"Off", "off", 0, ""}, @@ -383,7 +372,6 @@ combo_name_map_t combo_name_map[] = {"LogLongevity", &log_longevity_opts}, {"check_updates", &appcast_update_opts}, {"VideoQualityGranularity", &vqual_granularity_opts}, - {"FileFormat", &container_opts}, {"PictureDeinterlace", &deint_opts}, {"PictureDecomb", &decomb_opts}, {"PictureDetelecine", &detel_opts}, @@ -842,529 +830,532 @@ lookup_generic_option(combo_opts_t *opts, const GValue *gval) return result; } -static gint -lookup_mix_int(const GValue *mix) +static const hb_mixdown_t * +lookup_mixdown_by_int(int imix) { - gint ii; - gint result = 0; - - - if (G_VALUE_TYPE(mix) == G_TYPE_STRING) + const hb_mixdown_t *mix; + for (mix = hb_mixdown_get_next(NULL); mix != NULL; + mix = hb_mixdown_get_next(mix)) { - gchar * str = ghb_value_string(mix); - for (ii = 0; ii < hb_audio_mixdowns_count; ii++) + if (mix->amixdown == imix) { - if (strcmp(hb_audio_mixdowns[ii].short_name, str) == 0) - { - result = hb_audio_mixdowns[ii].amixdown; - break; - } - } - g_free(str); - } - else if (G_VALUE_TYPE(mix) == G_TYPE_INT || - G_VALUE_TYPE(mix) == G_TYPE_INT64 || - G_VALUE_TYPE(mix) == G_TYPE_DOUBLE) - { - gint val = ghb_value_int(mix); - for (ii = 0; ii < hb_audio_mixdowns_count; ii++) - { - if (hb_audio_mixdowns[ii].amixdown == val) - { - result = hb_audio_mixdowns[ii].amixdown; - break; - } + return mix; } } - return result; + return NULL; } -static const gchar* -lookup_mix_option(const GValue *mix) +static const hb_mixdown_t * +lookup_mixdown(const GValue *gmix) { - gint ii; - const gchar *result = "None"; + const hb_mixdown_t *mix; - - if (G_VALUE_TYPE(mix) == G_TYPE_STRING) + if (G_VALUE_TYPE(gmix) == G_TYPE_STRING) { - gchar *str = ghb_value_string(mix); - for (ii = 0; ii < hb_audio_mixdowns_count; ii++) + gchar * str = ghb_value_string(gmix); + for (mix = hb_mixdown_get_next(NULL); mix != NULL; + mix = hb_mixdown_get_next(mix)) { - if (strcmp(hb_audio_mixdowns[ii].short_name, str) == 0) + if (strcmp(mix->short_name, str) == 0) { - result = hb_audio_mixdowns[ii].human_readable_name; - break; + g_free(str); + return mix; } } g_free(str); } - else if (G_VALUE_TYPE(mix) == G_TYPE_INT || - G_VALUE_TYPE(mix) == G_TYPE_INT64 || - G_VALUE_TYPE(mix) == G_TYPE_DOUBLE) + else if (G_VALUE_TYPE(gmix) == G_TYPE_INT || + G_VALUE_TYPE(gmix) == G_TYPE_INT64 || + G_VALUE_TYPE(gmix) == G_TYPE_DOUBLE) { - gint val = ghb_value_int(mix); - for (ii = 0; ii < hb_audio_mixdowns_count; ii++) - { - if (hb_audio_mixdowns[ii].amixdown == val) - { - result = hb_audio_mixdowns[ii].human_readable_name; - break; - } - } + return lookup_mixdown_by_int(ghb_value_int(gmix)); } - return result; + return NULL; } -static const gchar* -lookup_mix_string(const GValue *mix) +static gint +lookup_mixdown_int(const GValue *gmix) { - gint ii; - const gchar *result = "none"; + const hb_mixdown_t *mix = lookup_mixdown(gmix); + if (mix != NULL) + return mix->amixdown; + return 0; +} +static const gchar* +lookup_mixdown_option(const GValue *gmix) +{ + const hb_mixdown_t *mix = lookup_mixdown(gmix); + if (mix != NULL) + return mix->name; + return "None"; +} - if (G_VALUE_TYPE(mix) == G_TYPE_STRING) - { - gchar *str = ghb_value_string(mix); - for (ii = 0; ii < hb_audio_mixdowns_count; ii++) - { - if (strcmp(hb_audio_mixdowns[ii].short_name, str) == 0) - { - result = hb_audio_mixdowns[ii].short_name; - break; - } - } - g_free(str); - } - else if (G_VALUE_TYPE(mix) == G_TYPE_INT || - G_VALUE_TYPE(mix) == G_TYPE_INT64 || - G_VALUE_TYPE(mix) == G_TYPE_DOUBLE) - { - gint val = ghb_value_int(mix); - for (ii = 0; ii < hb_audio_mixdowns_count; ii++) - { - if (hb_audio_mixdowns[ii].amixdown == val) - { - result = hb_audio_mixdowns[ii].short_name; - break; - } - } - } - return result; +static const gchar* +lookup_mixdown_string(const GValue *gmix) +{ + const hb_mixdown_t *mix = lookup_mixdown(gmix); + if (mix != NULL) + return mix->short_name; + return "none"; } -static gint -lookup_video_rate_int(const GValue *vrate) +static const hb_rate_t * +lookup_video_framerate(const GValue *grate) { - gint ii; gchar *str; - gint result = 0; + const hb_rate_t *rate; - str = ghb_value_string(vrate); - for (ii = 0; ii < hb_video_rates_count; ii++) + str = ghb_value_string(grate); + for (rate = hb_video_framerate_get_next(NULL); rate != NULL; + rate = hb_video_framerate_get_next(rate)) { - if (strcmp(hb_video_rates[ii].string, str) == 0) + if (strcmp(rate->name, str) == 0) { - result = hb_video_rates[ii].rate; - break; + g_free(str); + return rate; } } g_free(str); // Default to "same as source" - return result; + return NULL; } -static const gchar* -lookup_video_rate_option(const GValue *vrate) +static gint +lookup_video_framerate_int(const GValue *grate) { - gint ii; - gchar *str; - const gchar *result = "Same as source"; + const hb_rate_t *rate = lookup_video_framerate(grate); + if (rate != NULL) + return rate->rate; + return 0; +} - str = ghb_value_string(vrate); - for (ii = 0; ii < hb_video_rates_count; ii++) - { - if (strcmp(hb_video_rates[ii].string, str) == 0) - { - result = hb_video_rates[ii].string; - break; - } - } - g_free(str); - // Default to "same as source" - return result; +static const gchar* +lookup_video_framerate_option(const GValue *grate) +{ + const hb_rate_t *rate = lookup_video_framerate(grate); + if (rate != NULL) + return rate->name; + return "Same as source"; } -static gint -lookup_audio_rate_int(const GValue *rate) +static const hb_rate_t * +lookup_audio_samplerate(const GValue *grate) { - gint ii; - gint result = 0; + const hb_rate_t *rate; - if (G_VALUE_TYPE(rate) == G_TYPE_STRING) + if (G_VALUE_TYPE(grate) == G_TYPE_STRING) { // Coincidentally, the string "source" will return 0 // which is our flag to use "same as source" - gchar * str = ghb_value_string(rate); - for (ii = 0; ii < hb_audio_rates_count; ii++) + gchar * str = ghb_value_string(grate); + for (rate = hb_audio_samplerate_get_next(NULL); rate != NULL; + rate = hb_audio_samplerate_get_next(rate)) { - if (strcmp(hb_audio_rates[ii].string, str) == 0) + if (strcmp(rate->name, str) == 0) { - result = hb_audio_rates[ii].rate; - break; + g_free(str); + return rate; } } g_free(str); } - else if (G_VALUE_TYPE(rate) == G_TYPE_INT || - G_VALUE_TYPE(rate) == G_TYPE_INT64 || - G_VALUE_TYPE(rate) == G_TYPE_DOUBLE) + else if (G_VALUE_TYPE(grate) == G_TYPE_INT || + G_VALUE_TYPE(grate) == G_TYPE_INT64 || + G_VALUE_TYPE(grate) == G_TYPE_DOUBLE) { - for (ii = 0; ii < hb_audio_rates_count; ii++) + gint val = ghb_value_int(grate); + for (rate = hb_audio_samplerate_get_next(NULL); rate != NULL; + rate = hb_audio_samplerate_get_next(rate)) { - gint val = ghb_value_int(rate); - if (val == hb_audio_rates[ii].rate) + if (val == rate->rate) { - result = hb_audio_rates[ii].rate; - break; + return rate; } } } - return result; + return NULL; +} + +static gint +lookup_audio_samplerate_int(const GValue *grate) +{ + const hb_rate_t *rate = lookup_audio_samplerate(grate); + if (rate != NULL) + return rate->rate; + return 0; } static const gchar* -lookup_audio_rate(const GValue *rate, const char *def) +lookup_audio_samplerate_def(const GValue *grate, const char *def) { - gint ii; const gchar *result = def; - - if (G_VALUE_TYPE(rate) == G_TYPE_STRING) - { - // Coincidentally, the string "source" will return 0 - // which is our flag to use "same as source" - gchar *str = ghb_value_string(rate); - for (ii = 0; ii < hb_audio_rates_count; ii++) - { - if (strcmp(hb_audio_rates[ii].string, str) == 0) - { - result = hb_audio_rates[ii].string; - break; - } - } - g_free(str); - } - else if (G_VALUE_TYPE(rate) == G_TYPE_INT || - G_VALUE_TYPE(rate) == G_TYPE_INT64 || - G_VALUE_TYPE(rate) == G_TYPE_DOUBLE) - { - for (ii = 0; ii < hb_audio_rates_count; ii++) - { - gint val = ghb_value_int(rate); - if (val == hb_audio_rates[ii].rate) - { - result = hb_audio_rates[ii].string; - break; - } - } - } + const hb_rate_t *rate = lookup_audio_samplerate(grate); + if (rate != NULL) + return rate->name; return result; } static const gchar* -lookup_audio_rate_option(const GValue *rate) +lookup_audio_samplerate_option(const GValue *grate) { - return lookup_audio_rate(rate, "Same as Source"); + return lookup_audio_samplerate_def(grate, "Same as Source"); } static const gchar* -lookup_audio_rate_string(const GValue *rate) +lookup_audio_samplerate_string(const GValue *grate) { - return lookup_audio_rate(rate, "source"); + return lookup_audio_samplerate_def(grate, "source"); } gint -ghb_find_closest_audio_rate(gint rate) +ghb_find_closest_audio_samplerate(gint irate) { - gint ii; gint result; + const hb_rate_t *rate; result = 0; - for (ii = 0; ii < hb_audio_rates_count; ii++) + for (rate = hb_audio_samplerate_get_next(NULL); rate != NULL; + rate = hb_audio_samplerate_get_next(rate)) { - if (rate <= hb_audio_rates[ii].rate) + if (irate <= rate->rate) { - result = hb_audio_rates[ii].rate; + result = rate->rate; break; } } return result; } -hb_rate_t *ghb_audio_bitrates; -int ghb_audio_bitrates_count; +hb_rate_t *ghb_audio_bitrates = NULL; +int ghb_audio_bitrates_count = 0; +int ghb_abr_count = 0; -static gint -lookup_audio_bitrate_int(const GValue *rate) +static const hb_rate_t * +lookup_audio_bitrate(const GValue *grate) { gint ii; - gint result = 160; + const hb_rate_t *result = NULL; - if (G_VALUE_TYPE(rate) == G_TYPE_STRING) + if (G_VALUE_TYPE(grate) == G_TYPE_STRING) { // Coincidentally, the string "source" will return 0 // which is our flag to use "same as source" - gchar *str = ghb_value_string(rate); + gchar *str = ghb_value_string(grate); for (ii = 0; ii < ghb_audio_bitrates_count; ii++) { - if (strcmp(ghb_audio_bitrates[ii].string, str) == 0) + if (strcmp(ghb_audio_bitrates[ii].name, str) == 0) { - result = ghb_audio_bitrates[ii].rate; - break; + g_free(str); + return &ghb_audio_bitrates[ii]; } } g_free(str); } - else if (G_VALUE_TYPE(rate) == G_TYPE_INT || - G_VALUE_TYPE(rate) == G_TYPE_INT64 || - G_VALUE_TYPE(rate) == G_TYPE_DOUBLE) + else if (G_VALUE_TYPE(grate) == G_TYPE_INT || + G_VALUE_TYPE(grate) == G_TYPE_INT64 || + G_VALUE_TYPE(grate) == G_TYPE_DOUBLE) { - gint val = ghb_value_int(rate); + gint val = ghb_value_int(grate); for (ii = 0; ii < ghb_audio_bitrates_count; ii++) { if (ghb_audio_bitrates[ii].rate == val) { - result = ghb_audio_bitrates[ii].rate; - break; + return &ghb_audio_bitrates[ii]; } } } return result; } +static gint +lookup_audio_bitrate_int(const GValue *grate) +{ + const hb_rate_t *rate = lookup_audio_bitrate(grate); + if (rate != NULL) + return rate->rate; + return 160; +} + static const gchar* -lookup_audio_bitrate_option(const GValue *rate) +lookup_audio_bitrate_option(const GValue *grate) { - gint ii; - const gchar *result = "160"; + const hb_rate_t *rate = lookup_audio_bitrate(grate); + if (rate != NULL) + return rate->name; + return "160"; +} - if (G_VALUE_TYPE(rate) == G_TYPE_STRING) - { - // Coincidentally, the string "source" will return 0 - // which is our flag to use "same as source" - gchar *str = ghb_value_string(rate); - for (ii = 0; ii < ghb_audio_bitrates_count; ii++) - { - if (strcmp(ghb_audio_bitrates[ii].string, str) == 0) - { - result = ghb_audio_bitrates[ii].string; - break; - } - } - g_free(str); - } - else if (G_VALUE_TYPE(rate) == G_TYPE_INT || - G_VALUE_TYPE(rate) == G_TYPE_INT64 || - G_VALUE_TYPE(rate) == G_TYPE_DOUBLE) +static const hb_encoder_t * +lookup_audio_encoder_by_int(int ienc) +{ + const hb_encoder_t *enc; + for (enc = hb_audio_encoder_get_next(NULL); enc != NULL; + enc = hb_audio_encoder_get_next(enc)) { - gint val = ghb_value_int(rate); - for (ii = 0; ii < ghb_audio_bitrates_count; ii++) + if (enc->codec == ienc) { - if (ghb_audio_bitrates[ii].rate == val) - { - result = ghb_audio_bitrates[ii].string; - break; - } + return enc; } } - return result; + return NULL; } -static gint -lookup_hb_encoder_int(const GValue *enc, hb_encoder_t *encoders, int len) +static const hb_encoder_t * +lookup_audio_encoder(const GValue *genc) { - gint ii; + const hb_encoder_t *enc; - if (G_VALUE_TYPE(enc) == G_TYPE_STRING) + if (G_VALUE_TYPE(genc) == G_TYPE_STRING) { - gchar *str = ghb_value_string(enc); - for (ii = 0; ii < len; ii++) + gchar *str = ghb_value_string(genc); + for (enc = hb_audio_encoder_get_next(NULL); enc != NULL; + enc = hb_audio_encoder_get_next(enc)) { - if (strcmp(encoders[ii].human_readable_name, str) == 0 || - strcmp(encoders[ii].short_name, str) == 0) + if (strcmp(enc->name, str) == 0 || + strcmp(enc->short_name, str) == 0) { g_free(str); - return encoders[ii].encoder; + return enc; } } g_free(str); } - else if (G_VALUE_TYPE(enc) == G_TYPE_INT || - G_VALUE_TYPE(enc) == G_TYPE_INT64 || - G_VALUE_TYPE(enc) == G_TYPE_DOUBLE) + else if (G_VALUE_TYPE(genc) == G_TYPE_INT || + G_VALUE_TYPE(genc) == G_TYPE_INT64 || + G_VALUE_TYPE(genc) == G_TYPE_DOUBLE) { - int val = ghb_value_int(enc); - for (ii = 0; ii < len; ii++) - { - if (encoders[ii].encoder == val) - { - return encoders[ii].encoder; - } - } + return lookup_audio_encoder_by_int(ghb_value_int(genc)); } - return 0; + return NULL; } -static const gchar* -lookup_hb_encoder_option(const GValue *enc, hb_encoder_t *encoders, int len) +static const hb_encoder_t * +lookup_video_encoder_by_int(int ienc) { - gint ii; - - if (G_VALUE_TYPE(enc) == G_TYPE_STRING) + const hb_encoder_t *enc; + for (enc = hb_video_encoder_get_next(NULL); enc != NULL; + enc = hb_video_encoder_get_next(enc)) { - gchar *str = ghb_value_string(enc); - for (ii = 0; ii < len; ii++) + if (enc->codec == ienc) { - if (strcmp(encoders[ii].human_readable_name, str) == 0 || - strcmp(encoders[ii].short_name, str) == 0) - { - g_free(str); - return encoders[ii].human_readable_name; - } + return enc; } - g_free(str); } - else if (G_VALUE_TYPE(enc) == G_TYPE_INT || - G_VALUE_TYPE(enc) == G_TYPE_INT64 || - G_VALUE_TYPE(enc) == G_TYPE_DOUBLE) - { - int val = ghb_value_int(enc); - for (ii = 0; ii < len; ii++) - { - if (encoders[ii].encoder == val) - { - return encoders[ii].human_readable_name; - } - } - } - return 0; + return NULL; } -static const gchar* -lookup_hb_encoder_string(const GValue *enc, hb_encoder_t *encoders, int len) +static const hb_encoder_t * +lookup_video_encoder(const GValue *genc) { - gint ii; + const hb_encoder_t *enc; - if (G_VALUE_TYPE(enc) == G_TYPE_STRING) + if (G_VALUE_TYPE(genc) == G_TYPE_STRING) { - gchar *str = ghb_value_string(enc); - for (ii = 0; ii < len; ii++) + gchar *str = ghb_value_string(genc); + for (enc = hb_video_encoder_get_next(NULL); enc != NULL; + enc = hb_video_encoder_get_next(enc)) { - if (strcmp(encoders[ii].human_readable_name, str) == 0 || - strcmp(encoders[ii].short_name, str) == 0) + if (strcmp(enc->name, str) == 0 || + strcmp(enc->short_name, str) == 0) { g_free(str); - return encoders[ii].short_name; + return enc; } } g_free(str); } - else if (G_VALUE_TYPE(enc) == G_TYPE_INT || - G_VALUE_TYPE(enc) == G_TYPE_INT64 || - G_VALUE_TYPE(enc) == G_TYPE_DOUBLE) + else if (G_VALUE_TYPE(genc) == G_TYPE_INT || + G_VALUE_TYPE(genc) == G_TYPE_INT64 || + G_VALUE_TYPE(genc) == G_TYPE_DOUBLE) { - int val = ghb_value_int(enc); - for (ii = 0; ii < len; ii++) - { - if (encoders[ii].encoder == val) - { - return encoders[ii].short_name; - } - } + return lookup_video_encoder_by_int(ghb_value_int(genc)); } + return NULL; +} + +static gint +lookup_audio_encoder_int(const GValue *genc) +{ + const hb_encoder_t *enc = lookup_audio_encoder(genc); + if (enc != NULL) + return enc->codec; return 0; } static gint -lookup_audio_lang_int(const GValue *rate) +lookup_video_encoder_int(const GValue *genc) +{ + const hb_encoder_t *enc = lookup_video_encoder(genc); + if (enc != NULL) + return enc->codec; + return 0; +} + +static const gchar* +lookup_audio_encoder_option(const GValue *genc) +{ + const hb_encoder_t *enc = lookup_audio_encoder(genc); + if (enc != NULL) + return enc->name; + return NULL; +} + +static const gchar* +lookup_video_encoder_option(const GValue *genc) +{ + const hb_encoder_t *enc = lookup_video_encoder(genc); + if (enc != NULL) + return enc->name; + return NULL; +} + +static const gchar* +lookup_audio_encoder_string(const GValue *genc) +{ + const hb_encoder_t *enc = lookup_audio_encoder(genc); + if (enc != NULL) + return enc->short_name; + return NULL; +} + +static const gchar* +lookup_video_encoder_string(const GValue *genc) +{ + const hb_encoder_t *enc = lookup_video_encoder(genc); + if (enc != NULL) + return enc->short_name; + return NULL; +} + +static int +lookup_audio_lang(const GValue *glang) { gint ii; gchar *str; - gint result = 0; - // Coincidentally, the string "source" will return 0 - // which is our flag to use "same as source" - str = ghb_value_string(rate); + str = ghb_value_string(glang); for (ii = 0; ii < LANG_TABLE_SIZE; ii++) { if (strcmp(ghb_language_table[ii].iso639_2, str) == 0) { - result = ii; - break; + g_free(str); + return ii; } } g_free(str); - return result; + return -1; } -static const gchar* -lookup_audio_lang_option(const GValue *rate) +static int +lookup_audio_lang_int(const GValue *glang) { - gint ii; - gchar *str; - const gchar *result = "Same as source"; + gint ii = lookup_audio_lang(glang); + if (ii >= 0) + return ii; + return 0; +} - // Coincidentally, the string "source" will return 0 - // which is our flag to use "same as source" - str = ghb_value_string(rate); - for (ii = 0; ii < LANG_TABLE_SIZE; ii++) +static const gchar* +lookup_audio_lang_option(const GValue *glang) +{ + gint ii = lookup_audio_lang(glang); + if (ii >= 0) { - if (strcmp(ghb_language_table[ii].iso639_2, str) == 0) - { - if (ghb_language_table[ii].native_name[0] != 0) - result = ghb_language_table[ii].native_name; - else - result = ghb_language_table[ii].eng_name; - break; - } + if (ghb_language_table[ii].native_name[0] != 0) + return ghb_language_table[ii].native_name; + else + return ghb_language_table[ii].eng_name; } - g_free(str); - return result; + return "Same as source"; } GValue* -ghb_lookup_acodec_value(gint val) +ghb_lookup_audio_encoder_value(gint ienc) { - GValue *value = NULL; - gint ii; + const hb_encoder_t *enc = lookup_audio_encoder_by_int(ienc); + if (enc != NULL) + return ghb_string_value_new(enc->short_name); + return ghb_string_value_new("copy"); +} - for (ii = 0; ii < hb_audio_encoders_count; ii++) +static GValue* +lookup_mixdown_value(gint imix) +{ + const hb_mixdown_t *mix = lookup_mixdown_by_int(imix); + if (mix != NULL) + return ghb_string_value_new(mix->short_name); + return NULL; +} +static const hb_container_t * +lookup_container_by_int(int imux) +{ + const hb_container_t *mux; + for (mux = hb_container_get_next(NULL); mux != NULL; + mux = hb_container_get_next(mux)) { - if ((int)hb_audio_encoders[ii].encoder == val) + if (mux->format == imux) { - value = ghb_string_value_new(hb_audio_encoders[ii].short_name); - return value; + return mux; } } - value = ghb_string_value_new("copy"); - return value; + return NULL; } -static GValue* -get_amix_value(gint val) +static const hb_container_t * +lookup_container(const GValue *gmux) { - GValue *value = NULL; - gint ii; + const hb_container_t *mux; - for (ii = 0; ii < hb_audio_mixdowns_count; ii++) + if (G_VALUE_TYPE(gmux) == G_TYPE_STRING) { - if (hb_audio_mixdowns[ii].amixdown == val) + gchar *str = ghb_value_string(gmux); + for (mux = hb_container_get_next(NULL); mux != NULL; + mux = hb_container_get_next(mux)) { - value = ghb_string_value_new(hb_audio_mixdowns[ii].short_name); - break; + if (strcmp(mux->name, str) == 0 || + strcmp(mux->short_name, str) == 0) + { + g_free(str); + return mux; + } } + g_free(str); + } + else if (G_VALUE_TYPE(gmux) == G_TYPE_INT || + G_VALUE_TYPE(gmux) == G_TYPE_INT64 || + G_VALUE_TYPE(gmux) == G_TYPE_DOUBLE) + { + return lookup_container_by_int(ghb_value_int(gmux)); } - return value; + return NULL; +} + +static gint +lookup_container_int(const GValue *gmux) +{ + const hb_container_t *mux = lookup_container(gmux); + if (mux != NULL) + return mux->format; + return 0; +} + +static const gchar* +lookup_container_option(const GValue *gmux) +{ + const hb_container_t *mux = lookup_container(gmux); + if (mux != NULL) + return mux->name; + return 0; +} + +static const gchar* +lookup_container_string(const GValue *gmux) +{ + const hb_container_t *mux = lookup_container(gmux); + if (mux != NULL) + return mux->short_name; + return 0; } // Handle for libhb. Gets set by ghb_backend_init() @@ -1561,15 +1552,14 @@ grey_combo_box_item(GtkBuilder *builder, const gchar *name, gint value, gboolean static void grey_mix_opts(signal_user_data_t *ud, gint acodec, gint64 layout) { - gint ii; - g_debug("grey_mix_opts()\n"); - for (ii = 0; ii < hb_audio_mixdowns_count; ii++) + + const hb_mixdown_t *mix; + for (mix = hb_mixdown_get_next(NULL); mix != NULL; + mix = hb_mixdown_get_next(mix)) { - grey_combo_box_item(ud->builder, "AudioMixdown", - hb_audio_mixdowns[ii].amixdown, - !hb_mixdown_is_supported(hb_audio_mixdowns[ii].amixdown, - acodec, layout)); + grey_combo_box_item(ud->builder, "AudioMixdown", mix->amixdown, + !hb_mixdown_is_supported(mix->amixdown, acodec, layout)); } } @@ -1580,7 +1570,6 @@ ghb_grey_combo_options(signal_user_data_t *ud) gint mux, track, titleindex, acodec, fallback; hb_audio_config_t *aconfig = NULL; GValue *gval; - int ii; widget = GHB_WIDGET (ud->builder, "title"); gval = ghb_widget_value(widget); @@ -1597,34 +1586,38 @@ ghb_grey_combo_options(signal_user_data_t *ud) ghb_value_free(gval); grey_combo_box_item(ud->builder, "x264_analyse", 4, TRUE); - for (ii = 0; ii < hb_audio_encoders_count; ii++) + + const hb_encoder_t *enc; + for (enc = hb_audio_encoder_get_next(NULL); enc != NULL; + enc = hb_audio_encoder_get_next(enc)) { - if (!(mux & hb_audio_encoders[ii].muxers)) + if (!(mux & enc->muxers)) { grey_combo_box_item(ud->builder, "AudioEncoder", - hb_audio_encoders[ii].encoder, TRUE); + enc->codec, TRUE); grey_combo_box_item(ud->builder, "AudioEncoderFallback", - hb_audio_encoders[ii].encoder, TRUE); + enc->codec, TRUE); } else { grey_combo_box_item(ud->builder, "AudioEncoder", - hb_audio_encoders[ii].encoder, FALSE); + enc->codec, FALSE); grey_combo_box_item(ud->builder, "AudioEncoderFallback", - hb_audio_encoders[ii].encoder, FALSE); + enc->codec, FALSE); } } - for (ii = 0; ii < hb_video_encoders_count; ii++) + for (enc = hb_video_encoder_get_next(NULL); enc != NULL; + enc = hb_video_encoder_get_next(enc)) { - if (!(mux & hb_video_encoders[ii].muxers)) + if (!(mux & enc->muxers)) { grey_combo_box_item(ud->builder, "VideoEncoder", - hb_video_encoders[ii].encoder, TRUE); + enc->codec, TRUE); } else { grey_combo_box_item(ud->builder, "VideoEncoder", - hb_video_encoders[ii].encoder, FALSE); + enc->codec, FALSE); } } @@ -1668,9 +1661,9 @@ ghb_get_best_mix(hb_audio_config_t *aconfig, gint acodec, gint mix) layout = aconfig ? aconfig->in.channel_layout : AV_CH_LAYOUT_5POINT1; if (mix == HB_AMIXDOWN_NONE) - mix = hb_audio_mixdowns[hb_audio_mixdowns_count-1].amixdown; + mix = HB_INVALID_AMIXDOWN; - return hb_get_best_mixdown( acodec, layout, mix ); + return hb_mixdown_get_best( acodec, layout, mix ); } // Set up the model for the combo box @@ -1711,11 +1704,10 @@ init_combo_box(GtkBuilder *builder, const gchar *name) } static void -audio_samplerate_opts_set(GtkBuilder *builder, const gchar *name, hb_rate_t *rates, gint count) +audio_samplerate_opts_set(GtkBuilder *builder, const gchar *name) { GtkTreeIter iter; GtkListStore *store; - gint ii; gchar *str; g_debug("audio_samplerate_opts_set ()\n"); @@ -1730,29 +1722,31 @@ audio_samplerate_opts_set(GtkBuilder *builder, const gchar *name, hb_rate_t *rat 3, 0.0, 4, "source", -1); - for (ii = 0; ii < count; ii++) + + const hb_rate_t *rate; + for (rate = hb_audio_samplerate_get_next(NULL); rate != NULL; + rate = hb_audio_samplerate_get_next(rate)) { gtk_list_store_append(store, &iter); - str = g_strdup_printf("<small>%s</small>", rates[ii].string); + str = g_strdup_printf("<small>%s</small>", rate->name); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, - 2, rates[ii].string, - 3, (gdouble)rates[ii].rate, - 4, rates[ii].string, + 2, rate->name, + 3, (gdouble)rate->rate, + 4, rate->name, -1); g_free(str); } } static void -video_rate_opts_set(GtkBuilder *builder, const gchar *name, hb_rate_t *rates, gint count) +video_framerate_opts_set(GtkBuilder *builder, const gchar *name) { GtkTreeIter iter; GtkListStore *store; - gint ii; - g_debug("video_rate_opts_set ()\n"); + g_debug("video_framerate_opts_set ()\n"); store = get_combo_box_store(builder, name); gtk_list_store_clear(store); // Add an item for "Same As Source" @@ -1764,66 +1758,97 @@ video_rate_opts_set(GtkBuilder *builder, const gchar *name, hb_rate_t *rates, gi 3, 0.0, 4, "source", -1); - for (ii = 0; ii < count; ii++) + + const hb_rate_t *rate; + for (rate = hb_video_framerate_get_next(NULL); rate != NULL; + rate = hb_video_framerate_get_next(rate)) { gchar *desc = ""; gchar *option; - if (strcmp(rates[ii].string, "23.976") == 0) + if (strcmp(rate->name, "23.976") == 0) { desc = "(NTSC Film)"; } - else if (strcmp(rates[ii].string, "25") == 0) + else if (strcmp(rate->name, "25") == 0) { desc = "(PAL Film/Video)"; } - else if (strcmp(rates[ii].string, "29.97") == 0) + else if (strcmp(rate->name, "29.97") == 0) { desc = "(NTSC Video)"; } - option = g_strdup_printf ("%s %s", rates[ii].string, desc); + option = g_strdup_printf ("%s %s", rate->name, desc); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, option, 1, TRUE, - 2, rates[ii].string, - 3, (gdouble)rates[ii].rate, - 4, rates[ii].string, + 2, rate->name, + 3, (gdouble)rate->rate, + 4, rate->name, -1); g_free(option); } } static void -hb_encoder_opts_set_with_mask( +video_encoder_opts_set( + GtkBuilder *builder, + const gchar *name) +{ + GtkTreeIter iter; + GtkListStore *store; + gchar *str; + + g_debug("video_encoder_opts_set ()\n"); + store = get_combo_box_store(builder, name); + gtk_list_store_clear(store); + + const hb_encoder_t *enc; + for (enc = hb_video_encoder_get_next(NULL); enc != NULL; + enc = hb_video_encoder_get_next(enc)) + { + gtk_list_store_append(store, &iter); + str = g_strdup_printf("<small>%s</small>", enc->name); + gtk_list_store_set(store, &iter, + 0, str, + 1, TRUE, + 2, enc->short_name, + 3, (gdouble)enc->codec, + 4, enc->short_name, + -1); + g_free(str); + } +} + +static void +audio_encoder_opts_set_with_mask( GtkBuilder *builder, const gchar *name, - hb_encoder_t *encoders, - int len, int mask, int neg_mask) { GtkTreeIter iter; GtkListStore *store; - gint ii; gchar *str; - g_debug("hb_encoder_opts_set ()\n"); + g_debug("audio_encoder_opts_set ()\n"); store = get_combo_box_store(builder, name); gtk_list_store_clear(store); - for (ii = 0; ii < len; ii++) + + const hb_encoder_t *enc; + for (enc = hb_audio_encoder_get_next(NULL); enc != NULL; + enc = hb_audio_encoder_get_next(enc)) { - if ((mask & encoders[ii].encoder) && - !(neg_mask & encoders[ii].encoder)) + if ((mask & enc->codec) && !(neg_mask & enc->codec)) { gtk_list_store_append(store, &iter); - str = g_strdup_printf("<small>%s</small>", - encoders[ii].human_readable_name); + str = g_strdup_printf("<small>%s</small>", enc->name); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, - 2, encoders[ii].short_name, - 3, (gdouble)encoders[ii].encoder, - 4, encoders[ii].short_name, + 2, enc->short_name, + 3, (gdouble)enc->codec, + 4, enc->short_name, -1); g_free(str); } @@ -1831,20 +1856,17 @@ hb_encoder_opts_set_with_mask( } static void -hb_encoder_opts_set( +audio_encoder_opts_set( GtkBuilder *builder, - const gchar *name, - hb_encoder_t *encoders, - int len) + const gchar *name) { - hb_encoder_opts_set_with_mask(builder, name, encoders, len, ~0, 0); + audio_encoder_opts_set_with_mask(builder, name, ~0, 0); } static void acodec_fallback_opts_set(GtkBuilder *builder, const gchar *name) { - hb_encoder_opts_set_with_mask(builder, name, hb_audio_encoders, - hb_audio_encoders_count, ~0, HB_ACODEC_PASS_FLAG); + audio_encoder_opts_set_with_mask(builder, name, ~0, HB_ACODEC_PASS_FLAG); } static void @@ -1852,23 +1874,54 @@ mix_opts_set(GtkBuilder *builder, const gchar *name) { GtkTreeIter iter; GtkListStore *store; - gint ii; gchar *str; g_debug("mix_opts_set ()\n"); store = get_combo_box_store(builder, name); gtk_list_store_clear(store); - for (ii = 0; ii < hb_audio_mixdowns_count; ii++) + + const hb_mixdown_t *mix; + for (mix = hb_mixdown_get_next(NULL); mix != NULL; + mix = hb_mixdown_get_next(mix)) { gtk_list_store_append(store, &iter); - str = g_strdup_printf("<small>%s</small>", - hb_audio_mixdowns[ii].human_readable_name); + str = g_strdup_printf("<small>%s</small>", mix->name); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, - 2, hb_audio_mixdowns[ii].short_name, - 3, (gdouble)hb_audio_mixdowns[ii].amixdown, - 4, hb_audio_mixdowns[ii].internal_name, + 2, mix->short_name, + 3, (gdouble)mix->amixdown, + 4, mix->internal_name, + -1); + g_free(str); + } +} + +static void +container_opts_set( + GtkBuilder *builder, + const gchar *name) +{ + GtkTreeIter iter; + GtkListStore *store; + gchar *str; + + g_debug("hb_container_opts_set ()\n"); + store = get_combo_box_store(builder, name); + gtk_list_store_clear(store); + + const hb_container_t *mux; + for (mux = hb_container_get_next(NULL); mux != NULL; + mux = hb_container_get_next(mux)) + { + gtk_list_store_append(store, &iter); + str = g_strdup_printf("<small>%s</small>", mux->name); + gtk_list_store_set(store, &iter, + 0, str, + 1, TRUE, + 2, mux->short_name, + 3, (gdouble)mux->format, + 4, mux->short_name, -1); g_free(str); } @@ -2821,23 +2874,25 @@ ghb_lookup_combo_int(const gchar *name, const GValue *gval) if (strcmp(name, "AudioBitrate") == 0) return lookup_audio_bitrate_int(gval); else if (strcmp(name, "AudioSamplerate") == 0) - return lookup_audio_rate_int(gval); + return lookup_audio_samplerate_int(gval); else if (strcmp(name, "VideoFramerate") == 0) - return lookup_video_rate_int(gval); + return lookup_video_framerate_int(gval); else if (strcmp(name, "AudioMixdown") == 0) - return lookup_mix_int(gval); + return lookup_mixdown_int(gval); else if (strcmp(name, "SrtLanguage") == 0) return lookup_audio_lang_int(gval); else if (strcmp(name, "PreferredLanguage") == 0) return lookup_audio_lang_int(gval); else if (strcmp(name, "VideoEncoder") == 0) - return lookup_hb_encoder_int(gval, hb_video_encoders, hb_video_encoders_count); + return lookup_video_encoder_int(gval); else if (strcmp(name, "AudioEncoder") == 0) - return lookup_hb_encoder_int(gval, hb_audio_encoders, hb_audio_encoders_count); + return lookup_audio_encoder_int(gval); else if (strcmp(name, "AudioEncoderFallback") == 0) - return lookup_hb_encoder_int(gval, hb_audio_encoders, hb_audio_encoders_count); + return lookup_audio_encoder_int(gval); else if (strcmp(name, "AudioEncoderActual") == 0) - return lookup_hb_encoder_int(gval, hb_audio_encoders, hb_audio_encoders_count); + return lookup_audio_encoder_int(gval); + else if (strcmp(name, "FileFormat") == 0) + return lookup_container_int(gval); else { return lookup_generic_int(find_combo_table(name), gval); @@ -2854,23 +2909,25 @@ ghb_lookup_combo_double(const gchar *name, const GValue *gval) if (strcmp(name, "AudioBitrate") == 0) return lookup_audio_bitrate_int(gval); else if (strcmp(name, "AudioSamplerate") == 0) - return lookup_audio_rate_int(gval); + return lookup_audio_samplerate_int(gval); else if (strcmp(name, "VideoFramerate") == 0) - return lookup_video_rate_int(gval); + return lookup_video_framerate_int(gval); else if (strcmp(name, "AudioMixdown") == 0) - return lookup_mix_int(gval); + return lookup_mixdown_int(gval); else if (strcmp(name, "SrtLanguage") == 0) return lookup_audio_lang_int(gval); else if (strcmp(name, "PreferredLanguage") == 0) return lookup_audio_lang_int(gval); else if (strcmp(name, "VideoEncoder") == 0) - return lookup_hb_encoder_int(gval, hb_video_encoders, hb_video_encoders_count); + return lookup_video_encoder_int(gval); else if (strcmp(name, "AudioEncoder") == 0) - return lookup_hb_encoder_int(gval, hb_audio_encoders, hb_audio_encoders_count); + return lookup_audio_encoder_int(gval); else if (strcmp(name, "AudioEncoderFallback") == 0) - return lookup_hb_encoder_int(gval, hb_audio_encoders, hb_audio_encoders_count); + return lookup_audio_encoder_int(gval); else if (strcmp(name, "AudioEncoderActual") == 0) - return lookup_hb_encoder_int(gval, hb_audio_encoders, hb_audio_encoders_count); + return lookup_audio_encoder_int(gval); + else if (strcmp(name, "FileFormat") == 0) + return lookup_container_int(gval); else { return lookup_generic_double(find_combo_table(name), gval); @@ -2887,23 +2944,25 @@ ghb_lookup_combo_option(const gchar *name, const GValue *gval) if (strcmp(name, "AudioBitrate") == 0) return lookup_audio_bitrate_option(gval); else if (strcmp(name, "AudioSamplerate") == 0) - return lookup_audio_rate_option(gval); + return lookup_audio_samplerate_option(gval); else if (strcmp(name, "VideoFramerate") == 0) - return lookup_video_rate_option(gval); + return lookup_video_framerate_option(gval); else if (strcmp(name, "AudioMixdown") == 0) - return lookup_mix_option(gval); + return lookup_mixdown_option(gval); else if (strcmp(name, "SrtLanguage") == 0) return lookup_audio_lang_option(gval); else if (strcmp(name, "PreferredLanguage") == 0) return lookup_audio_lang_option(gval); else if (strcmp(name, "VideoEncoder") == 0) - return lookup_hb_encoder_option(gval, hb_video_encoders, hb_video_encoders_count); + return lookup_video_encoder_option(gval); else if (strcmp(name, "AudioEncoder") == 0) - return lookup_hb_encoder_option(gval, hb_audio_encoders, hb_audio_encoders_count); + return lookup_audio_encoder_option(gval); else if (strcmp(name, "AudioEncoderFallback") == 0) - return lookup_hb_encoder_option(gval, hb_audio_encoders, hb_audio_encoders_count); + return lookup_audio_encoder_option(gval); else if (strcmp(name, "AudioEncoderActual") == 0) - return lookup_hb_encoder_option(gval, hb_audio_encoders, hb_audio_encoders_count); + return lookup_audio_encoder_option(gval); + else if (strcmp(name, "FileFormat") == 0) + return lookup_container_option(gval); else { return lookup_generic_option(find_combo_table(name), gval); @@ -2920,23 +2979,25 @@ ghb_lookup_combo_string(const gchar *name, const GValue *gval) if (strcmp(name, "AudioBitrate") == 0) return lookup_audio_bitrate_option(gval); else if (strcmp(name, "AudioSamplerate") == 0) - return lookup_audio_rate_string(gval); + return lookup_audio_samplerate_string(gval); else if (strcmp(name, "VideoFramerate") == 0) - return lookup_video_rate_option(gval); + return lookup_video_framerate_option(gval); else if (strcmp(name, "AudioMixdown") == 0) - return lookup_mix_string(gval); + return lookup_mixdown_string(gval); else if (strcmp(name, "SrtLanguage") == 0) return lookup_audio_lang_option(gval); else if (strcmp(name, "PreferredLanguage") == 0) return lookup_audio_lang_option(gval); else if (strcmp(name, "VideoEncoder") == 0) - return lookup_hb_encoder_string(gval, hb_video_encoders, hb_video_encoders_count); + return lookup_video_encoder_string(gval); else if (strcmp(name, "AudioEncoder") == 0) - return lookup_hb_encoder_string(gval, hb_audio_encoders, hb_audio_encoders_count); + return lookup_audio_encoder_string(gval); else if (strcmp(name, "AudioEncoderFallback") == 0) - return lookup_hb_encoder_string(gval, hb_audio_encoders, hb_audio_encoders_count); + return lookup_audio_encoder_string(gval); else if (strcmp(name, "AudioEncoderActual") == 0) - return lookup_hb_encoder_string(gval, hb_audio_encoders, hb_audio_encoders_count); + return lookup_audio_encoder_string(gval); + else if (strcmp(name, "FileFormat") == 0) + return lookup_container_string(gval); else { return lookup_generic_string(find_combo_table(name), gval); @@ -2979,11 +3040,11 @@ ghb_update_ui_combo_box( if (all) { audio_bitrate_opts_set(ud->builder, "AudioBitrate"); - audio_samplerate_opts_set(ud->builder, "AudioSamplerate", hb_audio_rates, hb_audio_rates_count); - video_rate_opts_set(ud->builder, "VideoFramerate", hb_video_rates, hb_video_rates_count); + audio_samplerate_opts_set(ud->builder, "AudioSamplerate"); + video_framerate_opts_set(ud->builder, "VideoFramerate"); mix_opts_set(ud->builder, "AudioMixdown"); - hb_encoder_opts_set(ud->builder, "VideoEncoder", hb_video_encoders, hb_video_encoders_count); - hb_encoder_opts_set(ud->builder, "AudioEncoder", hb_audio_encoders, hb_audio_encoders_count); + video_encoder_opts_set(ud->builder, "VideoEncoder"); + audio_encoder_opts_set(ud->builder, "AudioEncoder"); acodec_fallback_opts_set(ud->builder, "AudioEncoderFallback"); language_opts_set(ud->builder, "SrtLanguage"); language_opts_set(ud->builder, "PreferredLanguage"); @@ -2999,7 +3060,6 @@ ghb_update_ui_combo_box( generic_opts_set(ud->builder, "LoggingLevel", &logging_opts); generic_opts_set(ud->builder, "LogLongevity", &log_longevity_opts); generic_opts_set(ud->builder, "check_updates", &appcast_update_opts); - generic_opts_set(ud->builder, "FileFormat", &container_opts); generic_opts_set(ud->builder, "PictureDeinterlace", &deint_opts); generic_opts_set(ud->builder, "PictureDetelecine", &detel_opts); generic_opts_set(ud->builder, "PictureDecomb", &decomb_opts); @@ -3015,21 +3075,22 @@ ghb_update_ui_combo_box( x264_tune_opts_set(ud->builder, "x264Tune"); h264_profile_opts_set(ud->builder, "h264Profile"); h264_level_opts_set(ud->builder, "h264Level"); + container_opts_set(ud->builder, "FileFormat"); } else { if (strcmp(name, "AudioBitrate") == 0) audio_bitrate_opts_set(ud->builder, "AudioBitrate"); else if (strcmp(name, "AudioSamplerate") == 0) - audio_samplerate_opts_set(ud->builder, "AudioSamplerate", hb_audio_rates, hb_audio_rates_count); + audio_samplerate_opts_set(ud->builder, "AudioSamplerate"); else if (strcmp(name, "VideoFramerate") == 0) - video_rate_opts_set(ud->builder, "VideoFramerate", hb_video_rates, hb_video_rates_count); + video_framerate_opts_set(ud->builder, "VideoFramerate"); else if (strcmp(name, "AudioMixdown") == 0) mix_opts_set(ud->builder, "AudioMixdown"); else if (strcmp(name, "VideoEncoder") == 0) - hb_encoder_opts_set(ud->builder, "VideoEncoder", hb_video_encoders, hb_video_encoders_count); + video_encoder_opts_set(ud->builder, "VideoEncoder"); else if (strcmp(name, "AudioEncoder") == 0) - hb_encoder_opts_set(ud->builder, "AudioEncoder", hb_audio_encoders, hb_audio_encoders_count); + audio_encoder_opts_set(ud->builder, "AudioEncoder"); else if (strcmp(name, "AudioEncoderFallback") == 0) acodec_fallback_opts_set(ud->builder, "AudioEncoderFallback"); else if (strcmp(name, "SrtLanguage") == 0) @@ -3050,6 +3111,8 @@ ghb_update_ui_combo_box( h264_profile_opts_set(ud->builder, "h264Profile"); else if (strcmp(name, "h264Level") == 0) h264_level_opts_set(ud->builder, "h264Level"); + else if (strcmp(name, "FileFormat") == 0) + container_opts_set(ud->builder, "FileFormat"); else generic_opts_set(ud->builder, name, find_combo_table(name)); } @@ -3079,6 +3142,7 @@ init_ui_combo_boxes(GtkBuilder *builder) init_combo_box(builder, "x264Tune"); init_combo_box(builder, "h264Profile"); init_combo_box(builder, "h264Level"); + init_combo_box(builder, "FileFormat"); for (ii = 0; combo_name_map[ii].name != NULL; ii++) { init_combo_box(builder, combo_name_map[ii].name); @@ -3325,35 +3389,33 @@ audio_bitrate_opts_add(GtkBuilder *builder, const gchar *name, gint rate) if (rate >= 0 && rate < 8) return; - if (ghb_audio_bitrates[hb_audio_bitrates_count].string) + if (ghb_audio_bitrates[ghb_abr_count].name) { - g_free((char*)ghb_audio_bitrates[hb_audio_bitrates_count].string); + g_free((char*)ghb_audio_bitrates[ghb_abr_count].name); } - ghb_audio_bitrates[hb_audio_bitrates_count].rate = rate; + ghb_audio_bitrates[ghb_abr_count].rate = rate; if (rate < 0) { - ghb_audio_bitrates[hb_audio_bitrates_count].string = - g_strdup_printf("N/A"); + ghb_audio_bitrates[ghb_abr_count].name = g_strdup_printf("N/A"); } else { - ghb_audio_bitrates[hb_audio_bitrates_count].string = - g_strdup_printf("%d", rate); + ghb_audio_bitrates[ghb_abr_count].name = g_strdup_printf("%d", rate); } - ghb_audio_bitrates_count = hb_audio_bitrates_count + 1; + ghb_audio_bitrates_count = ghb_abr_count + 1; store = get_combo_box_store(builder, name); if (!find_combo_item_by_int(GTK_TREE_MODEL(store), rate, &iter)) { str = g_strdup_printf ("<small>%s</small>", - ghb_audio_bitrates[hb_audio_bitrates_count].string); + ghb_audio_bitrates[ghb_abr_count].name); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, - 2, ghb_audio_bitrates[hb_audio_bitrates_count].string, + 2, ghb_audio_bitrates[ghb_abr_count].name, 3, (gdouble)rate, - 4, ghb_audio_bitrates[hb_audio_bitrates_count].string, + 4, ghb_audio_bitrates[ghb_abr_count].name, -1); g_free(str); } @@ -3375,7 +3437,7 @@ audio_bitrate_opts_update( guint last = (guint)last_rate; guint first = (guint)first_rate; - ghb_audio_bitrates_count = hb_audio_bitrates_count; + ghb_audio_bitrates_count = ghb_abr_count; g_debug("audio_bitrate_opts_clean ()\n"); store = get_combo_box_store(builder, name); @@ -3414,15 +3476,29 @@ audio_bitrate_opts_set(GtkBuilder *builder, const gchar *name) { GtkTreeIter iter; GtkListStore *store; - gint ii; + gint count = 0; gchar *str; + int ii; - ghb_audio_bitrates_count = hb_audio_bitrates_count; - ghb_audio_bitrates = calloc(hb_audio_bitrates_count+1, sizeof(hb_rate_t)); + const hb_rate_t *rate; - for (ii = 0; ii < hb_audio_bitrates_count; ii++) + if (ghb_audio_bitrates == NULL) { - ghb_audio_bitrates[ii] = hb_audio_bitrates[ii]; + // Count rates + for (rate = hb_audio_bitrate_get_next(NULL); rate != NULL; + rate = hb_audio_bitrate_get_next(rate)) + { + count++; + } + + ghb_audio_bitrates_count = ghb_abr_count = count; + ghb_audio_bitrates = calloc(count+1, sizeof(hb_rate_t)); + + for (ii = 0, rate = hb_audio_bitrate_get_next(NULL); rate != NULL; + ii++, rate = hb_audio_bitrate_get_next(rate)) + { + ghb_audio_bitrates[ii] = *rate; + } } g_debug("audio_bitrate_opts_set ()\n"); @@ -3432,13 +3508,13 @@ audio_bitrate_opts_set(GtkBuilder *builder, const gchar *name) { gtk_list_store_append(store, &iter); str = g_strdup_printf ("<small>%s</small>", - ghb_audio_bitrates[ii].string); + ghb_audio_bitrates[ii].name); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, - 2, ghb_audio_bitrates[ii].string, + 2, ghb_audio_bitrates[ii].name, 3, (gdouble)ghb_audio_bitrates[ii].rate, - 4, ghb_audio_bitrates[ii].string, + 4, ghb_audio_bitrates[ii].name, -1); g_free(str); } @@ -4513,7 +4589,7 @@ ghb_validate_audio(GValue *settings) { codec = HB_ACODEC_FAAC; } - value = ghb_lookup_acodec_value(codec); + value = ghb_lookup_audio_encoder_value(codec); ghb_settings_take_value(asettings, "AudioEncoder", value); } gchar *a_unsup = NULL; @@ -4540,25 +4616,18 @@ ghb_validate_audio(GValue *settings) return FALSE; } g_free(message); - value = ghb_lookup_acodec_value(codec); + value = ghb_lookup_audio_encoder_value(codec); ghb_settings_take_value(asettings, "AudioEncoder", value); } gint mix = ghb_settings_combo_int (asettings, "AudioMixdown"); - gint jj; const gchar *mix_unsup = NULL; if (!hb_mixdown_is_supported(mix, codec, aconfig->in.channel_layout)) { - for (jj = 0; jj < hb_audio_mixdowns_count; jj++) - { - if (mix == hb_audio_mixdowns[jj].amixdown) - { - { - mix_unsup = hb_audio_mixdowns[jj].human_readable_name; - } - } - } + const hb_mixdown_t *hb_mix = lookup_mixdown_by_int(mix); + if (hb_mix != NULL) + mix_unsup = hb_mix->name; } if (mix_unsup) { @@ -4573,7 +4642,7 @@ ghb_validate_audio(GValue *settings) } g_free(message); mix = ghb_get_best_mix(aconfig, codec, mix); - value = get_amix_value(mix); + value = lookup_mixdown_value(mix); ghb_settings_take_value(asettings, "AudioMixdown", value); } } @@ -5017,7 +5086,7 @@ add_job(hb_handle_t *h, GValue *js, gint unique_id, gint titleindex) audio.out.bitrate = ghb_settings_combo_int(asettings, "AudioBitrate"); - audio.out.bitrate = hb_get_best_audio_bitrate( + audio.out.bitrate = hb_audio_bitrate_get_best( audio.out.codec, audio.out.bitrate, audio.out.samplerate, audio.out.mixdown); } diff --git a/gtk/src/hb-backend.h b/gtk/src/hb-backend.h index 242a3e3b7..03d0ce4ed 100644 --- a/gtk/src/hb-backend.h +++ b/gtk/src/hb-backend.h @@ -156,7 +156,7 @@ gdouble ghb_lookup_combo_double(const gchar *name, const GValue *gval); const gchar* ghb_lookup_combo_option(const gchar *name, const GValue *gval); const gchar* ghb_lookup_combo_string(const gchar *name, const GValue *gval); gchar* ghb_get_tmp_dir(); -gint ghb_find_closest_audio_rate(gint rate); -GValue* ghb_lookup_acodec_value(gint val); +gint ghb_find_closest_audio_samplerate(gint rate); +GValue* ghb_lookup_audio_encoder_value(gint val); #endif // _HBBACKEND_H_ diff --git a/gtk/src/presets.c b/gtk/src/presets.c index d76d5d280..12fdfbd67 100644 --- a/gtk/src/presets.c +++ b/gtk/src/presets.c @@ -2044,19 +2044,26 @@ static value_map_t acodec_xlat_compat[] = static value_map_t *acodec_xlat; -static value_map_t * create_encoder_xlat_tbl(value_map_t *compat, hb_encoder_t *enc, int size) +static value_map_t * create_video_encoder_xlat_tbl(value_map_t *compat) { value_map_t *out; - int cc, ii; + int cc, ii, size = 0; for (cc = 0; compat[cc].mac_val != NULL; cc++); + const hb_encoder_t *enc; + for (enc = hb_video_encoder_get_next(NULL); enc != NULL; + enc = hb_video_encoder_get_next(enc)) + { + size++; + } out = calloc(cc + size + 1, sizeof(value_map_t)); - for (ii = 0; ii < size; ii++) + for (ii = 0, enc = hb_video_encoder_get_next(NULL); enc != NULL; + ii++, enc = hb_video_encoder_get_next(enc)) { - out[ii].mac_val = enc[ii].human_readable_name; - out[ii].lin_val = enc[ii].short_name; + out[ii].mac_val = enc->name; + out[ii].lin_val = enc->short_name; } for (ii = 0; ii < cc; ii++) @@ -2065,19 +2072,26 @@ static value_map_t * create_encoder_xlat_tbl(value_map_t *compat, hb_encoder_t * return out; } -static value_map_t * create_mix_xlat_tbl(value_map_t *compat, hb_mixdown_t * mix, int size) +static value_map_t * create_audio_encoder_xlat_tbl(value_map_t *compat) { value_map_t *out; - int cc, ii; + int cc, ii, size = 0; for (cc = 0; compat[cc].mac_val != NULL; cc++); - + + const hb_encoder_t *enc; + for (enc = hb_audio_encoder_get_next(NULL); enc != NULL; + enc = hb_audio_encoder_get_next(enc)) + { + size++; + } out = calloc(cc + size + 1, sizeof(value_map_t)); - for (ii = 0; ii < size; ii++) + for (ii = 0, enc = hb_audio_encoder_get_next(NULL); enc != NULL; + ii++, enc = hb_audio_encoder_get_next(enc)) { - out[ii].mac_val = mix[ii].human_readable_name; - out[ii].lin_val = mix[ii].short_name; + out[ii].mac_val = enc->name; + out[ii].lin_val = enc->short_name; } for (ii = 0; ii < cc; ii++) @@ -2086,15 +2100,33 @@ static value_map_t * create_mix_xlat_tbl(value_map_t *compat, hb_mixdown_t * mix return out; } -value_map_t container_xlat[] = +static value_map_t * create_mix_xlat_tbl(value_map_t *compat) { - {"MP4 file", "mp4"}, - {"M4V file", "mp4"}, - {"MKV file", "mkv"}, - {"AVI file", "mkv"}, - {"OGM file", "mkv"}, - {NULL, NULL} -}; + value_map_t *out; + int cc, ii, size = 0;; + + for (cc = 0; compat[cc].mac_val != NULL; cc++); + + const hb_mixdown_t *mix; + for (mix = hb_mixdown_get_next(NULL); mix != NULL; + mix = hb_mixdown_get_next(mix)) + { + size++; + } + out = calloc(cc + size + 1, sizeof(value_map_t)); + + for (ii = 0, mix = hb_mixdown_get_next(NULL); mix != NULL; + ii++, mix = hb_mixdown_get_next(mix)) + { + out[ii].mac_val = mix->name; + out[ii].lin_val = mix->short_name; + } + + for (ii = 0; ii < cc; ii++) + out[ii+size] = compat[ii]; + + return out; +} value_map_t framerate_xlat[] = { @@ -2361,6 +2393,24 @@ export_value_xlat2(value_map_t *value_map, GValue *lin_val, GType mac_type) return NULL; } +static GValue* +export_value_xlat_container(GValue *lin_val) +{ + GValue *sval = NULL; + gchar *str; + const gchar *mux; + int imux; + + str = ghb_value_string(lin_val); + imux = hb_container_get_from_name(str); + g_free(str); + mux = hb_container_get_name(imux); + if (mux != NULL) + sval = ghb_string_value_new(mux); + + return sval; +} + static void export_value_xlat(GValue *dict) { @@ -2374,7 +2424,7 @@ export_value_xlat(GValue *dict) ghb_dict_insert(dict, g_strdup(key), gval); key = "FileFormat"; lin_val = ghb_dict_lookup(dict, key); - gval = export_value_xlat2(container_xlat, lin_val, G_TYPE_STRING); + gval = export_value_xlat_container(lin_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); key = "VideoFramerate"; @@ -2528,6 +2578,34 @@ import_value_xlat2( return NULL; } +static GValue* +import_value_xlat_container(GValue *mac_val) +{ + GValue *sval = NULL; + gchar *str; + const gchar *mux; + int imux; + + str = ghb_value_string(mac_val); + mux = hb_container_sanitize_name(str); + g_free(str); + + if (mux == NULL) + { + imux = hb_container_get_from_extension("mp4"); + } + else + { + imux = hb_container_get_from_name(mux); + } + mux = hb_container_get_short_name(imux); + + if (mux != NULL) + sval = ghb_string_value_new(mux); + + return sval; +} + static void import_value_xlat(GValue *dict) { @@ -2542,7 +2620,7 @@ import_value_xlat(GValue *dict) ghb_dict_insert(dict, g_strdup(key), gval); key = "FileFormat"; mac_val = ghb_dict_lookup(dict, key); - gval = import_value_xlat2(defaults, container_xlat, key, mac_val); + gval = import_value_xlat_container(mac_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); key = "VideoFramerate"; @@ -3304,9 +3382,9 @@ void ghb_presets_load(signal_user_data_t *ud) { // Create translation tables from libhb tables - mix_xlat = create_mix_xlat_tbl(mix_xlat_compat, hb_audio_mixdowns, hb_audio_mixdowns_count); - acodec_xlat = create_encoder_xlat_tbl(acodec_xlat_compat, hb_audio_encoders, hb_audio_encoders_count); - vcodec_xlat = create_encoder_xlat_tbl(vcodec_xlat_compat, hb_video_encoders, hb_video_encoders_count); + mix_xlat = create_mix_xlat_tbl(mix_xlat_compat); + acodec_xlat = create_audio_encoder_xlat_tbl(acodec_xlat_compat); + vcodec_xlat = create_video_encoder_xlat_tbl(vcodec_xlat_compat); presetsPlist = load_plist("presets"); if (presetsPlist == NULL) diff --git a/libhb/common.c b/libhb/common.c index 95c9f921d..1d02af536 100644 --- a/libhb/common.c +++ b/libhb/common.c @@ -19,6 +19,8 @@ /********************************************************************** * Global variables *********************************************************************/ +static hb_error_handler_t *error_handler = NULL; + hb_rate_t hb_video_rates[] = { { "5", 5400000 }, @@ -81,8 +83,6 @@ hb_rate_t hb_audio_bitrates[] = }; int hb_audio_bitrates_count = sizeof(hb_audio_bitrates) / sizeof(hb_rate_t); -static hb_error_handler_t *error_handler = NULL; - hb_dither_t hb_audio_dithers[] = { { "default", "auto", AV_RESAMPLE_DITHER_NONE - 1, }, @@ -112,10 +112,10 @@ int hb_audio_mixdowns_count = sizeof(hb_audio_mixdowns) / sizeof(hb_mixdown_t); hb_encoder_t hb_video_encoders[] = { - { "H.264 (x264)", "x264", HB_VCODEC_X264, HB_MUX_MP4|HB_MUX_MKV }, - { "MPEG-4 (FFmpeg)", "ffmpeg4", HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MP4|HB_MUX_MKV }, - { "MPEG-2 (FFmpeg)", "ffmpeg2", HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MP4|HB_MUX_MKV }, - { "VP3 (Theora)", "theora", HB_VCODEC_THEORA, HB_MUX_MKV }, + { "H.264 (x264)", "x264", HB_VCODEC_X264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, + { "MPEG-4 (FFmpeg)", "ffmpeg4", HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, + { "MPEG-2 (FFmpeg)", "ffmpeg2", HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, + { "VP3 (Theora)", "theora", HB_VCODEC_THEORA, HB_MUX_MASK_MKV }, }; int hb_video_encoders_count = sizeof(hb_video_encoders) / sizeof(hb_encoder_t); @@ -123,566 +123,420 @@ int hb_video_encoders_count = sizeof(hb_video_encoders) / sizeof(hb_encoder_t); hb_encoder_t hb_audio_encoders[] = { #ifdef __APPLE__ - { "AAC (CoreAudio)", "ca_aac", HB_ACODEC_CA_AAC, HB_MUX_MP4|HB_MUX_MKV }, - { "HE-AAC (CoreAudio)", "ca_haac", HB_ACODEC_CA_HAAC, HB_MUX_MP4|HB_MUX_MKV }, + { "AAC (CoreAudio)", "ca_aac", HB_ACODEC_CA_AAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, + { "HE-AAC (CoreAudio)", "ca_haac", HB_ACODEC_CA_HAAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, #endif - { "AAC (faac)", "faac", HB_ACODEC_FAAC, HB_MUX_MP4|HB_MUX_MKV }, + { "AAC (faac)", "faac", HB_ACODEC_FAAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, #ifdef USE_FDK_AAC - { "AAC (FDK)", "fdk_aac", HB_ACODEC_FDK_AAC, HB_MUX_MP4|HB_MUX_MKV }, - { "HE-AAC (FDK)", "fdk_haac", HB_ACODEC_FDK_HAAC, HB_MUX_MP4|HB_MUX_MKV }, + { "AAC (FDK)", "fdk_aac", HB_ACODEC_FDK_AAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, + { "HE-AAC (FDK)", "fdk_haac", HB_ACODEC_FDK_HAAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, #endif - { "AAC (ffmpeg)", "ffaac", HB_ACODEC_FFAAC, HB_MUX_MP4|HB_MUX_MKV }, - { "AAC Passthru", "copy:aac", HB_ACODEC_AAC_PASS, HB_MUX_MP4|HB_MUX_MKV }, - { "AC3 (ffmpeg)", "ffac3", HB_ACODEC_AC3, HB_MUX_MP4|HB_MUX_MKV }, - { "AC3 Passthru", "copy:ac3", HB_ACODEC_AC3_PASS, HB_MUX_MP4|HB_MUX_MKV }, - { "DTS Passthru", "copy:dts", HB_ACODEC_DCA_PASS, HB_MUX_MP4|HB_MUX_MKV }, - { "DTS-HD Passthru", "copy:dtshd", HB_ACODEC_DCA_HD_PASS, HB_MUX_MP4|HB_MUX_MKV }, - { "MP3 (lame)", "lame", HB_ACODEC_LAME, HB_MUX_MP4|HB_MUX_MKV }, - { "MP3 Passthru", "copy:mp3", HB_ACODEC_MP3_PASS, HB_MUX_MP4|HB_MUX_MKV }, - { "Vorbis (vorbis)", "vorbis", HB_ACODEC_VORBIS, HB_MUX_MKV }, - { "FLAC (ffmpeg)", "ffflac", HB_ACODEC_FFFLAC, HB_MUX_MKV }, - { "FLAC (24-bit)", "ffflac24", HB_ACODEC_FFFLAC24, HB_MUX_MKV }, - { "Auto Passthru", "copy", HB_ACODEC_AUTO_PASS, HB_MUX_MP4|HB_MUX_MKV }, + { "AAC (ffmpeg)", "ffaac", HB_ACODEC_FFAAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, + { "AAC Passthru", "copy:aac", HB_ACODEC_AAC_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, + { "AC3 (ffmpeg)", "ffac3", HB_ACODEC_AC3, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, + { "AC3 Passthru", "copy:ac3", HB_ACODEC_AC3_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, + { "DTS Passthru", "copy:dts", HB_ACODEC_DCA_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, + { "DTS-HD Passthru", "copy:dtshd", HB_ACODEC_DCA_HD_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, + { "MP3 (lame)", "lame", HB_ACODEC_LAME, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, + { "MP3 Passthru", "copy:mp3", HB_ACODEC_MP3_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, + { "Vorbis (vorbis)", "vorbis", HB_ACODEC_VORBIS, HB_MUX_MASK_MKV }, + { "FLAC (ffmpeg)", "ffflac", HB_ACODEC_FFFLAC, HB_MUX_MASK_MKV }, + { "FLAC (24-bit)", "ffflac24", HB_ACODEC_FFFLAC24, HB_MUX_MASK_MKV }, + { "Auto Passthru", "copy", HB_ACODEC_AUTO_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV }, }; int hb_audio_encoders_count = sizeof(hb_audio_encoders) / sizeof(hb_encoder_t); -/* Expose values for PInvoke */ -hb_rate_t* hb_get_video_rates() { return hb_video_rates; } -int hb_get_video_rates_count() { return hb_video_rates_count; } -hb_rate_t* hb_get_audio_rates() { return hb_audio_rates; } -int hb_get_audio_rates_count() { return hb_audio_rates_count; } -hb_rate_t* hb_get_audio_bitrates() { return hb_audio_bitrates; } -int hb_get_audio_bitrates_count() { return hb_audio_bitrates_count; } -hb_dither_t* hb_get_audio_dithers() { return hb_audio_dithers; } -int hb_get_audio_dithers_count() { return hb_audio_dithers_count; } -hb_mixdown_t* hb_get_audio_mixdowns() { return hb_audio_mixdowns; } -int hb_get_audio_mixdowns_count() { return hb_audio_mixdowns_count; } -hb_encoder_t* hb_get_video_encoders() { return hb_video_encoders; } -int hb_get_video_encoders_count() { return hb_video_encoders_count; } -hb_encoder_t* hb_get_audio_encoders() { return hb_audio_encoders; } -int hb_get_audio_encoders_count() { return hb_audio_encoders_count; } - -int hb_audio_dither_get_default() +// note: for each container, the muxer nearer the top is the default +hb_container_t hb_containers[] = { - // "auto" - return hb_audio_dithers[0].method; -} + { "MPEG-4 (mp4v2)", "mp4v2", "mp4", HB_MUX_MP4V2, }, + { "Matroska (libmkv)", "libmkv", "mkv", HB_MUX_LIBMKV, }, +}; +int hb_containers_count = sizeof(hb_containers) / sizeof(hb_container_t); -int hb_audio_dither_get_default_method() +int hb_video_framerate_get_from_name(const char *name) { - /* - * input could be s16 (possibly already dithered) converted to flt, so - * let's use a "low-risk" dither algorithm (standard triangular). - */ - return AV_RESAMPLE_DITHER_TRIANGULAR; -} + if (name == NULL || *name == '\0') + goto fail; -int hb_audio_dither_is_supported(uint32_t codec) -{ - // encoder's input sample format must be s16(p) - switch (codec) + // TODO: implement something more flexible + if (!strcasecmp(name, "23.976 (NTSC Film)")) { - case HB_ACODEC_FFFLAC: - case HB_ACODEC_FDK_AAC: - case HB_ACODEC_FDK_HAAC: - return 1; - default: - return 0; + return 1126125; + } + if (!strcasecmp(name, "25 (PAL Film/Video)")) + { + return 1080000; + } + if (!strcasecmp(name, "29.97 (NTSC Video)")) + { + return 900900; } -} -const char* hb_audio_dither_get_description(int method) -{ int i; - for (i = 0; i < hb_audio_dithers_count; i++) + for (i = 0; i < hb_video_rates_count; i++) { - if (hb_audio_dithers[i].method == method) + if (!strcasecmp(hb_video_rates[i].name, name)) { - return hb_audio_dithers[i].description; + return hb_video_rates[i].rate; } } - return ""; -} - -int hb_mixdown_is_supported(int mixdown, uint32_t codec, uint64_t layout) -{ - return (hb_mixdown_has_codec_support(mixdown, codec) && - hb_mixdown_has_remix_support(mixdown, layout)); +fail: + return -1; } -int hb_mixdown_has_codec_support(int mixdown, uint32_t codec) +const char* hb_video_framerate_get_name(int framerate) { - // Passthru, only "None" mixdown is supported - if (codec & HB_ACODEC_PASS_FLAG) - return (mixdown == HB_AMIXDOWN_NONE); - - // Not passthru, "None" mixdown never supported - if (mixdown == HB_AMIXDOWN_NONE) - return 0; + if (framerate > hb_video_rates[0].rate || + framerate < hb_video_rates[hb_video_rates_count - 1].rate) + goto fail; - switch (codec) + int i; + for (i = 0; i < hb_video_rates_count; i++) { - case HB_ACODEC_VORBIS: - case HB_ACODEC_FFFLAC: - case HB_ACODEC_FFFLAC24: - return (mixdown <= HB_AMIXDOWN_7POINT1); - - case HB_ACODEC_LAME: - case HB_ACODEC_FFAAC: - return (mixdown <= HB_AMIXDOWN_DOLBYPLII); - - case HB_ACODEC_FAAC: - case HB_ACODEC_CA_AAC: - case HB_ACODEC_CA_HAAC: - return ((mixdown <= HB_AMIXDOWN_5POINT1) || - (mixdown == HB_AMIXDOWN_5_2_LFE)); - - default: - return (mixdown <= HB_AMIXDOWN_5POINT1); + if (hb_video_rates[i].rate == framerate) + { + return hb_video_rates[i].name; + } } + +fail: + return NULL; } -int hb_mixdown_has_remix_support(int mixdown, uint64_t layout) +const char* hb_video_framerate_sanitize_name(const char *name) { - switch (mixdown) - { - // stereo + front left/right of center - case HB_AMIXDOWN_5_2_LFE: - return ((layout & AV_CH_FRONT_LEFT_OF_CENTER) && - (layout & AV_CH_FRONT_RIGHT_OF_CENTER) && - (layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO); - - // 7.0 or better - case HB_AMIXDOWN_7POINT1: - return ((layout & AV_CH_LAYOUT_7POINT0) == AV_CH_LAYOUT_7POINT0); - - // 6.0 or better - case HB_AMIXDOWN_6POINT1: - return ((layout & AV_CH_LAYOUT_7POINT0) == AV_CH_LAYOUT_7POINT0 || - (layout & AV_CH_LAYOUT_6POINT0) == AV_CH_LAYOUT_6POINT0 || - (layout & AV_CH_LAYOUT_HEXAGONAL) == AV_CH_LAYOUT_HEXAGONAL); - - // stereo + either of front center, side or back left/right, back center - case HB_AMIXDOWN_5POINT1: - return ((layout & AV_CH_LAYOUT_2_1) == AV_CH_LAYOUT_2_1 || - (layout & AV_CH_LAYOUT_2_2) == AV_CH_LAYOUT_2_2 || - (layout & AV_CH_LAYOUT_QUAD) == AV_CH_LAYOUT_QUAD || - (layout & AV_CH_LAYOUT_SURROUND) == AV_CH_LAYOUT_SURROUND); - - // stereo + either of side or back left/right, back center - // also, allow Dolby Surrounbd output if the input is already Dolby - case HB_AMIXDOWN_DOLBY: - case HB_AMIXDOWN_DOLBYPLII: - return ((layout & AV_CH_LAYOUT_2_1) == AV_CH_LAYOUT_2_1 || - (layout & AV_CH_LAYOUT_2_2) == AV_CH_LAYOUT_2_2 || - (layout & AV_CH_LAYOUT_QUAD) == AV_CH_LAYOUT_QUAD || - (layout == AV_CH_LAYOUT_STEREO_DOWNMIX && - mixdown == HB_AMIXDOWN_DOLBY)); - - // more than 1 channel - case HB_AMIXDOWN_STEREO: - return (av_get_channel_layout_nb_channels(layout) > 1); - - // regular stereo (not Dolby) - case HB_AMIXDOWN_LEFT: - case HB_AMIXDOWN_RIGHT: - return (layout == AV_CH_LAYOUT_STEREO); - - // mono remix always supported - // HB_AMIXDOWN_NONE always supported (for Passthru) - case HB_AMIXDOWN_MONO: - case HB_AMIXDOWN_NONE: - return 1; - - // unknown mixdown, should never happen - default: - return 0; - } + return hb_video_framerate_get_name(hb_video_framerate_get_from_name(name)); } -int hb_mixdown_get_discrete_channel_count(int amixdown) +const hb_rate_t* hb_video_framerate_get_next(const hb_rate_t *last) { - switch (amixdown) + if (last == NULL) { - case HB_AMIXDOWN_5_2_LFE: - case HB_AMIXDOWN_7POINT1: - return 8; - - case HB_AMIXDOWN_6POINT1: - return 7; - - case HB_AMIXDOWN_5POINT1: - return 6; - - case HB_AMIXDOWN_MONO: - case HB_AMIXDOWN_LEFT: - case HB_AMIXDOWN_RIGHT: - return 1; - - case HB_AMIXDOWN_NONE: - return 0; - - default: - return 2; + return &hb_video_rates[0]; } -} - -int hb_mixdown_get_low_freq_channel_count(int amixdown) -{ - switch (amixdown) + if (last < &hb_video_rates[0] || + last >= &hb_video_rates[hb_video_rates_count - 1]) { - case HB_AMIXDOWN_5POINT1: - case HB_AMIXDOWN_6POINT1: - case HB_AMIXDOWN_7POINT1: - case HB_AMIXDOWN_5_2_LFE: - return 1; - - default: - return 0; + return NULL; } + return last + 1; } -int hb_mixdown_get_mixdown_from_short_name(const char *short_name) +int hb_audio_samplerate_get_best(uint32_t codec, int samplerate, int *sr_shift) { - int i; - for (i = 0; i < hb_audio_mixdowns_count; i++) + int ii, best_samplerate, samplerate_shift; + if ((samplerate < 32000) && + (codec == HB_ACODEC_CA_HAAC || codec == HB_ACODEC_AC3)) { - if (!strcmp(hb_audio_mixdowns[i].short_name, short_name)) - { - return hb_audio_mixdowns[i].amixdown; - } + // ca_haac can't do samplerates < 32 kHz + // AC-3 < 32 kHz suffers from poor hardware compatibility + best_samplerate = 32000; + samplerate_shift = 0; } - return 0; -} - -const char* hb_mixdown_get_short_name_from_mixdown(int amixdown) -{ - int i; - for (i = 0; i < hb_audio_mixdowns_count; i++) + else if (samplerate < 16000 && codec == HB_ACODEC_FDK_HAAC) { - if (hb_audio_mixdowns[i].amixdown == amixdown) - { - return hb_audio_mixdowns[i].short_name; - } + // fdk_haac can't do samplerates < 16 kHz + best_samplerate = 16000; + samplerate_shift = 1; } - return ""; -} - -void hb_autopassthru_apply_settings( hb_job_t * job ) -{ - int i, j, already_printed; - hb_audio_t * audio; - for( i = 0, already_printed = 0; i < hb_list_count( job->list_audio ); ) + else { - audio = hb_list_item( job->list_audio, i ); - if( audio->config.out.codec == HB_ACODEC_AUTO_PASS ) + best_samplerate = samplerate; + for (ii = hb_audio_rates_count - 1; ii >= 0; ii--) { - if( !already_printed ) - hb_autopassthru_print_settings( job ); - already_printed = 1; - audio->config.out.codec = hb_autopassthru_get_encoder( audio->config.in.codec, - job->acodec_copy_mask, - job->acodec_fallback, - job->mux ); - if( !( audio->config.out.codec & HB_ACODEC_PASS_FLAG ) && - !( audio->config.out.codec & HB_ACODEC_MASK ) ) - { - hb_log( "Auto Passthru: passthru not possible and no valid fallback specified, dropping track %d", - audio->config.out.track ); - hb_list_rem( job->list_audio, audio ); - hb_audio_close( &audio ); - continue; - } - audio->config.out.samplerate = audio->config.in.samplerate; - if( !( audio->config.out.codec & HB_ACODEC_PASS_FLAG ) ) - { - if( audio->config.out.codec == job->acodec_fallback ) - { - hb_log( "Auto Passthru: passthru not possible for track %d, using fallback", - audio->config.out.track ); - } - else - { - hb_log( "Auto Passthru: passthru and fallback not possible for track %d, using default encoder", - audio->config.out.track ); - } - audio->config.out.mixdown = hb_get_default_mixdown( audio->config.out.codec, - audio->config.in.channel_layout ); - audio->config.out.bitrate = hb_get_default_audio_bitrate( audio->config.out.codec, - audio->config.out.samplerate, - audio->config.out.mixdown ); - audio->config.out.compression_level = hb_get_default_audio_compression( audio->config.out.codec ); - } - else + // valid samplerate + if (best_samplerate == hb_audio_rates[ii].rate) + break; + + // samplerate is higher than the next valid samplerate, + // or lower than the lowest valid samplerate + if (best_samplerate > hb_audio_rates[ii].rate || ii == 0) { - for( j = 0; j < hb_audio_encoders_count; j++ ) - { - if( hb_audio_encoders[j].encoder == audio->config.out.codec ) - { - hb_log( "Auto Passthru: using %s for track %d", - hb_audio_encoders[j].human_readable_name, - audio->config.out.track ); - break; - } - } + best_samplerate = hb_audio_rates[ii].rate; + break; } } - /* Adjust output track number, in case we removed one. - * Output tracks sadly still need to be in sequential order. - * Note: out.track starts at 1, i starts at 0 */ - audio->config.out.track = ++i; + /* sr_shift: 0 -> 48000, 44100, 32000 Hz + * 1 -> 24000, 22050, 16000 Hz + * 2 -> 12000, 11025, 8000 Hz + * + * also, since samplerates are sanitized downwards: + * + * (samplerate < 32000) implies (samplerate <= 24000) + */ + samplerate_shift = ((best_samplerate < 16000) ? 2 : + (best_samplerate < 32000) ? 1 : 0); } + if (sr_shift != NULL) + { + *sr_shift = samplerate_shift; + } + return best_samplerate; } -void hb_autopassthru_print_settings( hb_job_t * job ) +int hb_audio_samplerate_get_from_name(const char *name) { - int i, codec_len; - char *mask = NULL, *tmp; - const char *fallback = NULL; - for( i = 0; i < hb_audio_encoders_count; i++ ) + if (name == NULL || *name == '\0') + goto fail; + + // TODO: implement something more flexible + int i = atoi(name); + if (i >= hb_audio_rates[0].rate && + i <= hb_audio_rates[hb_audio_rates_count - 1].rate) { - if( ( hb_audio_encoders[i].encoder & HB_ACODEC_PASS_FLAG ) && - ( hb_audio_encoders[i].encoder != HB_ACODEC_AUTO_PASS ) && - ( hb_audio_encoders[i].encoder & job->acodec_copy_mask ) ) - { - if( mask ) - { - tmp = hb_strncat_dup( mask, ", ", 2 ); - if( tmp ) - { - free( mask ); - mask = tmp; - } - } - // passthru name without " Passthru" - codec_len = strlen( hb_audio_encoders[i].human_readable_name ) - 9; - tmp = hb_strncat_dup( mask, hb_audio_encoders[i].human_readable_name, codec_len ); - if( tmp ) - { - free( mask ); - mask = tmp; - } - } - else if( !( hb_audio_encoders[i].encoder & HB_ACODEC_PASS_FLAG ) && - ( hb_audio_encoders[i].encoder == job->acodec_fallback ) ) + return i; + } + + for (i = 0; i < hb_audio_rates_count; i++) + { + if (!strcasecmp(hb_audio_rates[i].name, name)) { - fallback = hb_audio_encoders[i].human_readable_name; + return hb_audio_rates[i].rate; } } - if( !mask ) - hb_log( "Auto Passthru: no codecs allowed" ); - else - hb_log( "Auto Passthru: allowed codecs are %s", mask ); - if( !fallback ) - hb_log( "Auto Passthru: no valid fallback specified" ); - else - hb_log( "Auto Passthru: fallback is %s", fallback ); + +fail: + return -1; } -int hb_autopassthru_get_encoder( int in_codec, int copy_mask, int fallback, int muxer ) +const char* hb_audio_samplerate_get_name(int samplerate) { + if (samplerate < hb_audio_rates[0].rate || + samplerate > hb_audio_rates[hb_audio_rates_count - 1].rate) + goto fail; + int i; - int out_codec = ( copy_mask & in_codec ) | HB_ACODEC_PASS_FLAG; - // sanitize fallback encoder and selected passthru - // note: invalid fallbacks are caught in hb_autopassthru_apply_settings - for( i = 0; i < hb_audio_encoders_count; i++ ) + for (i = 0; i < hb_audio_rates_count; i++) { - if( ( hb_audio_encoders[i].encoder == fallback ) && - !( hb_audio_encoders[i].muxers & muxer ) ) + if (hb_audio_rates[i].rate == samplerate) { - // fallback not possible with current muxer - // use the default audio encoder instead - fallback = hb_get_default_audio_encoder(muxer); - break; + return hb_audio_rates[i].name; } } - for( i = 0; i < hb_audio_encoders_count; i++ ) - { - if( ( hb_audio_encoders[i].encoder == out_codec ) && - !( hb_audio_encoders[i].muxers & muxer ) ) - { - // selected passthru not possible with current muxer - out_codec = fallback; - break; - } - } - if( !( out_codec & HB_ACODEC_PASS_MASK ) ) - return fallback; - return out_codec; + +fail: + return NULL; } -int hb_get_default_audio_encoder(int muxer) +const hb_rate_t* hb_audio_samplerate_get_next(const hb_rate_t *last) { -#ifndef __APPLE__ - if (muxer == HB_MUX_MKV) + if (last == NULL) { - return HB_ACODEC_LAME; + return &hb_audio_rates[0]; } -#endif - return hb_audio_encoders[0].encoder; + if (last < &hb_audio_rates[0] || + last >= &hb_audio_rates[hb_audio_rates_count - 1]) + { + return NULL; + } + return last + 1; } // Given an input bitrate, find closest match in the set of allowed bitrates -int hb_find_closest_audio_bitrate(int bitrate) +static int hb_audio_bitrate_find_closest(int bitrate) { // Check if bitrate mode was disabled if (bitrate <= 0) return bitrate; - int ii, result; // result is highest rate if none found during search. // rate returned will always be <= rate asked for. - result = hb_audio_bitrates[0].rate; - for (ii = hb_audio_bitrates_count - 1; ii > 0; ii--) + int i, result = hb_audio_bitrates[0].rate; + for (i = hb_audio_bitrates_count - 1; i > 0; i--) { - if (bitrate >= hb_audio_bitrates[ii].rate) + if (bitrate >= hb_audio_bitrates[i].rate) { - result = hb_audio_bitrates[ii].rate; + result = hb_audio_bitrates[i].rate; break; } } return result; } -/* Get the bitrate low and high limits for a codec/samplerate/mixdown triplet. +// Given an input bitrate, sanitize it. +// Check low and high limits and make sure it is in the set of allowed bitrates. +int hb_audio_bitrate_get_best(uint32_t codec, int bitrate, int samplerate, + int mixdown) +{ + int low, high; + hb_audio_bitrate_get_limits(codec, samplerate, mixdown, &low, &high); + if (bitrate > high) + bitrate = high; + if (bitrate < low) + bitrate = low; + return hb_audio_bitrate_find_closest(bitrate); +} -Encoder 1.0 channel 2.0 channels 5.1 channels 6.1 channels 7.1 channels --------------------------------------------------------------------------------------- - -faac ----- -supported samplerates: 8 - 48 kHz -libfaac/util.c defines the bitrate limits: -MinBitrate() -> 8000 bps (per channel, incl. LFE). -MaxBitrate() -> (6144 * samplerate / 1024) bps (per channel, incl. LFE). -But output bitrates don't go as high as the theoretical maximums: -12 kHz 43 (72) 87 (144) 260 (432) 303 (504) 342 (576) -24 kHz 87 (144) 174 (288) 514 (864) 595 (1008) 669 (1152) -48 kHz 174 (288) 347 (576) 970 (1728) 1138 (2016) 1287 (2304) -Also, faac isn't a great encoder, so you don't want to allow too low a bitrate. -Limits: minimum of 32 Kbps per channel - maximum of 192 Kbps per channel at 32-48 kHz, adjusted for sr_shift - - -ffaac ------ -supported samplerates: 8 - 48 kHz -libavcodec/aacenc.c defines a maximum bitrate: --> 6144 * samplerate / 1024 bps (per channel, incl. LFE). -But output bitrates don't go as high as the theoretical maximums: -12 kHz 61 (72) 123 (144) -24 kHz 121 (144) 242 (288) -48 kHz 236 (288) 472 (576) -Also, ffaac isn't a great encoder, so you don't want to allow too low a bitrate. -Limits: minimum of 32 Kbps per channel - maximum of 192 Kbps per channel at 32 kHz, adjusted for sr_shift - maximum of 256 Kbps per channel at 44.1-48 kHz, adjusted for sr_shift - -vorbis ------- -supported samplerates: 8 - 48 kHz -lib/modes/setup_*.h provides a range of allowed bitrates for various configurations. -for each samplerate, the highest minimums and lowest maximums are: - 8 kHz Minimum 8 Kbps, maximum 32 Kbps (per channel, incl. LFE). -12 kHz Minimum 14 Kbps, maximum 44 Kbps (per channel, incl. LFE). -16 kHz Minimum 16 Kbps, maximum 86 Kbps (per channel, incl. LFE). -24 kHz Minimum 22 Kbps, maximum 86 Kbps (per channel, incl. LFE). -32 kHz Minimum 26 Kbps, maximum 190 Kbps (per channel, incl. LFE). -48 kHz Minimum 28 Kbps, maximum 240 Kbps (per channel, incl. LFE). -Limits: minimum of 14/22/28 Kbps per channel (8-12, 16-24, 32-48 kHz) - maximum of 32/86/190/240 Kbps per channel (8-12, 16-24, 32, 44.1-48 kHz) - -lame ----- -supported samplerates: 8 - 48 kHz -lame_init_params() allows the following bitrates: -12 kHz Minimum 8 Kbps, maximum 64 Kbps -24 kHz Minimum 8 Kbps, maximum 160 Kbps -48 kHz Minimum 32 Kbps, maximum 320 Kbps -Limits: minimum of 8/8/32 Kbps (8-12, 16-24, 32-48 kHz) - maximum of 64/160/320 Kbps (8-12, 16-24, 32-48 kHz) - -ffac3 ------ -supported samplerates: 32 - 48 kHz (< 32 kHz disabled for compatibility reasons) -Dolby's encoder has a min. of 224 Kbps for 5 full-bandwidth channels (5.0, 5.1) -The maximum AC3 bitrate is 640 Kbps -Limits: minimum of 224/5 Kbps per full-bandwidth channel, maximum of 640 Kbps - -ca_aac ------- -supported samplerates: 8 - 48 kHz -Core Audio API provides a range of allowed bitrates: - 8 kHz 8 - 24 16 - 48 40 - 112 48 - 144 56 - 160 -12 kHz 12 - 32 24 - 64 64 - 160 72 - 192 96 - 224 -16 kHz 12 - 48 24 - 96 64 - 224 72 - 288 96 - 320 -24 kHz 16 - 64 32 - 128 80 - 320 96 - 384 112 - 448 -32 kHz 24 - 96 48 - 192 128 - 448 144 - 576 192 - 640 -48 kHz 32 - 256 64 - 320 160 - 768 192 - 960 224 - 960 -Limits: - 8 kHz -> minimum of 8 Kbps and maximum of 24 Kbps per full-bandwidth channel -12 kHz -> minimum of 12 Kbps and maximum of 32 Kbps per full-bandwidth channel -16 kHz -> minimum of 12 Kbps and maximum of 48 Kbps per full-bandwidth channel -24 kHz -> minimum of 16 Kbps and maximum of 64 Kbps per full-bandwidth channel -32 kHz -> minimum of 24 Kbps and maximum of 96 Kbps per full-bandwidth channel -48 kHz -> minimum of 32 Kbps and maximum of 160 Kbps per full-bandwidth channel -48 kHz -> maximum of +96 Kbps for Mono -Note: encCoreAudioInit() will sanitize any mistake made here. - -ca_haac -------- -supported samplerates: 32 - 48 kHz -Core Audio API provides a range of allowed bitrates: -32 kHz 12 - 40 24 - 80 64 - 192 N/A 96 - 256 -48 kHz 16 - 40 32 - 80 80 - 192 N/A 112 - 256 -Limits: minimum of 12 Kbps per full-bandwidth channel (<= 32 kHz) - minimum of 16 Kbps per full-bandwidth channel ( > 32 kHz) - maximum of 40 Kbps per full-bandwidth channel -Note: encCoreAudioInit() will sanitize any mistake made here. - -fdk_aac -------- -supported samplerates: 8 - 48 kHz -libfdk limits the bitrate to the following values: - 8 kHz 48 96 240 -12 kHz 72 144 360 -16 kHz 96 192 480 -24 kHz 144 288 720 -32 kHz 192 384 960 -48 kHz 288 576 1440 -Limits: minimum of samplerate * 2/3 Kbps per full-bandwidth channel (see ca_aac) - maximum of samplerate * 6.0 Kbps per full-bandwidth channel - -fdk_haac --------- -supported samplerates: 16 - 48 kHz -libfdk limits the bitrate to the following values: -16 kHz 8 - 48 16 - 96 45 - 199 -24 kHz 8 - 63 16 - 127 45 - 266 -32 kHz 8 - 63 16 - 127 45 - 266 -48 kHz 12 - 63 16 - 127 50 - 266 -Limits: minimum of 12 Kbps per full-bandwidth channel (<= 32 kHz) (see ca_haac) - minimum of 16 Kbps per full-bandwidth channel ( > 32 kHz) (see ca_haac) - maximum of 48, 96 or 192 Kbps (1.0, 2.0, 5.1) (<= 16 kHz) - maximum of 64, 128 or 256 Kbps (1.0, 2.0, 5.1) ( > 16 kHz) -*/ - -void hb_get_audio_bitrate_limits(uint32_t codec, int samplerate, int mixdown, - int *low, int *high) +// Get the default bitrate for a given codec/samplerate/mixdown triplet. +int hb_audio_bitrate_get_default(uint32_t codec, int samplerate, int mixdown) { - if (codec & HB_ACODEC_PASS_FLAG) + if ((codec & HB_ACODEC_PASS_FLAG) || !(codec & HB_ACODEC_MASK)) + goto fail; + + int bitrate, nchannels, sr_shift; + /* full-bandwidth channels, sr_shift */ + nchannels = (hb_mixdown_get_discrete_channel_count(mixdown) - + hb_mixdown_get_low_freq_channel_count(mixdown)); + hb_audio_samplerate_get_best(codec, samplerate, &sr_shift); + + switch (codec) { - // Bitrates don't apply to passthrough audio, but may apply if we - // fallback to an encoder when the source can't be passed through. - *low = hb_audio_bitrates[0].rate; - *high = hb_audio_bitrates[hb_audio_bitrates_count-1].rate; - return; + case HB_ACODEC_FFFLAC: + case HB_ACODEC_FFFLAC24: + goto fail; + + // 96, 224, 640 Kbps + case HB_ACODEC_AC3: + bitrate = (nchannels * 128) - (32 * (nchannels < 5)); + break; + + case HB_ACODEC_CA_HAAC: + case HB_ACODEC_FDK_HAAC: + bitrate = nchannels * 32; + break; + + default: + bitrate = nchannels * 80; + break; } + // sample_rate adjustment + bitrate >>= sr_shift; + return hb_audio_bitrate_get_best(codec, bitrate, samplerate, mixdown); + +fail: + return -1; +} +/* Get the bitrate low and high limits for a codec/samplerate/mixdown triplet. + * + * Encoder 1.0 channel 2.0 channels 5.1 channels 6.1 channels 7.1 channels + * -------------------------------------------------------------------------------------- + * + * faac + * ---- + * supported samplerates: 8 - 48 kHz + * libfaac/util.c defines the bitrate limits: + * MinBitrate() -> 8000 bps (per channel, incl. LFE). + * MaxBitrate() -> (6144 * samplerate / 1024) bps (per channel, incl. LFE). + * But output bitrates don't go as high as the theoretical maximums: + * 12 kHz 43 (72) 87 (144) 260 (432) 303 (504) 342 (576) + * 24 kHz 87 (144) 174 (288) 514 (864) 595 (1008) 669 (1152) + * 48 kHz 174 (288) 347 (576) 970 (1728) 1138 (2016) 1287 (2304) + * Also, faac isn't a great encoder, so you don't want to allow too low a bitrate. + * Limits: minimum of 32 Kbps per channel + * maximum of 192 Kbps per channel at 32-48 kHz, adjusted for sr_shift + * + * + * ffaac + * ----- + * supported samplerates: 8 - 48 kHz + * libavcodec/aacenc.c defines a maximum bitrate: + * -> 6144 * samplerate / 1024 bps (per channel, incl. LFE). + * But output bitrates don't go as high as the theoretical maximums: + * 12 kHz 61 (72) 123 (144) + * 24 kHz 121 (144) 242 (288) + * 48 kHz 236 (288) 472 (576) + * Also, ffaac isn't a great encoder, so you don't want to allow too low a bitrate. + * Limits: minimum of 32 Kbps per channel + * maximum of 192 Kbps per channel at 32 kHz, adjusted for sr_shift + * maximum of 256 Kbps per channel at 44.1-48 kHz, adjusted for sr_shift + * + * vorbis + * ------ + * supported samplerates: 8 - 48 kHz + * lib/modes/setup_*.h provides a range of allowed bitrates for various configurations. + * for each samplerate, the highest minimums and lowest maximums are: + * 8 kHz Minimum 8 Kbps, maximum 32 Kbps (per channel, incl. LFE). + * 12 kHz Minimum 14 Kbps, maximum 44 Kbps (per channel, incl. LFE). + * 16 kHz Minimum 16 Kbps, maximum 86 Kbps (per channel, incl. LFE). + * 24 kHz Minimum 22 Kbps, maximum 86 Kbps (per channel, incl. LFE). + * 32 kHz Minimum 26 Kbps, maximum 190 Kbps (per channel, incl. LFE). + * 48 kHz Minimum 28 Kbps, maximum 240 Kbps (per channel, incl. LFE). + * Limits: minimum of 14/22/28 Kbps per channel (8-12, 16-24, 32-48 kHz) + * maximum of 32/86/190/240 Kbps per channel (8-12, 16-24, 32, 44.1-48 kHz) + * + * lame + * ---- + * supported samplerates: 8 - 48 kHz + * lame_init_params() allows the following bitrates: + * 12 kHz Minimum 8 Kbps, maximum 64 Kbps + * 24 kHz Minimum 8 Kbps, maximum 160 Kbps + * 48 kHz Minimum 32 Kbps, maximum 320 Kbps + * Limits: minimum of 8/8/32 Kbps (8-12, 16-24, 32-48 kHz) + * maximum of 64/160/320 Kbps (8-12, 16-24, 32-48 kHz) + * + * ffac3 + * ----- + * supported samplerates: 32 - 48 kHz (< 32 kHz disabled for compatibility reasons) + * Dolby's encoder has a min. of 224 Kbps for 5 full-bandwidth channels (5.0, 5.1) + * The maximum AC3 bitrate is 640 Kbps + * Limits: minimum of 224/5 Kbps per full-bandwidth channel, maximum of 640 Kbps + * + * ca_aac + * ------ + * supported samplerates: 8 - 48 kHz + * Core Audio API provides a range of allowed bitrates: + * 8 kHz 8 - 24 16 - 48 40 - 112 48 - 144 56 - 160 + * 12 kHz 12 - 32 24 - 64 64 - 160 72 - 192 96 - 224 + * 16 kHz 12 - 48 24 - 96 64 - 224 72 - 288 96 - 320 + * 24 kHz 16 - 64 32 - 128 80 - 320 96 - 384 112 - 448 + * 32 kHz 24 - 96 48 - 192 128 - 448 144 - 576 192 - 640 + * 48 kHz 32 - 256 64 - 320 160 - 768 192 - 960 224 - 960 + * Limits: + * 8 kHz -> minimum of 8 Kbps and maximum of 24 Kbps per full-bandwidth channel + * 12 kHz -> minimum of 12 Kbps and maximum of 32 Kbps per full-bandwidth channel + * 16 kHz -> minimum of 12 Kbps and maximum of 48 Kbps per full-bandwidth channel + * 24 kHz -> minimum of 16 Kbps and maximum of 64 Kbps per full-bandwidth channel + * 32 kHz -> minimum of 24 Kbps and maximum of 96 Kbps per full-bandwidth channel + * 48 kHz -> minimum of 32 Kbps and maximum of 160 Kbps per full-bandwidth channel + * 48 kHz -> maximum of +96 Kbps for Mono + * Note: encCoreAudioInit() will sanitize any mistake made here. + * + * ca_haac + * ------- + * supported samplerates: 32 - 48 kHz + * Core Audio API provides a range of allowed bitrates: + * 32 kHz 12 - 40 24 - 80 64 - 192 N/A 96 - 256 + * 48 kHz 16 - 40 32 - 80 80 - 192 N/A 112 - 256 + * Limits: minimum of 12 Kbps per full-bandwidth channel (<= 32 kHz) + * minimum of 16 Kbps per full-bandwidth channel ( > 32 kHz) + * maximum of 40 Kbps per full-bandwidth channel + * Note: encCoreAudioInit() will sanitize any mistake made here. + * + * fdk_aac + * ------- + * supported samplerates: 8 - 48 kHz + * libfdk limits the bitrate to the following values: + * 8 kHz 48 96 240 + * 12 kHz 72 144 360 + * 16 kHz 96 192 480 + * 24 kHz 144 288 720 + * 32 kHz 192 384 960 + * 48 kHz 288 576 1440 + * Limits: minimum of samplerate * 2/3 Kbps per full-bandwidth channel (see ca_aac) + * maximum of samplerate * 6.0 Kbps per full-bandwidth channel + * + * fdk_haac + * -------- + * supported samplerates: 16 - 48 kHz + * libfdk limits the bitrate to the following values: + * 16 kHz 8 - 48 16 - 96 45 - 199 + * 24 kHz 8 - 63 16 - 127 45 - 266 + * 32 kHz 8 - 63 16 - 127 45 - 266 + * 48 kHz 12 - 63 16 - 127 50 - 266 + * Limits: minimum of 12 Kbps per full-bandwidth channel (<= 32 kHz) (see ca_haac) + * minimum of 16 Kbps per full-bandwidth channel ( > 32 kHz) (see ca_haac) + * maximum of 48, 96 or 192 Kbps (1.0, 2.0, 5.1) (<= 16 kHz) + * maximum of 64, 128 or 256 Kbps (1.0, 2.0, 5.1) ( > 16 kHz) + */ +void hb_audio_bitrate_get_limits(uint32_t codec, int samplerate, int mixdown, + int *low, int *high) +{ /* samplerate, sr_shift */ int sr_shift; - samplerate = hb_get_best_samplerate(codec, samplerate, &sr_shift); + samplerate = hb_audio_samplerate_get_best(codec, samplerate, &sr_shift); /* LFE, full-bandwidth channels */ int lfe_count, nchannels; @@ -710,32 +564,37 @@ void hb_get_audio_bitrate_limits(uint32_t codec, int samplerate, int mixdown, *low = nchannels * 8; *high = nchannels * 24; break; + case 11025: case 12000: *low = nchannels * 12; *high = nchannels * 32; break; + case 16000: *low = nchannels * 12; *high = nchannels * 48; break; + case 22050: case 24000: *low = nchannels * 16; *high = nchannels * 64; break; + case 32000: *low = nchannels * 24; *high = nchannels * 96; break; + case 44100: case 48000: default: *low = nchannels * 32; *high = nchannels * (160 + (96 * (nchannels == 1))); break; - } - } break; + } break; + } case HB_ACODEC_CA_HAAC: *low = nchannels * (12 + (4 * (samplerate >= 44100))); @@ -781,69 +640,33 @@ void hb_get_audio_bitrate_limits(uint32_t codec, int samplerate, int mixdown, ( 50 * (samplerate >= 44100))); break; + // Bitrates don't apply to passthrough audio, but may apply if we + // fall back to an encoder when the source can't be passed through. default: *low = hb_audio_bitrates[0].rate; - *high = hb_audio_bitrates[hb_audio_bitrates_count-1].rate; + *high = hb_audio_bitrates[hb_audio_bitrates_count - 1].rate; break; } + // sanitize max. bitrate if (*high < hb_audio_bitrates[0].rate) *high = hb_audio_bitrates[0].rate; - if (*high > hb_audio_bitrates[hb_audio_bitrates_count-1].rate) - *high = hb_audio_bitrates[hb_audio_bitrates_count-1].rate; -} - -// Given an input bitrate, sanitize it. -// Check low and high limits and make sure it is in the set of allowed bitrates. -int hb_get_best_audio_bitrate(uint32_t codec, int bitrate, int samplerate, - int mixdown) -{ - int low, high; - hb_get_audio_bitrate_limits(codec, samplerate, mixdown, &low, &high); - if (bitrate > high) - bitrate = high; - if (bitrate < low) - bitrate = low; - return hb_find_closest_audio_bitrate(bitrate); + if (*high > hb_audio_bitrates[hb_audio_bitrates_count - 1].rate) + *high = hb_audio_bitrates[hb_audio_bitrates_count - 1].rate; } -// Get the default bitrate for a given codec/samplerate/mixdown triplet. -int hb_get_default_audio_bitrate(uint32_t codec, int samplerate, int mixdown) +const hb_rate_t* hb_audio_bitrate_get_next(const hb_rate_t *last) { - if (codec & HB_ACODEC_PASS_FLAG) + if (last == NULL) { - return -1; + return &hb_audio_bitrates[0]; } - - int bitrate, nchannels, sr_shift; - /* full-bandwidth channels, sr_shift */ - nchannels = (hb_mixdown_get_discrete_channel_count(mixdown) - - hb_mixdown_get_low_freq_channel_count(mixdown)); - hb_get_best_samplerate(codec, samplerate, &sr_shift); - - switch (codec) + if (last < &hb_audio_bitrates[0] || + last >= &hb_audio_bitrates[hb_audio_bitrates_count - 1]) { - case HB_ACODEC_FFFLAC: - case HB_ACODEC_FFFLAC24: - return -1; - - // 96, 224, 640 Kbps - case HB_ACODEC_AC3: - bitrate = (nchannels * 128) - (32 * (nchannels < 5)); - break; - - case HB_ACODEC_CA_HAAC: - case HB_ACODEC_FDK_HAAC: - bitrate = nchannels * 32; - break; - - default: - bitrate = nchannels * 80; - break; + return NULL; } - // sample_rate adjustment - bitrate >>= sr_shift; - return hb_get_best_audio_bitrate(codec, bitrate, samplerate, mixdown); + return last + 1; } // Get limits and hints for the UIs. @@ -851,47 +674,47 @@ int hb_get_default_audio_bitrate(uint32_t codec, int samplerate, int mixdown) // granularity sets the minimum step increments that should be used // (it's ok to round up to some nice multiple if you like) // -// direction says whether 'low' limit is highest or lowest +// direction says whether 'low' limit is highest or lowest // quality (direction 0 == lowest value is worst quality) -void hb_get_audio_quality_limits(uint32_t codec, float *low, float *high, +void hb_audio_quality_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction) { switch (codec) { case HB_ACODEC_LAME: - *direction = 1; + *direction = 1; *granularity = 0.5; - *low = 0.; - *high = 10.0; + *low = 0.; + *high = 10.; break; case HB_ACODEC_VORBIS: - *direction = 0; + *direction = 0; *granularity = 0.5; - *low = -2.0; - *high = 10.0; + *low = -2.; + *high = 10.; break; case HB_ACODEC_CA_AAC: - *direction = 0; - *granularity = 9; - *low = 1.; - *high = 127.0; + *direction = 0; + *granularity = 9.; + *low = 1.; + *high = 127.; break; default: - *direction = 0; - *granularity = 1; + *direction = 0; + *granularity = 1.; *low = *high = HB_INVALID_AUDIO_QUALITY; break; } } -float hb_get_best_audio_quality(uint32_t codec, float quality) +float hb_audio_quality_get_best(uint32_t codec, float quality) { - float low, high, granularity; int direction; - hb_get_audio_quality_limits(codec, &low, &high, &granularity, &direction); + float low, high, granularity; + hb_audio_quality_get_limits(codec, &low, &high, &granularity, &direction); if (quality > high) quality = high; if (quality < low) @@ -899,28 +722,22 @@ float hb_get_best_audio_quality(uint32_t codec, float quality) return quality; } -float hb_get_default_audio_quality( uint32_t codec ) +float hb_audio_quality_get_default(uint32_t codec) { - float quality; - switch( codec ) + switch (codec) { case HB_ACODEC_LAME: - quality = 2.; - break; + return 2.; case HB_ACODEC_VORBIS: - quality = 5.; - break; + return 5.; case HB_ACODEC_CA_AAC: - quality = 91.; - break; + return 91.; default: - quality = HB_INVALID_AUDIO_QUALITY; - break; + return HB_INVALID_AUDIO_QUALITY; } - return quality; } // Get limits and hints for the UIs. @@ -928,41 +745,41 @@ float hb_get_default_audio_quality( uint32_t codec ) // granularity sets the minimum step increments that should be used // (it's ok to round up to some nice multiple if you like) // -// direction says whether 'low' limit is highest or lowest +// direction says whether 'low' limit is highest or lowest // compression level (direction 0 == lowest value is worst compression level) -void hb_get_audio_compression_limits(uint32_t codec, float *low, float *high, +void hb_audio_compression_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction) { switch (codec) { case HB_ACODEC_FFFLAC: case HB_ACODEC_FFFLAC24: - *direction = 0; - *granularity = 1; - *high = 12; - *low = 0; + *direction = 0; + *granularity = 1.; + *high = 12.; + *low = 0.; break; - + case HB_ACODEC_LAME: - *direction = 1; - *granularity = 1; - *high = 9; - *low = 0; + *direction = 1; + *granularity = 1.; + *high = 9.; + *low = 0.; break; - + default: - *direction = 0; - *granularity = 1; - *low = *high = -1; + *direction = 0; + *granularity = 1.; + *low = *high = -1.; break; } } -float hb_get_best_audio_compression(uint32_t codec, float compression) +float hb_audio_compression_get_best(uint32_t codec, float compression) { - float low, high, granularity; int direction; - hb_get_audio_compression_limits(codec, &low, &high, &granularity, &direction); + float low, high, granularity; + hb_audio_compression_get_limits(codec, &low, &high, &granularity, &direction); if( compression > high ) compression = high; if( compression < low ) @@ -970,7 +787,7 @@ float hb_get_best_audio_compression(uint32_t codec, float compression) return compression; } -float hb_get_default_audio_compression(uint32_t codec) +float hb_audio_compression_get_default(uint32_t codec) { switch (codec) { @@ -986,7 +803,226 @@ float hb_get_default_audio_compression(uint32_t codec) } } -int hb_get_best_mixdown(uint32_t codec, uint64_t layout, int mixdown) +int hb_audio_dither_get_default() +{ + // "auto" + return hb_audio_dithers[0].method; +} + +int hb_audio_dither_get_default_method() +{ + /* + * input could be s16 (possibly already dithered) converted to flt, so + * let's use a "low-risk" dither algorithm (standard triangular). + */ + return AV_RESAMPLE_DITHER_TRIANGULAR; +} + +int hb_audio_dither_is_supported(uint32_t codec) +{ + // encoder's input sample format must be s16(p) + switch (codec) + { + case HB_ACODEC_FFFLAC: + case HB_ACODEC_FDK_AAC: + case HB_ACODEC_FDK_HAAC: + return 1; + + default: + return 0; + } +} + +int hb_audio_dither_get_from_name(const char *name) +{ + if (name == NULL || *name == '\0') + goto fail; + + int i; + for ( i = 0; i < hb_audio_dithers_count; i++) + { + if (!strcasecmp(hb_audio_dithers[i].short_name, name)) + { + return hb_audio_dithers[i].method; + } + } + +fail: + return hb_audio_dither_get_default(); +} + +const char* hb_audio_dither_get_description(int method) +{ + if (method < hb_audio_dithers[0].method || + method > hb_audio_dithers[hb_audio_dithers_count - 1].method) + goto fail; + + int i; + for (i = 0; i < hb_audio_dithers_count; i++) + { + if (hb_audio_dithers[i].method == method) + { + return hb_audio_dithers[i].description; + } + } + +fail: + return NULL; +} + +const hb_dither_t* hb_audio_dither_get_next(const hb_dither_t *last) +{ + if (last == NULL) + { + return &hb_audio_dithers[0]; + } + if (last < &hb_audio_dithers[0] || + last >= &hb_audio_dithers[hb_audio_dithers_count - 1]) + { + return NULL; + } + return last + 1; +} + +int hb_mixdown_is_supported(int mixdown, uint32_t codec, uint64_t layout) +{ + return (hb_mixdown_has_codec_support(mixdown, codec) && + hb_mixdown_has_remix_support(mixdown, layout)); +} + +int hb_mixdown_has_codec_support(int mixdown, uint32_t codec) +{ + // Passthru, only "None" mixdown is supported + if (codec & HB_ACODEC_PASS_FLAG) + return (mixdown == HB_AMIXDOWN_NONE); + + // Not passthru, "None" mixdown never supported + if (mixdown == HB_AMIXDOWN_NONE) + return 0; + + switch (codec) + { + case HB_ACODEC_VORBIS: + case HB_ACODEC_FFFLAC: + case HB_ACODEC_FFFLAC24: + return (mixdown <= HB_AMIXDOWN_7POINT1); + + case HB_ACODEC_LAME: + case HB_ACODEC_FFAAC: + return (mixdown <= HB_AMIXDOWN_DOLBYPLII); + + case HB_ACODEC_FAAC: + case HB_ACODEC_CA_AAC: + case HB_ACODEC_CA_HAAC: + return ((mixdown <= HB_AMIXDOWN_5POINT1) || + (mixdown == HB_AMIXDOWN_5_2_LFE)); + + default: + return (mixdown <= HB_AMIXDOWN_5POINT1); + } +} + +int hb_mixdown_has_remix_support(int mixdown, uint64_t layout) +{ + switch (mixdown) + { + // stereo + front left/right of center + case HB_AMIXDOWN_5_2_LFE: + return ((layout & AV_CH_FRONT_LEFT_OF_CENTER) && + (layout & AV_CH_FRONT_RIGHT_OF_CENTER) && + (layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO); + + // 7.0 or better + case HB_AMIXDOWN_7POINT1: + return ((layout & AV_CH_LAYOUT_7POINT0) == AV_CH_LAYOUT_7POINT0); + + // 6.0 or better + case HB_AMIXDOWN_6POINT1: + return ((layout & AV_CH_LAYOUT_7POINT0) == AV_CH_LAYOUT_7POINT0 || + (layout & AV_CH_LAYOUT_6POINT0) == AV_CH_LAYOUT_6POINT0 || + (layout & AV_CH_LAYOUT_HEXAGONAL) == AV_CH_LAYOUT_HEXAGONAL); + + // stereo + either of front center, side or back left/right, back center + case HB_AMIXDOWN_5POINT1: + return ((layout & AV_CH_LAYOUT_2_1) == AV_CH_LAYOUT_2_1 || + (layout & AV_CH_LAYOUT_2_2) == AV_CH_LAYOUT_2_2 || + (layout & AV_CH_LAYOUT_QUAD) == AV_CH_LAYOUT_QUAD || + (layout & AV_CH_LAYOUT_SURROUND) == AV_CH_LAYOUT_SURROUND); + + // stereo + either of side or back left/right, back center + // also, allow Dolby Surrounbd output if the input is already Dolby + case HB_AMIXDOWN_DOLBY: + case HB_AMIXDOWN_DOLBYPLII: + return ((layout & AV_CH_LAYOUT_2_1) == AV_CH_LAYOUT_2_1 || + (layout & AV_CH_LAYOUT_2_2) == AV_CH_LAYOUT_2_2 || + (layout & AV_CH_LAYOUT_QUAD) == AV_CH_LAYOUT_QUAD || + (layout == AV_CH_LAYOUT_STEREO_DOWNMIX && + mixdown == HB_AMIXDOWN_DOLBY)); + + // more than 1 channel + case HB_AMIXDOWN_STEREO: + return (av_get_channel_layout_nb_channels(layout) > 1); + + // regular stereo (not Dolby) + case HB_AMIXDOWN_LEFT: + case HB_AMIXDOWN_RIGHT: + return (layout == AV_CH_LAYOUT_STEREO); + + // mono remix always supported + // HB_AMIXDOWN_NONE always supported (for Passthru) + case HB_AMIXDOWN_MONO: + case HB_AMIXDOWN_NONE: + return 1; + + // unknown mixdown, should never happen + default: + return 0; + } +} + +int hb_mixdown_get_discrete_channel_count(int amixdown) +{ + switch (amixdown) + { + case HB_AMIXDOWN_5_2_LFE: + case HB_AMIXDOWN_7POINT1: + return 8; + + case HB_AMIXDOWN_6POINT1: + return 7; + + case HB_AMIXDOWN_5POINT1: + return 6; + + case HB_AMIXDOWN_MONO: + case HB_AMIXDOWN_LEFT: + case HB_AMIXDOWN_RIGHT: + return 1; + + case HB_AMIXDOWN_NONE: + return 0; + + default: + return 2; + } +} + +int hb_mixdown_get_low_freq_channel_count(int amixdown) +{ + switch (amixdown) + { + case HB_AMIXDOWN_5POINT1: + case HB_AMIXDOWN_6POINT1: + case HB_AMIXDOWN_7POINT1: + case HB_AMIXDOWN_5_2_LFE: + return 1; + + default: + return 0; + } +} + +int hb_mixdown_get_best(uint32_t codec, uint64_t layout, int mixdown) { // Passthru, only "None" mixdown is supported if (codec & HB_ACODEC_PASS_FLAG) @@ -996,17 +1032,17 @@ int hb_get_best_mixdown(uint32_t codec, uint64_t layout, int mixdown) if (mixdown == HB_INVALID_AMIXDOWN) mixdown = hb_audio_mixdowns[hb_audio_mixdowns_count - 1].amixdown; - int ii; // test all mixdowns until an authorized, supported mixdown is found // stop before we reach the "worst" non-None mixdown (index == 1) - for (ii = hb_audio_mixdowns_count - 1; ii > 1; ii--) - if (hb_audio_mixdowns[ii].amixdown <= mixdown && - hb_mixdown_is_supported(hb_audio_mixdowns[ii].amixdown, codec, layout)) + int i; + for (i = hb_audio_mixdowns_count - 1; i > 1; i--) + if (hb_audio_mixdowns[i].amixdown <= mixdown && + hb_mixdown_is_supported(hb_audio_mixdowns[i].amixdown, codec, layout)) break; - return hb_audio_mixdowns[ii].amixdown; + return hb_audio_mixdowns[i].amixdown; } -int hb_get_default_mixdown(uint32_t codec, uint64_t layout) +int hb_mixdown_get_default(uint32_t codec, uint64_t layout) { int mixdown; switch (codec) @@ -1016,69 +1052,641 @@ int hb_get_default_mixdown(uint32_t codec, uint64_t layout) case HB_ACODEC_FFFLAC24: mixdown = HB_AMIXDOWN_7POINT1; break; + // the AC3 encoder defaults to the best mixdown up to 5.1 case HB_ACODEC_AC3: mixdown = HB_AMIXDOWN_5POINT1; break; + // other encoders default to the best mixdown up to DPLII default: mixdown = HB_AMIXDOWN_DOLBYPLII; break; } + // return the best available mixdown up to the selected default - return hb_get_best_mixdown(codec, layout, mixdown); + return hb_mixdown_get_best(codec, layout, mixdown); } -int hb_get_best_samplerate(uint32_t codec, int samplerate, int *sr_shift) +int hb_mixdown_get_from_name(const char *name) { - int ii, best_samplerate, samplerate_shift; - if ((samplerate < 32000) && - (codec == HB_ACODEC_CA_HAAC || codec == HB_ACODEC_AC3)) + if (name == NULL || *name == '\0') + goto fail; + + // TODO: implement something more flexible + if (!strcasecmp(name, "AC3 Passthru") || + !strcasecmp(name, "DTS Passthru") || + !strcasecmp(name, "DTS-HD Passthru")) { - // ca_haac can't do samplerates < 32 kHz - // AC-3 < 32 kHz suffers from poor hardware compatibility - best_samplerate = 32000; - samplerate_shift = 0; + return HB_AMIXDOWN_NONE; } - else if (samplerate < 16000 && codec == HB_ACODEC_FDK_HAAC) + if (!strcasecmp(name, "6-channel discrete")) { - // fdk_haac can't do samplerates < 16 kHz - best_samplerate = 16000; - samplerate_shift = 1; + return HB_AMIXDOWN_5POINT1; } - else + + int i; + for (i = 0; i < hb_audio_mixdowns_count; i++) { - best_samplerate = samplerate; - for (ii = hb_audio_rates_count - 1; ii >= 0; ii--) + if (!strcasecmp(hb_audio_mixdowns[i].name, name) || + !strcasecmp(hb_audio_mixdowns[i].short_name, name)) { - // valid samplerate - if (best_samplerate == hb_audio_rates[ii].rate) - break; + return hb_audio_mixdowns[i].amixdown; + } + } - // samplerate is higher than the next valid samplerate, - // or lower than the lowest valid samplerate - if (best_samplerate > hb_audio_rates[ii].rate || ii == 0) +fail: + return -1; +} + +const char* hb_mixdown_get_name(int mixdown) +{ + if (mixdown < hb_audio_mixdowns[0].amixdown || + mixdown > hb_audio_mixdowns[hb_audio_mixdowns_count - 1].amixdown) + goto fail; + + int i; + for (i = 0; i < hb_audio_mixdowns_count; i++) + { + if (hb_audio_mixdowns[i].amixdown == mixdown) + { + return hb_audio_mixdowns[i].name; + } + } + +fail: + return NULL; +} + +const char* hb_mixdown_get_short_name(int mixdown) +{ + if (mixdown < hb_audio_mixdowns[0].amixdown || + mixdown > hb_audio_mixdowns[hb_audio_mixdowns_count - 1].amixdown) + goto fail; + + int i; + for (i = 0; i < hb_audio_mixdowns_count; i++) + { + if (hb_audio_mixdowns[i].amixdown == mixdown) + { + return hb_audio_mixdowns[i].short_name; + } + } + +fail: + return NULL; +} + +const char* hb_mixdown_sanitize_name(const char *name) +{ + return hb_mixdown_get_name(hb_mixdown_get_from_name(name)); +} + +const hb_mixdown_t* hb_mixdown_get_next(const hb_mixdown_t *last) +{ + if (last == NULL) + { + return &hb_audio_mixdowns[0]; + } + if (last < &hb_audio_mixdowns[0] || + last >= &hb_audio_mixdowns[hb_audio_mixdowns_count - 1]) + { + return NULL; + } + return last + 1; +} + +int hb_video_encoder_get_default(int muxer) +{ + if (!(muxer & HB_MUX_MASK)) + goto fail; + + int i; + for (i = 0; i < hb_video_encoders_count; i++) + { + if (hb_video_encoders[i].muxers & muxer) + { + return hb_video_encoders[i].codec; + } + } + +fail: + return -1; +} + +int hb_video_encoder_get_from_name(const char *name) +{ + if (name == NULL || *name == '\0') + goto fail; + + // TODO: implement something more flexible + if (!strcasecmp(name, "XviD") || + !strcasecmp(name, "FFmpeg")) + { + return HB_VCODEC_FFMPEG_MPEG4; + } + + int i; + for (i = 0; i < hb_video_encoders_count; i++) + { + if (!strcasecmp(hb_video_encoders[i].name, name) || + !strcasecmp(hb_video_encoders[i].short_name, name)) + { + return hb_video_encoders[i].codec; + } + } + +fail: + return -1; +} + +const char* hb_video_encoder_get_name(int encoder) +{ + if (!(encoder & HB_VCODEC_MASK)) + goto fail; + + int i; + for (i = 0; i < hb_video_encoders_count; i++) + { + if (hb_video_encoders[i].codec == encoder) + { + return hb_video_encoders[i].name; + } + } + +fail: + return NULL; +} + +const char* hb_video_encoder_get_short_name(int encoder) +{ + if (!(encoder & HB_VCODEC_MASK)) + goto fail; + + int i; + for (i = 0; i < hb_video_encoders_count; i++) + { + if (hb_video_encoders[i].codec == encoder) + { + return hb_video_encoders[i].short_name; + } + } + +fail: + return NULL; +} + +const char* hb_video_encoder_sanitize_name(const char *name) +{ + return hb_video_encoder_get_name(hb_video_encoder_get_from_name(name)); +} + +const hb_encoder_t* hb_video_encoder_get_next(const hb_encoder_t *last) +{ + if (last == NULL) + { + return &hb_video_encoders[0]; + } + if (last < &hb_video_encoders[0] || + last >= &hb_video_encoders[hb_video_encoders_count - 1]) + { + return NULL; + } + return last + 1; +} + +// for a valid passthru, return the matching encoder for that codec (if any), +// else return -1 (i.e. drop the track) +int hb_audio_encoder_get_fallback_for_passthru(int passthru) +{ + // TODO: implement something more flexible + switch (passthru) + { + case HB_ACODEC_AAC_PASS: +#ifdef __APPLE__ + return HB_ACODEC_CA_AAC; +#else + return HB_ACODEC_FAAC; +#endif + + case HB_ACODEC_AC3_PASS: + return HB_ACODEC_AC3; + + case HB_ACODEC_MP3_PASS: + return HB_ACODEC_LAME; + + // passthru tracks are often the second audio from the same source track + // if we don't have an encoder matching the passthru codec, return -1 + // dropping the track, as well as ensuring that there is at least one + // audio track in the output is then up to the UIs + default: + return -1; + } +} + +int hb_audio_encoder_get_default(int muxer) +{ + if (!(muxer & HB_MUX_MASK)) + goto fail; + +#ifndef __APPLE__ + if (muxer == HB_MUX_MKV) + { + return HB_ACODEC_LAME; + } +#endif + + int i; + for (i = 0; i < hb_audio_encoders_count; i++) + { + // default encoder should not be passthru + if ((hb_audio_encoders[i].muxers & muxer) && + (hb_audio_encoders[i].codec & HB_ACODEC_PASS_FLAG) == 0) + { + return hb_audio_encoders[i].codec; + } + } + +fail: + return -1; +} + +int hb_audio_encoder_get_from_name(const char *name) +{ + if (name == NULL || *name == '\0') + goto fail; + + // TODO: implement something more flexible + if (!strcasecmp(name, "AC3")) + { + return HB_ACODEC_AC3; + } + // libfdk fallback, use Core Audio if available, else FAAC +#ifndef USE_FDK_AAC +#ifdef __APPLE__ +#define AAC_ENC HB_ACODEC_CA_AAC +#define HAAC_ENC HB_ACODEC_CA_HAAC +#else +#define AAC_ENC HB_ACODEC_FAAC +#define HAAC_ENC HB_ACODEC_FAAC +#endif + if (!strcasecmp(name, "AAC (FDK)") || !strcasecmp(name, "fdk_aac")) + { + return AAC_ENC; + } + if (!strcasecmp(name, "HE-AAC (FDK)") || !strcasecmp(name, "fdk_haac")) + { + return HAAC_ENC; + } +#undef AAC_ENC +#undef HAAC_ENC +#endif + + int i; + for (i = 0; i < hb_audio_encoders_count; i++) + { + if (!strcasecmp(hb_audio_encoders[i].name, name) || + !strcasecmp(hb_audio_encoders[i].short_name, name)) + { + return hb_audio_encoders[i].codec; + } + } + +fail: + return -1; +} + +const char* hb_audio_encoder_get_name(int encoder) +{ + if (!(encoder & HB_ACODEC_ANY)) + goto fail; + + int i; + for (i = 0; i < hb_audio_encoders_count; i++) + { + if (hb_audio_encoders[i].codec == encoder) + { + return hb_audio_encoders[i].name; + } + } + +fail: + return NULL; +} + +const char* hb_audio_encoder_get_short_name(int encoder) +{ + if (!(encoder & HB_ACODEC_ANY)) + goto fail; + + int i; + for (i = 0; i < hb_audio_encoders_count; i++) + { + if (hb_audio_encoders[i].codec == encoder) + { + return hb_audio_encoders[i].short_name; + } + } + +fail: + return NULL; +} + +const char* hb_audio_encoder_sanitize_name(const char *name) +{ + return hb_audio_encoder_get_name(hb_audio_encoder_get_from_name(name)); +} + +const hb_encoder_t* hb_audio_encoder_get_next(const hb_encoder_t *last) +{ + if (last == NULL) + { + return &hb_audio_encoders[0]; + } + if (last < &hb_audio_encoders[0] || + last >= &hb_audio_encoders[hb_audio_encoders_count - 1]) + { + return NULL; + } + return last + 1; +} + +void hb_autopassthru_apply_settings(hb_job_t *job) +{ + hb_audio_t *audio; + int i, already_printed; + for (i = already_printed = 0; i < hb_list_count(job->list_audio);) + { + audio = hb_list_item(job->list_audio, i); + if (audio->config.out.codec == HB_ACODEC_AUTO_PASS) + { + if (!already_printed) + hb_autopassthru_print_settings(job); + already_printed = 1; + audio->config.out.codec = hb_autopassthru_get_encoder(audio->config.in.codec, + job->acodec_copy_mask, + job->acodec_fallback, + job->mux); + if (!(audio->config.out.codec & HB_ACODEC_PASS_FLAG) && + !(audio->config.out.codec & HB_ACODEC_MASK)) { - best_samplerate = hb_audio_rates[ii].rate; - break; + hb_log("Auto Passthru: passthru not possible and no valid fallback specified, dropping track %d", + audio->config.out.track ); + hb_list_rem(job->list_audio, audio); + hb_audio_close(&audio); + continue; + } + audio->config.out.samplerate = audio->config.in.samplerate; + if (!(audio->config.out.codec & HB_ACODEC_PASS_FLAG)) + { + if (audio->config.out.codec == job->acodec_fallback) + { + hb_log("Auto Passthru: passthru not possible for track %d, using fallback", + audio->config.out.track); + } + else + { + hb_log("Auto Passthru: passthru and fallback not possible for track %d, using default encoder", + audio->config.out.track); + } + audio->config.out.mixdown = + hb_mixdown_get_default(audio->config.out.codec, + audio->config.in.channel_layout); + audio->config.out.bitrate = + hb_audio_bitrate_get_default(audio->config.out.codec, + audio->config.out.samplerate, + audio->config.out.mixdown ); + audio->config.out.compression_level = + hb_audio_compression_get_default(audio->config.out.codec); + } + else + { + const hb_encoder_t *audio_encoder = NULL; + while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) + { + if (audio_encoder->codec == audio->config.out.codec) + { + hb_log("Auto Passthru: using %s for track %d", + audio_encoder->name, + audio->config.out.track); + break; + } + } } } - /* sr_shift: 0 -> 48000, 44100, 32000 Hz - * 1 -> 24000, 22050, 16000 Hz - * 2 -> 12000, 11025, 8000 Hz - * - * also, since samplerates are sanitized downwards: - * - * (samplerate < 32000) implies (samplerate <= 24000) - */ - samplerate_shift = ((best_samplerate < 16000) ? 2 : - (best_samplerate < 32000) ? 1 : 0); + /* Adjust output track number, in case we removed one. + * Output tracks sadly still need to be in sequential order. + * Note: out.track starts at 1, i starts at 0 */ + audio->config.out.track = ++i; } - if (sr_shift != NULL) +} + +void hb_autopassthru_print_settings(hb_job_t *job) +{ + char *mask = NULL, *tmp; + const char *fallback = NULL; + const hb_encoder_t *audio_encoder = NULL; + while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) { - *sr_shift = samplerate_shift; + if ((audio_encoder->codec & HB_ACODEC_PASS_FLAG) && + (audio_encoder->codec != HB_ACODEC_AUTO_PASS) && + (audio_encoder->codec & job->acodec_copy_mask)) + { + if (mask != NULL) + { + tmp = hb_strncat_dup(mask, ", ", 2); + if (tmp != NULL) + { + free(mask); + mask = tmp; + } + } + // passthru name without " Passthru" + tmp = hb_strncat_dup(mask, audio_encoder->name, + strlen(audio_encoder->name) - 9); + if (tmp != NULL) + { + free(mask); + mask = tmp; + } + } + else if ((audio_encoder->codec & HB_ACODEC_PASS_FLAG) == 0 && + (audio_encoder->codec == job->acodec_fallback)) + { + fallback = audio_encoder->name; + } } - return best_samplerate; + if (mask == NULL) + hb_log("Auto Passthru: no codecs allowed"); + else + hb_log("Auto Passthru: allowed codecs are %s", mask); + if (fallback == NULL) + hb_log("Auto Passthru: no valid fallback specified"); + else + hb_log("Auto Passthru: fallback is %s", fallback); +} + +int hb_autopassthru_get_encoder(int in_codec, int copy_mask, int fallback, + int muxer) +{ + int i = 0; + const hb_encoder_t *audio_encoder = NULL; + int out_codec = (copy_mask & in_codec) | HB_ACODEC_PASS_FLAG; + // sanitize fallback encoder and selected passthru + // note: invalid fallbacks are caught in hb_autopassthru_apply_settings + while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) + { + if (audio_encoder->codec == out_codec) + { + i++; + if (!(audio_encoder->muxers & muxer)) + out_codec = 0; + } + else if (audio_encoder->codec == fallback) + { + i++; + if (!(audio_encoder->muxers & muxer)) + fallback = hb_audio_encoder_get_default(muxer); + } + if (i > 1) + { + break; + } + } + return (out_codec & HB_ACODEC_PASS_MASK) ? out_codec : fallback; +} + +int hb_container_get_from_name(const char *name) +{ + if (name == NULL || *name == '\0') + goto fail; + + // TODO: implement something more flexible + if (!strcasecmp(name, "m4v")) + { + // old CLI alternate short name for "mp4" + return HB_MUX_MP4; + } + if (!strcasecmp(name, "MP4 file")) + { + return HB_MUX_MP4; + } + if (!strcasecmp(name, "MKV file")) + { + return HB_MUX_MKV; + } + + int i; + for (i = 0; i < hb_containers_count; i++) + { + if (!strcasecmp(hb_containers[i].name, name) || + !strcasecmp(hb_containers[i].short_name, name) || + !strcasecmp(hb_containers[i].default_extension, name)) + { + return hb_containers[i].format; + } + } + +fail: + return -1; +} + +int hb_container_get_from_extension(const char *extension) +{ + if (extension == NULL || *extension == '\0') + goto fail; + + // TODO: implement something more flexible + if (!strcasecmp(extension, "m4v")) + { + return HB_MUX_MP4; + } + + int i; + for (i = 0; i < hb_containers_count; i++) + { + if (!strcasecmp(hb_containers[i].default_extension, extension)) + { + return hb_containers[i].format; + } + } + +fail: + return -1; +} + +const char* hb_container_get_name(int format) +{ + if (!(format & HB_MUX_MASK)) + goto fail; + + int i; + for (i = 0; i < hb_containers_count; i++) + { + if (hb_containers[i].format == format) + { + return hb_containers[i].name; + } + + } + +fail: + return NULL; +} + +const char* hb_container_get_short_name(int format) +{ + if (!(format & HB_MUX_MASK)) + goto fail; + + int i; + for (i = 0; i < hb_containers_count; i++) + { + if (hb_containers[i].format == format) + { + return hb_containers[i].short_name; + } + } + +fail: + return NULL; +} + +const char* hb_container_get_default_extension(int format) +{ + if (!(format & HB_MUX_MASK)) + goto fail; + + int i; + for (i = 0; i < hb_containers_count; i++) + { + if (hb_containers[i].format == format) + { + return hb_containers[i].default_extension; + } + } + +fail: + return NULL; +} + +const char* hb_container_sanitize_name(const char *name) +{ + return hb_container_get_name(hb_container_get_from_name(name)); +} + +const hb_container_t* hb_container_get_next(const hb_container_t *last) +{ + if (last == NULL) + { + return &hb_containers[0]; + } + if (last < &hb_containers[0] || + last >= &hb_containers[hb_containers_count - 1]) + { + return NULL; + } + return last + 1; } /********************************************************************** @@ -2336,7 +2944,7 @@ void hb_audio_config_init(hb_audio_config_t * audiocfg) /* Initalize some sensible defaults */ audiocfg->in.track = audiocfg->out.track = 0; - audiocfg->out.codec = hb_audio_encoders[0].encoder; + audiocfg->out.codec = hb_audio_encoders[0].codec; audiocfg->out.samplerate = -1; audiocfg->out.samples_per_frame = -1; audiocfg->out.bitrate = -1; @@ -2970,9 +3578,9 @@ void hb_attachment_close( hb_attachment_t **attachment ) * hb_yuv2rgb ********************************************************************** * Converts a YCrCb pixel to an RGB pixel. - * + * * This conversion is lossy (due to rounding and clamping). - * + * * Algorithm: * http://en.wikipedia.org/w/index.php?title=YCbCr&oldid=361987695#Technical_details *********************************************************************/ @@ -3004,9 +3612,9 @@ int hb_yuv2rgb(int yuv) * hb_rgb2yuv ********************************************************************** * Converts an RGB pixel to a YCrCb pixel. - * + * * This conversion is lossy (due to rounding and clamping). - * + * * Algorithm: * http://en.wikipedia.org/w/index.php?title=YCbCr&oldid=361987695#Technical_details *********************************************************************/ diff --git a/libhb/common.h b/libhb/common.h index 2951def5d..58d102c9a 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -73,6 +73,7 @@ typedef struct hb_rate_s hb_rate_t; typedef struct hb_dither_s hb_dither_t; typedef struct hb_mixdown_s hb_mixdown_t; typedef struct hb_encoder_s hb_encoder_t; +typedef struct hb_container_s hb_container_t; typedef struct hb_job_s hb_job_t; typedef struct hb_title_set_s hb_title_set_t; typedef struct hb_title_s hb_title_t; @@ -171,7 +172,7 @@ void hb_chapter_set_title(hb_chapter_t *chapter, const char *title); struct hb_rate_s { - const char *string; + const char *name; int rate; }; @@ -184,7 +185,7 @@ struct hb_dither_s struct hb_mixdown_s { - const char *human_readable_name; + const char *name; const char *internal_name; const char *short_name; int amixdown; @@ -192,10 +193,18 @@ struct hb_mixdown_s struct hb_encoder_s { - const char *human_readable_name; // note: used in presets - const char *short_name; // note: used in CLI - int encoder; // HB_*CODEC_* define - int muxers; // supported muxers + const char *name; // note: used in presets + const char *short_name; // note: used in CLI + int codec; // HB_*CODEC_* define + int muxers; // supported muxers +}; + +struct hb_container_s +{ + const char *name; + const char *short_name; + const char *default_extension; + int format; }; struct hb_subtitle_config_s @@ -210,77 +219,115 @@ struct hb_subtitle_config_s int64_t offset; }; -#define HB_VIDEO_RATE_BASE 27000000 - -extern hb_rate_t hb_video_rates[]; -extern int hb_video_rates_count; -extern hb_rate_t hb_audio_rates[]; -extern int hb_audio_rates_count; -extern int hb_audio_rates_default; -extern hb_rate_t hb_audio_bitrates[]; -extern int hb_audio_bitrates_count; -extern hb_dither_t hb_audio_dithers[]; -extern int hb_audio_dithers_count; -extern hb_mixdown_t hb_audio_mixdowns[]; -extern int hb_audio_mixdowns_count; -extern hb_encoder_t hb_video_encoders[]; -extern int hb_video_encoders_count; -extern hb_encoder_t hb_audio_encoders[]; -extern int hb_audio_encoders_count; - -/* Expose values for PInvoke */ -hb_rate_t* hb_get_video_rates(); -int hb_get_video_rates_count(); -hb_rate_t* hb_get_audio_rates(); -int hb_get_audio_rates_count(); -int hb_get_audio_rates_default(); -hb_rate_t* hb_get_audio_bitrates(); -int hb_get_audio_bitrates_count(); -hb_dither_t* hb_get_audio_dithers(); -int hb_get_audio_dithers_count(); -hb_mixdown_t* hb_get_audio_mixdowns(); -int hb_get_audio_mixdowns_count(); -hb_encoder_t* hb_get_video_encoders(); -int hb_get_video_encoders_count(); -hb_encoder_t* hb_get_audio_encoders(); -int hb_get_audio_encoders_count(); - -int hb_audio_dither_get_default(); -int hb_audio_dither_get_default_method(); -int hb_audio_dither_is_supported(uint32_t codec); -const char* hb_audio_dither_get_description(int method); - -int hb_mixdown_is_supported(int mixdown, uint32_t codec, uint64_t layout); -int hb_mixdown_has_codec_support(int mixdown, uint32_t codec); -int hb_mixdown_has_remix_support(int mixdown, uint64_t layout); -int hb_mixdown_get_discrete_channel_count(int amixdown); -int hb_mixdown_get_low_freq_channel_count(int amixdown); -int hb_mixdown_get_mixdown_from_short_name(const char *short_name); -const char* hb_mixdown_get_short_name_from_mixdown(int amixdown); +/******************************************************************************* + * Lists of rates, mixdowns, encoders etc. + ******************************************************************************* + * + * Use hb_*_get_next() to get the next list item (use NULL to get the first). + * + * Use hb_*_get_from_name() to get the value corresponding to a name. + * The name can be either the short or full name. + * Legacy names are sanitized to currently-supported values whenever possible. + * Returns -1 if no value could be found. + * + * Use hb_*_get_name() and hb_*_get_short_name() to get the corresponding value. + * Returns NULL if the value is invalid. + * + * hb_*_sanitize_name() are convenience functions for use when dealing + * with full names (e.g. to translate legacy values while loading a preset). + * + * Names are case-insensitive; libhb will ensure that the lists do not contain + * more than one entry with the same name. + * + * Use hb_*_get_limits() to get the minimum/maximum for lists with numerically + * ordered values. + * + * Use hb_*_get_best() to sanitize a value based on other relevant parameters. + * + * Use hb_*_get_default() to get the default based on other relevant parameters. + * + */ + +int hb_video_framerate_get_from_name(const char *name); +const char* hb_video_framerate_get_name(int framerate); +const char* hb_video_framerate_sanitize_name(const char *name); +const hb_rate_t* hb_video_framerate_get_next(const hb_rate_t *last); + +int hb_audio_samplerate_get_best(uint32_t codec, int samplerate, int *sr_shift); +int hb_audio_samplerate_get_from_name(const char *name); +const char* hb_audio_samplerate_get_name(int samplerate); +const hb_rate_t* hb_audio_samplerate_get_next(const hb_rate_t *last); + +int hb_audio_bitrate_get_best(uint32_t codec, int bitrate, int samplerate, int mixdown); +int hb_audio_bitrate_get_default(uint32_t codec, int samplerate, int mixdown); +void hb_audio_bitrate_get_limits(uint32_t codec, int samplerate, int mixdown, int *low, int *high); +const hb_rate_t* hb_audio_bitrate_get_next(const hb_rate_t *last); + +void hb_audio_quality_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction); +float hb_audio_quality_get_best(uint32_t codec, float quality); +float hb_audio_quality_get_default(uint32_t codec); + +void hb_audio_compression_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction); +float hb_audio_compression_get_best(uint32_t codec, float compression); +float hb_audio_compression_get_default(uint32_t codec); + +int hb_audio_dither_get_default(); +int hb_audio_dither_get_default_method(); // default method, if enabled && supported +int hb_audio_dither_is_supported(uint32_t codec); +int hb_audio_dither_get_from_name(const char *name); +const char* hb_audio_dither_get_description(int method); +const hb_dither_t* hb_audio_dither_get_next(const hb_dither_t *last); + +int hb_mixdown_is_supported(int mixdown, uint32_t codec, uint64_t layout); +int hb_mixdown_has_codec_support(int mixdown, uint32_t codec); +int hb_mixdown_has_remix_support(int mixdown, uint64_t layout); +int hb_mixdown_get_discrete_channel_count(int mixdown); +int hb_mixdown_get_low_freq_channel_count(int mixdown); +int hb_mixdown_get_best(uint32_t codec, uint64_t layout, int mixdown); +int hb_mixdown_get_default(uint32_t codec, uint64_t layout); +int hb_mixdown_get_from_name(const char *name); +const char* hb_mixdown_get_name(int mixdown); +const char* hb_mixdown_get_short_name(int mixdown); +const char* hb_mixdown_sanitize_name(const char *name); +const hb_mixdown_t* hb_mixdown_get_next(const hb_mixdown_t *last); + +int hb_video_encoder_get_default(int muxer); +int hb_video_encoder_get_from_name(const char *name); +const char* hb_video_encoder_get_name(int encoder); +const char* hb_video_encoder_get_short_name(int encoder); +const char* hb_video_encoder_sanitize_name(const char *name); +const hb_encoder_t* hb_video_encoder_get_next(const hb_encoder_t *last); + +/* + * hb_audio_encoder_get_fallback_for_passthru() will sanitize a passthru codec + * to the matching audio encoder (if any is available). + * + * hb_audio_encoder_get_from_name(), hb_audio_encoder_sanitize_name() will + * sanitize legacy encoder names, but won't convert passthru to an encoder. + */ +int hb_audio_encoder_get_fallback_for_passthru(int passthru); +int hb_audio_encoder_get_default(int muxer); +int hb_audio_encoder_get_from_name(const char *name); +const char* hb_audio_encoder_get_name(int encoder); +const char* hb_audio_encoder_get_short_name(int encoder); +const char* hb_audio_encoder_sanitize_name(const char *name); +const hb_encoder_t* hb_audio_encoder_get_next(const hb_encoder_t *last); +/* + * Not typically used by the UIs + * (set hb_job_t.acodec_copy_mask, hb_job_t.acodec_fallback instead). + */ void hb_autopassthru_apply_settings(hb_job_t *job); void hb_autopassthru_print_settings(hb_job_t *job); int hb_autopassthru_get_encoder(int in_codec, int copy_mask, int fallback, int muxer); -int hb_get_default_audio_encoder(int muxer); - -int hb_get_best_mixdown(uint32_t codec, uint64_t layout, int mixdown); -int hb_get_default_mixdown(uint32_t codec, uint64_t layout); - -int hb_get_best_samplerate(uint32_t codec, int samplerate, int *sr_shift); - -int hb_find_closest_audio_bitrate(int bitrate); -void hb_get_audio_bitrate_limits(uint32_t codec, int samplerate, int mixdown, int *low, int *high); -int hb_get_best_audio_bitrate(uint32_t codec, int bitrate, int samplerate, int mixdown); -int hb_get_default_audio_bitrate(uint32_t codec, int samplerate, int mixdown); - -void hb_get_audio_quality_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction); -float hb_get_best_audio_quality(uint32_t codec, float quality); -float hb_get_default_audio_quality(uint32_t codec); - -void hb_get_audio_compression_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction); -float hb_get_best_audio_compression(uint32_t codec, float compression); -float hb_get_default_audio_compression(uint32_t codec); +int hb_container_get_from_name(const char *name); +int hb_container_get_from_extension(const char *extension); // not really a container name +const char* hb_container_get_name(int format); +const char* hb_container_get_short_name(int format); +const char* hb_container_get_default_extension(int format); +const char* hb_container_sanitize_name(const char *name); +const hb_container_t* hb_container_get_next(const hb_container_t *last); struct hb_title_set_s { @@ -408,12 +455,19 @@ struct hb_job_s hb_metadata_t * metadata; - /* Muxer settings - mux: output file format - file: file path */ -#define HB_MUX_MASK 0xFF0000 -#define HB_MUX_MP4 0x010000 -#define HB_MUX_MKV 0x200000 + /* + * Muxer settings + * mux: output file format + * file: file path + */ +#define HB_MUX_MASK 0xFF0000 +#define HB_MUX_MP4V2 0x010000 +#define HB_MUX_MASK_MP4 0x0F0000 +#define HB_MUX_LIBMKV 0x100000 +#define HB_MUX_MASK_MKV 0xF00000 +// default MP4 and MKV muxers +#define HB_MUX_MP4 HB_MUX_MP4V2 +#define HB_MUX_MKV HB_MUX_LIBMKV int mux; char * file; diff --git a/libhb/encavcodec.c b/libhb/encavcodec.c index b232dae81..dcb19da34 100644 --- a/libhb/encavcodec.c +++ b/libhb/encavcodec.c @@ -117,12 +117,12 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) // to fps.den. if (fps.num == 27000000) { - int ii; - for (ii = 0; ii < hb_video_rates_count; ii++) + const hb_rate_t *video_framerate = NULL; + while ((video_framerate = hb_video_framerate_get_next(video_framerate)) != NULL) { - if (abs(fps.den - hb_video_rates[ii].rate) < 10) + if (abs(fps.den - video_framerate->rate) < 10) { - fps.den = hb_video_rates[ii].rate; + fps.den = video_framerate->rate; break; } } diff --git a/libhb/scan.c b/libhb/scan.c index c83326d4d..0d7dde7ff 100644 --- a/libhb/scan.c +++ b/libhb/scan.c @@ -820,11 +820,12 @@ skip_preview: // if the frame rate is very close to one of our "common" framerates, // assume it actually is said frame rate; e.g. some 24000/1001 sources // may have a rate_base of 1126124 (instead of 1126125) - for( i = 0; i < hb_video_rates_count; i++ ) + const hb_rate_t *video_framerate = NULL; + while ((video_framerate = hb_video_framerate_get_next(video_framerate)) != NULL) { - if( is_close_to( vid_info.rate_base, hb_video_rates[i].rate, 100 ) ) + if (is_close_to(vid_info.rate_base, video_framerate->rate, 100)) { - vid_info.rate_base = hb_video_rates[i].rate; + vid_info.rate_base = video_framerate->rate; break; } } diff --git a/libhb/work.c b/libhb/work.c index 72d758eed..1e05c9db7 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -149,12 +149,12 @@ hb_work_object_t* hb_codec_encoder(int codec) * Displays job parameters in the debug log. * @param job Handle work hb_job_t. */ -void hb_display_job_info( hb_job_t * job ) +void hb_display_job_info(hb_job_t *job) { - hb_title_t * title = job->title; - hb_audio_t * audio; - hb_subtitle_t * subtitle; - int i, j; + int i; + hb_title_t *title = job->title; + hb_audio_t *audio; + hb_subtitle_t *subtitle; hb_log("job configuration:"); hb_log( " * source"); @@ -206,26 +206,19 @@ void hb_display_job_info( hb_job_t * job ) hb_log( " + %s", job->file ); - switch( job->mux ) + hb_log(" + container: %s", hb_container_get_name(job->mux)); + switch (job->mux) { - case HB_MUX_MP4: - hb_log(" + container: MPEG-4 (.mp4 and .m4v)"); - - if( job->ipod_atom ) - hb_log( " + compatibility atom for iPod 5G"); - - if( job->largeFileSize ) - hb_log( " + 64-bit formatting"); - - if( job->mp4_optimize ) - hb_log( " + optimized for progressive web downloads"); - - if( job->color_matrix_code ) - hb_log( " + custom color matrix: %s", job->color_matrix_code == 1 ? "ITU Bt.601 (SD)" : job->color_matrix_code == 2 ? "ITU Bt.709 (HD)" : "Custom" ); + case HB_MUX_MP4V2: + if (job->largeFileSize) + hb_log(" + 64-bit chunk offsets"); + if (job->mp4_optimize) + hb_log(" + optimized for HTTP streaming (fast start)"); + if (job->ipod_atom) + hb_log(" + compatibility atom for iPod 5G"); break; - case HB_MUX_MKV: - hb_log(" + container: Matroska (.mkv)"); + default: break; } @@ -295,14 +288,7 @@ void hb_display_job_info( hb_job_t * job ) if( !job->indepth_scan ) { /* Video encoder */ - for( i = 0; i < hb_video_encoders_count; i++ ) - { - if( hb_video_encoders[i].encoder == job->vcodec ) - { - hb_log( " + encoder: %s", hb_video_encoders[i].human_readable_name ); - break; - } - } + hb_log(" + encoder: %s", hb_video_encoder_get_name(job->vcodec)); if( job->x264_preset && *job->x264_preset && job->vcodec == HB_VCODEC_X264 ) @@ -346,6 +332,18 @@ void hb_display_job_info( hb_job_t * job ) hb_log( " subq=2 (if originally greater than 2, else subq unchanged)" ); } } + + if (job->color_matrix_code && (job->vcodec == HB_VCODEC_X264 || + job->mux == HB_MUX_MP4V2)) + { + // color matrix is set: + // 1) at the stream level (x264 only), + // 2) at the container level (mp4v2 only) + hb_log(" + custom color matrix: %s", + job->color_matrix_code == 1 ? "ITU Bt.601 (NTSC)" : + job->color_matrix_code == 2 ? "ITU Bt.601 (PAL)" : + job->color_matrix_code == 3 ? "ITU Bt.709 (HD)" : "Custom"); + } } if( job->indepth_scan ) @@ -413,25 +411,13 @@ void hb_display_job_info( hb_job_t * job ) if( audio->config.out.codec & HB_ACODEC_PASS_FLAG ) { - for( j = 0; j < hb_audio_encoders_count; j++ ) - { - if( hb_audio_encoders[j].encoder == audio->config.out.codec ) - { - hb_log( " + %s", hb_audio_encoders[j].human_readable_name ); - break; - } - } + hb_log(" + %s", + hb_audio_encoder_get_name(audio->config.out.codec)); } else { - for( j = 0; j < hb_audio_mixdowns_count; j++ ) - { - if( hb_audio_mixdowns[j].amixdown == audio->config.out.mixdown ) - { - hb_log( " + mixdown: %s", hb_audio_mixdowns[j].human_readable_name ); - break; - } - } + hb_log(" + mixdown: %s", + hb_mixdown_get_name(audio->config.out.mixdown)); if( audio->config.out.normalize_mix_level != 0 ) { hb_log( " + normalized mixing levels" ); @@ -449,22 +435,27 @@ void hb_display_job_info( hb_job_t * job ) hb_log(" + dither: %s", hb_audio_dither_get_description(audio->config.out.dither_method)); } - for( j = 0; j < hb_audio_encoders_count; j++ ) + hb_log(" + encoder: %s", + hb_audio_encoder_get_name(audio->config.out.codec)); + if (audio->config.out.bitrate > 0) { - if( hb_audio_encoders[j].encoder == audio->config.out.codec ) - { - hb_log( " + encoder: %s", hb_audio_encoders[j].human_readable_name ); - if( audio->config.out.bitrate > 0 ) - hb_log( " + bitrate: %d kbps, samplerate: %d Hz", audio->config.out.bitrate, audio->config.out.samplerate ); - else if( audio->config.out.quality != HB_INVALID_AUDIO_QUALITY ) - hb_log( " + quality: %.2f, samplerate: %d Hz", audio->config.out.quality, audio->config.out.samplerate ); - else if( audio->config.out.samplerate > 0 ) - hb_log( " + samplerate: %d Hz", audio->config.out.samplerate ); - if( audio->config.out.compression_level >= 0 ) - hb_log( " + compression level: %.2f", - audio->config.out.compression_level ); - break; - } + hb_log(" + bitrate: %d kbps, samplerate: %d Hz", + audio->config.out.bitrate, audio->config.out.samplerate); + } + else if (audio->config.out.quality != HB_INVALID_AUDIO_QUALITY) + { + hb_log(" + quality: %.2f, samplerate: %d Hz", + audio->config.out.quality, audio->config.out.samplerate); + } + else if (audio->config.out.samplerate > 0) + { + hb_log(" + samplerate: %d Hz", + audio->config.out.samplerate); + } + if (audio->config.out.compression_level >= 0) + { + hb_log(" + compression level: %.2f", + audio->config.out.compression_level); } } } @@ -494,24 +485,24 @@ void correct_framerate( hb_job_t * job ) * Closes threads and frees fifos. * @param job Handle work hb_job_t. */ -static void do_job( hb_job_t * job ) +static void do_job(hb_job_t *job) { - hb_title_t * title; - int i, j; - hb_work_object_t * w; - hb_work_object_t * sync; - hb_work_object_t * muxer; + int i; + hb_title_t *title; + hb_interjob_t *interjob; + hb_work_object_t *w; + hb_work_object_t *sync; + hb_work_object_t *muxer; hb_work_object_t *reader = hb_get_work(WORK_READER); - hb_interjob_t * interjob; - - hb_audio_t * audio; - hb_subtitle_t * subtitle; - unsigned int subtitle_highest = 0; - unsigned int subtitle_lowest = 0; - unsigned int subtitle_lowest_id = 0; - unsigned int subtitle_forced_id = 0; + + hb_audio_t *audio; + hb_subtitle_t *subtitle; + unsigned int subtitle_highest = 0; + unsigned int subtitle_lowest = 0; + unsigned int subtitle_lowest_id = 0; + unsigned int subtitle_forced_id = 0; unsigned int subtitle_forced_hits = 0; - unsigned int subtitle_hit = 0; + unsigned int subtitle_hit = 0; title = job->title; interjob = hb_interjob_get( job->h ); @@ -797,21 +788,14 @@ static void do_job( hb_job_t * job ) audio->config.out.samplerate = audio->config.in.samplerate; } best_samplerate = - hb_get_best_samplerate(audio->config.out.codec, - audio->config.out.samplerate, NULL); + hb_audio_samplerate_get_best(audio->config.out.codec, + audio->config.out.samplerate, + NULL); if (best_samplerate != audio->config.out.samplerate) { - int ii; - for (ii = 0; ii < hb_audio_rates_count; ii++) - { - if (best_samplerate == hb_audio_rates[ii].rate) - { - hb_log("work: sanitizing track %d unsupported samplerate %d Hz to %s kHz", - audio->config.out.track, audio->config.out.samplerate, - hb_audio_rates[ii].string); - break; - } - } + hb_log("work: sanitizing track %d unsupported samplerate %d Hz to %s kHz", + audio->config.out.track, audio->config.out.samplerate, + hb_audio_samplerate_get_name(best_samplerate)); audio->config.out.samplerate = best_samplerate; } @@ -820,44 +804,25 @@ static void do_job( hb_job_t * job ) { /* Mixdown not specified, set the default mixdown */ audio->config.out.mixdown = - hb_get_default_mixdown(audio->config.out.codec, + hb_mixdown_get_default(audio->config.out.codec, audio->config.in.channel_layout); - for (j = 0; j < hb_audio_mixdowns_count; j++) - { - if (hb_audio_mixdowns[j].amixdown == audio->config.out.mixdown) - { - hb_log("work: mixdown not specified, track %d setting mixdown %s", - audio->config.out.track, - hb_audio_mixdowns[j].human_readable_name); - break; - } - } + hb_log("work: mixdown not specified, track %d setting mixdown %s", + audio->config.out.track, + hb_mixdown_get_name(audio->config.out.mixdown)); } else { best_mixdown = - hb_get_best_mixdown(audio->config.out.codec, + hb_mixdown_get_best(audio->config.out.codec, audio->config.in.channel_layout, audio->config.out.mixdown); if (audio->config.out.mixdown != best_mixdown) { - int prev_mix_idx = 0, best_mix_idx = 0; - for (j = 0; j < hb_audio_mixdowns_count; j++) - { - if (hb_audio_mixdowns[j].amixdown == audio->config.out.mixdown) - { - prev_mix_idx = j; - } - else if (hb_audio_mixdowns[j].amixdown == best_mixdown) - { - best_mix_idx = j; - } - } /* log the output mixdown */ hb_log("work: sanitizing track %d mixdown %s to %s", audio->config.out.track, - hb_audio_mixdowns[prev_mix_idx].human_readable_name, - hb_audio_mixdowns[best_mix_idx].human_readable_name); + hb_mixdown_get_name(audio->config.out.mixdown), + hb_mixdown_get_name(best_mixdown)); audio->config.out.mixdown = best_mixdown; } } @@ -866,7 +831,7 @@ static void do_job( hb_job_t * job ) if (audio->config.out.compression_level < 0) { audio->config.out.compression_level = - hb_get_default_audio_compression(audio->config.out.codec); + hb_audio_compression_get_default(audio->config.out.codec); if (audio->config.out.compression_level >= 0) { hb_log("work: compression level not specified, track %d setting compression level %.2f", @@ -877,7 +842,7 @@ static void do_job( hb_job_t * job ) else { float best_compression = - hb_get_best_audio_compression(audio->config.out.codec, + hb_audio_compression_get_best(audio->config.out.codec, audio->config.out.compression_level); if (best_compression != audio->config.out.compression_level) { @@ -901,7 +866,7 @@ static void do_job( hb_job_t * job ) if (audio->config.out.quality != HB_INVALID_AUDIO_QUALITY) { float best_quality = - hb_get_best_audio_quality(audio->config.out.codec, + hb_audio_quality_get_best(audio->config.out.codec, audio->config.out.quality); if (best_quality != audio->config.out.quality) { @@ -927,7 +892,7 @@ static void do_job( hb_job_t * job ) { /* Bitrate not specified, set the default bitrate */ audio->config.out.bitrate = - hb_get_default_audio_bitrate(audio->config.out.codec, + hb_audio_bitrate_get_default(audio->config.out.codec, audio->config.out.samplerate, audio->config.out.mixdown); if (audio->config.out.bitrate > 0) @@ -940,7 +905,7 @@ static void do_job( hb_job_t * job ) else { best_bitrate = - hb_get_best_audio_bitrate(audio->config.out.codec, + hb_audio_bitrate_get_best(audio->config.out.codec, audio->config.out.bitrate, audio->config.out.samplerate, audio->config.out.mixdown); diff --git a/macosx/Controller.m b/macosx/Controller.m index 93c61f8b5..e92f7393a 100644 --- a/macosx/Controller.m +++ b/macosx/Controller.m @@ -656,29 +656,33 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It /* Destination box*/ NSMenuItem *menuItem; [fDstFormatPopUp removeAllItems]; - // MP4 file - menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MP4 file" action: NULL keyEquivalent: @""]; - [menuItem setTag: HB_MUX_MP4]; - // MKV file - menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MKV file" action: NULL keyEquivalent: @""]; - [menuItem setTag: HB_MUX_MKV]; - - [fDstFormatPopUp selectItemAtIndex: 0]; - + for (const hb_container_t *container = hb_container_get_next(NULL); + container != NULL; + container = hb_container_get_next(container)) + { + menuItem = [[fDstFormatPopUp menu] addItemWithTitle:[NSString stringWithUTF8String:container->name] + action:nil + keyEquivalent:@""]; + [menuItem setTag:container->format]; + } + // select the first container + [fDstFormatPopUp selectItemAtIndex:0]; [self formatPopUpChanged:nil]; + + // enable/disable chapter markers as necessary + if ([fCreateChapterMarkers isEnabled] && + [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultChapterMarkers"]) + { + [fCreateChapterMarkers setState:NSOnState]; + } + else + { + [fCreateChapterMarkers setState:NSOffState]; + } - /* We enable the create chapters checkbox here since we are .mp4 */ - [fCreateChapterMarkers setEnabled: YES]; - if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultChapterMarkers"] > 0) - { - [fCreateChapterMarkers setState: NSOnState]; - } - - - - - [fDstFile2Field setStringValue: [NSString stringWithFormat: - @"%@/Desktop/Movie.mp4", NSHomeDirectory()]]; + [fDstFile2Field setStringValue:[NSString + stringWithFormat:@"%@/Desktop/Movie.mp4", + NSHomeDirectory()]]; /* Video encoder */ [fVidEncoderPopUp removeAllItems]; @@ -694,31 +698,37 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It /* Video framerate */ [fVidRatePopUp removeAllItems]; - [fVidRatePopUp addItemWithTitle: NSLocalizedString( @"Same as source", @"" )]; - for( int i = 0; i < hb_video_rates_count; i++ ) + menuItem = [[fVidRatePopUp menu] addItemWithTitle:@"Same as source" + action:nil + keyEquivalent:@""]; + [menuItem setTag:-1]; // hb_video_framerate_get_from_name(NULL) + for (const hb_rate_t *video_framerate = hb_video_framerate_get_next(NULL); + video_framerate != NULL; + video_framerate = hb_video_framerate_get_next(video_framerate)) { - if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]]) - { - [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@", - [NSString stringWithUTF8String: hb_video_rates[i].string], @" (NTSC Film)"]]; - } - else if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]]) - { - [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@", - [NSString stringWithUTF8String: hb_video_rates[i].string], @" (PAL Film/Video)"]]; - } - else if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]]) - { - [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@", - [NSString stringWithUTF8String: hb_video_rates[i].string], @" (NTSC Video)"]]; - } - else - { - [fVidRatePopUp addItemWithTitle: - [NSString stringWithUTF8String: hb_video_rates[i].string]]; - } + NSString *itemTitle; + if (!strcmp(video_framerate->name, "23.976")) + { + itemTitle = @"23.976 (NTSC Film)"; + } + else if (!strcmp(video_framerate->name, "25")) + { + itemTitle = @"25 (PAL Film/Video)"; + } + else if (!strcmp(video_framerate->name, "29.97")) + { + itemTitle = @"29.97 (NTSC Video)"; + } + else + { + itemTitle = [NSString stringWithUTF8String:video_framerate->name]; + } + menuItem = [[fVidRatePopUp menu] addItemWithTitle:itemTitle + action:nil + keyEquivalent:@""]; + [menuItem setTag:video_framerate->rate]; } - [fVidRatePopUp selectItemAtIndex: 0]; + [fVidRatePopUp selectItemAtIndex:0]; /* Set Auto Crop to On at launch */ [fPictureController setAutoCrop:YES]; @@ -2114,12 +2124,8 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [fSrcDVD2Field setStringValue:browsedSourceDisplayName]; // use the correct extension based on the container - int format = [fDstFormatPopUp indexOfSelectedItem]; - char *ext = "mp4"; - if (format == 1) - { - ext = "mkv"; - } + int videoContainer = [[fDstFormatPopUp selectedItem] tag]; + const char *ext = hb_container_get_default_extension(videoContainer); /* If its a queue rescan for edit, get the queue item output path */ /* if not, its a new source scan. */ @@ -2140,9 +2146,9 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It } // set m4v extension if necessary - do not override user-specified .mp4 extension - if (format == 0 && applyQueueToScan != YES) + if ((videoContainer & HB_MUX_MASK_MP4) && (applyQueueToScan != YES)) { - [self autoSetM4vExtension: sender]; + [self autoSetM4vExtension:sender]; } /* See if this is the main feature according to libhb */ @@ -2720,8 +2726,8 @@ fWorkingCount = 0; /* Framerate */ [queueFileJob setObject:[NSNumber numberWithInt:[fVidRatePopUp indexOfSelectedItem]] forKey:@"JobIndexVideoFramerate"]; - [queueFileJob setObject:[NSNumber numberWithInt:title->rate] forKey:@"JobVrate"]; - [queueFileJob setObject:[NSNumber numberWithInt:title->rate_base] forKey:@"JobVrateBase"]; + [queueFileJob setObject:[NSNumber numberWithInt:title->rate] forKey:@"JobVrate"]; + [queueFileJob setObject:[NSNumber numberWithInt:title->rate_base] forKey:@"JobVrateBase"]; /* Picture Sizing */ /* Use Max Picture settings for whatever the dvd is.*/ @@ -3313,8 +3319,6 @@ fWorkingCount = 0; [fSrcTitlePopUp indexOfSelectedItem] ); hb_job_t * job = title->job; hb_filter_object_t * filter; - int vrate, vrate_base; - /* set job->angle for libdvdnav */ job->angle = [fSrcAnglePopUp indexOfSelectedItem] + 1; /* Chapter selection */ @@ -3381,37 +3385,37 @@ fWorkingCount = 0; } /* Video settings */ - + int fps_mode, fps_num, fps_den; if( [fVidRatePopUp indexOfSelectedItem] > 0 ) { /* a specific framerate has been chosen */ - vrate = 27000000; - vrate_base = hb_video_rates[[fVidRatePopUp indexOfSelectedItem]-1].rate; + fps_num = 27000000; + fps_den = [[fVidRatePopUp selectedItem] tag]; if ([fFramerateMatrix selectedRow] == 1) { // CFR - job->cfr = 1; + fps_mode = 1; } else { // PFR - job->cfr = 2; + fps_mode = 2; } } else { /* same as source */ - vrate = title->rate; - vrate_base = title->rate_base; + fps_num = title->rate; + fps_den = title->rate_base; if ([fFramerateMatrix selectedRow] == 1) { // CFR - job->cfr = 1; + fps_mode = 1; } else { // VFR - job->cfr = 0; + fps_mode = 0; } } @@ -3723,9 +3727,9 @@ bool one_burned = FALSE; job->crop[2], job->crop[3]] UTF8String] ); /* Add framerate shaping filter */ - filter = hb_filter_init( HB_FILTER_VFR ); - hb_add_filter( job, filter, [[NSString stringWithFormat:@"%d:%d:%d", - job->cfr, vrate, vrate_base] UTF8String] ); + filter = hb_filter_init(HB_FILTER_VFR); + hb_add_filter(job, filter, [[NSString stringWithFormat:@"%d:%d:%d", + fps_mode, fps_num, fps_den] UTF8String]); } @@ -3742,8 +3746,6 @@ bool one_burned = FALSE; hb_job_t * job = title->job; hb_audio_config_t * audio; hb_filter_object_t * filter; - int vrate, vrate_base; - /* Title Angle for dvdnav */ job->angle = [[queueToApply objectForKey:@"TitleAngle"] intValue]; @@ -3938,37 +3940,37 @@ bool one_burned = FALSE; /* Video settings */ /* Framerate */ - - if( [[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue] > 0 ) + int fps_mode, fps_num, fps_den; + if ([[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue] > 0) { /* a specific framerate has been chosen */ - vrate = 27000000; - vrate_base = hb_video_rates[[[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue]-1].rate; + fps_num = 27000000; + fps_den = [[fVidRatePopUp itemAtIndex:[[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue]] tag]; if ([[queueToApply objectForKey:@"VideoFramerateMode"] isEqualToString:@"cfr"]) { // CFR - job->cfr = 1; + fps_mode = 1; } else { // PFR - job->cfr = 2; + fps_mode = 2; } } else { /* same as source */ - vrate = [[queueToApply objectForKey:@"JobVrate"] intValue]; - vrate_base = [[queueToApply objectForKey:@"JobVrateBase"] intValue]; + fps_num = [[queueToApply objectForKey:@"JobVrate"] intValue]; + fps_den = [[queueToApply objectForKey:@"JobVrateBase"] intValue]; if ([[queueToApply objectForKey:@"VideoFramerateMode"] isEqualToString:@"cfr"]) { // CFR - job->cfr = 1; + fps_mode = 1; } else { // VFR - job->cfr = 0; + fps_mode = 0; } } @@ -4159,7 +4161,7 @@ bool one_burned = FALSE; /* We go ahead and assign values to our audio->out.<properties> */ audio->out.track = audio->in.track; audio->out.codec = [[queueToApply objectForKey:[jobPrefix stringByAppendingString:@"Encoder"]] intValue]; - audio->out.compression_level = hb_get_default_audio_compression(audio->out.codec); + audio->out.compression_level = hb_audio_compression_get_default(audio->out.codec); audio->out.mixdown = [[queueToApply objectForKey:[jobPrefix stringByAppendingString:@"Mixdown"]] intValue]; audio->out.normalize_mix_level = 0; audio->out.bitrate = [[queueToApply objectForKey:[jobPrefix stringByAppendingString:@"Bitrate"]] intValue]; @@ -4284,9 +4286,9 @@ bool one_burned = FALSE; job->crop[2], job->crop[3]] UTF8String] ); /* Add framerate shaping filter */ - filter = hb_filter_init( HB_FILTER_VFR ); - hb_add_filter( job, filter, [[NSString stringWithFormat:@"%d:%d:%d", - job->cfr, vrate, vrate_base] UTF8String] ); + filter = hb_filter_init(HB_FILTER_VFR); + hb_add_filter(job, filter, [[NSString stringWithFormat:@"%d:%d:%d", + fps_mode, fps_num, fps_den] UTF8String]); [self writeToActivityLog: "prepareJob exiting"]; } @@ -4922,31 +4924,31 @@ bool one_burned = FALSE; - (IBAction) formatPopUpChanged: (id) sender { - NSString * string = [fDstFile2Field stringValue]; - int format = [fDstFormatPopUp indexOfSelectedItem]; - char * ext = NULL; + NSString *string = [fDstFile2Field stringValue]; + int videoContainer = [[fDstFormatPopUp selectedItem] tag]; + const char *ext = NULL; NSMenuItem *menuItem; - int i; - /* Initially set the large file (64 bit formatting) output checkbox to hidden */ - [fDstMp4LargeFileCheck setHidden: YES]; - [fDstMp4HttpOptFileCheck setHidden: YES]; - [fDstMp4iPodFileCheck setHidden: YES]; + /* Initially set the large file (64 bit formatting) output checkbox to hidden */ + [fDstMp4LargeFileCheck setHidden:YES]; + [fDstMp4HttpOptFileCheck setHidden:YES]; + [fDstMp4iPodFileCheck setHidden:YES]; /* Update the Video Codec Popup */ /* lets get the tag of the currently selected item first so we might reset it later */ - int selectedVidEncoderTag; - selectedVidEncoderTag = [[fVidEncoderPopUp selectedItem] tag]; + int selectedVidEncoderTag = [[fVidEncoderPopUp selectedItem] tag]; /* Note: we now store the video encoder int values from common.c in the tags of each popup for easy retrieval later */ [fVidEncoderPopUp removeAllItems]; - for( i = 0; i < hb_video_encoders_count; i++ ) + for (const hb_encoder_t *video_encoder = hb_video_encoder_get_next(NULL); + video_encoder != NULL; + video_encoder = hb_video_encoder_get_next(video_encoder)) { - if( ( ( format == 0 ) && ( hb_video_encoders[i].muxers & HB_MUX_MP4 ) ) || - ( ( format == 1 ) && ( hb_video_encoders[i].muxers & HB_MUX_MKV ) ) ) + if (video_encoder->muxers & videoContainer) { - menuItem = [[fVidEncoderPopUp menu] addItemWithTitle: [NSString stringWithUTF8String: hb_video_encoders[i].human_readable_name] - action: NULL keyEquivalent: @""]; - [menuItem setTag: hb_video_encoders[i].encoder]; + menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:[NSString stringWithUTF8String:video_encoder->name] + action:nil + keyEquivalent:@""]; + [menuItem setTag:video_encoder->codec]; } } @@ -4965,70 +4967,72 @@ bool one_burned = FALSE; /* Update the Auto Passtgru Fallback Codec Popup */ /* lets get the tag of the currently selected item first so we might reset it later */ - int selectedAutoPassthruFallbackEncoderTag; - selectedAutoPassthruFallbackEncoderTag = [[fAudioFallbackPopUp selectedItem] tag]; + int selectedAutoPassthruFallbackEncoderTag = [[fAudioFallbackPopUp selectedItem] tag]; [fAudioFallbackPopUp removeAllItems]; - for( i = 0; i < hb_audio_encoders_count; i++ ) + for (const hb_encoder_t *audio_encoder = hb_audio_encoder_get_next(NULL); + audio_encoder != NULL; + audio_encoder = hb_audio_encoder_get_next(audio_encoder)) { - if( !( hb_audio_encoders[i].encoder & HB_ACODEC_PASS_FLAG ) && - ( ( ( format == 0 ) && ( hb_audio_encoders[i].muxers & HB_MUX_MP4 ) ) || - ( ( format == 1 ) && ( hb_audio_encoders[i].muxers & HB_MUX_MKV ) ) ) ) + if ((audio_encoder->codec & HB_ACODEC_PASS_FLAG) == 0 && + (audio_encoder->muxers & videoContainer)) { - menuItem = [[fAudioFallbackPopUp menu] addItemWithTitle: [NSString stringWithUTF8String: hb_audio_encoders[i].human_readable_name] - action: NULL keyEquivalent: @""]; - [menuItem setTag: hb_audio_encoders[i].encoder]; + menuItem = [[fAudioFallbackPopUp menu] addItemWithTitle:[NSString stringWithUTF8String:audio_encoder->name] + action:nil + keyEquivalent:@""]; + [menuItem setTag:audio_encoder->codec]; } } /* if we have a previously selected auto passthru fallback encoder tag, then try to select it */ if (selectedAutoPassthruFallbackEncoderTag) { - selectedAutoPassthruFallbackEncoderTag = [fAudioFallbackPopUp selectItemWithTag: selectedAutoPassthruFallbackEncoderTag]; + selectedAutoPassthruFallbackEncoderTag = [fAudioFallbackPopUp selectItemWithTag:selectedAutoPassthruFallbackEncoderTag]; } /* if we had no previous fallback selected OR if selection failed * select the default fallback encoder (AC3) */ if (!selectedAutoPassthruFallbackEncoderTag) { - [fAudioFallbackPopUp selectItemWithTag: HB_ACODEC_AC3]; + [fAudioFallbackPopUp selectItemWithTag:HB_ACODEC_AC3]; } - switch( format ) + // enable chapter markers and hide muxer-specific options + [fCreateChapterMarkers setEnabled:YES]; + [fDstMp4LargeFileCheck setHidden:YES]; + [fDstMp4HttpOptFileCheck setHidden:YES]; + [fDstMp4iPodFileCheck setHidden:YES]; + switch (videoContainer) { - case 0: - [self autoSetM4vExtension: nil]; - /* We show the mp4 option checkboxes here since we are mp4 */ - [fCreateChapterMarkers setEnabled: YES]; - [fDstMp4LargeFileCheck setHidden: NO]; - [fDstMp4HttpOptFileCheck setHidden: NO]; - [fDstMp4iPodFileCheck setHidden: NO]; + case HB_MUX_MP4V2: + [fDstMp4LargeFileCheck setHidden:NO]; + [fDstMp4HttpOptFileCheck setHidden:NO]; + [fDstMp4iPodFileCheck setHidden:NO]; break; - - case 1: - ext = "mkv"; - /* We enable the create chapters checkbox here */ - [fCreateChapterMarkers setEnabled: YES]; - break; - + default: + break; + } + // set the file extension + ext = hb_container_get_default_extension(videoContainer); + [fDstFile2Field setStringValue:[NSString stringWithFormat:@"%@.%s", + [string stringByDeletingPathExtension], + ext]]; + if (videoContainer & HB_MUX_MASK_MP4) + { + [self autoSetM4vExtension:sender]; } - /* tell fSubtitlesDelegate we have a new video container */ - [fSubtitlesDelegate containerChanged:[[fDstFormatPopUp selectedItem] tag]]; + /* tell fSubtitlesDelegate we have a new video container */ + [fSubtitlesDelegate containerChanged:videoContainer]; [fSubtitlesTable reloadData]; - /* post a notification for any interested observers to indicate that our video container has changed */ - [[NSNotificationCenter defaultCenter] postNotification: - [NSNotification notificationWithName: HBContainerChangedNotification - object: self - userInfo: [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInt: [[fDstFormatPopUp selectedItem] tag]], keyContainerTag, - nil]]]; - - if( format == 0 ) - [self autoSetM4vExtension: sender]; - else - [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%s", [string stringByDeletingPathExtension], ext]]; + /* post a notification for any interested observers to indicate that our video container has changed */ + [[NSNotificationCenter defaultCenter] postNotification: + [NSNotification notificationWithName:HBContainerChangedNotification + object:self + userInfo:[NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt:videoContainer], keyContainerTag, + nil]]]; if (SuccessfulScan) { @@ -5090,6 +5094,12 @@ the user is using "Custom" settings by determining the sender*/ - (IBAction) videoEncoderPopUpChanged: (id) sender { + /* if no valid encoder is selected, use the first one */ + if ([fVidEncoderPopUp selectedItem] == nil) + { + [fVidEncoderPopUp selectItemAtIndex:0]; + } + int videoEncoder = [[fVidEncoderPopUp selectedItem] tag]; [fAdvancedOptions setHidden:YES]; @@ -5170,6 +5180,12 @@ the user is using "Custom" settings by determining the sender*/ - (IBAction ) videoFrameRateChanged: (id) sender { + /* if no valid framerate is selected, use "Same as source" */ + if ([fVidRatePopUp selectedItem] == nil) + { + [fVidRatePopUp selectItemAtIndex:0]; + } + /* Hide and set the PFR Checkbox to OFF if we are set to Same as Source */ /* Depending on whether or not Same as source is selected modify the title for * fFramerateVfrPfrCell*/ @@ -5898,21 +5914,23 @@ the user is using "Custom" settings by determining the sender*/ - (NSString*) muxerOptionsSummary { NSMutableString *summary = [NSMutableString stringWithString:@""]; - if (([fDstFormatPopUp selectedItem]) && - [[fDstFormatPopUp selectedItem] tag] == HB_MUX_MP4) + if ([fDstMp4LargeFileCheck isHidden] == NO && + [fDstMp4LargeFileCheck isEnabled] == YES && + [fDstMp4LargeFileCheck state] == NSOnState) { - if ([fDstMp4LargeFileCheck state]) - { - [summary appendString:@" - Large file size"]; - } - if ([fDstMp4HttpOptFileCheck state]) - { - [summary appendString:@" - Web optimized"]; - } - if ([fDstMp4iPodFileCheck state]) - { - [summary appendString:@" - iPod 5G support"]; - } + [summary appendString:@" - Large file size"]; + } + if ([fDstMp4HttpOptFileCheck isHidden] == NO && + [fDstMp4HttpOptFileCheck isEnabled] == YES && + [fDstMp4HttpOptFileCheck state] == NSOnState) + { + [summary appendString:@" - Web optimized"]; + } + if ([fDstMp4iPodFileCheck isHidden] == NO && + [fDstMp4iPodFileCheck isEnabled] == YES && + [fDstMp4iPodFileCheck state] == NSOnState) + { + [summary appendString:@" - iPod 5G support"]; } if ([summary hasPrefix:@" - "]) { @@ -6397,9 +6415,11 @@ return YES; - (IBAction)selectPreset:(id)sender { - if (YES == [self hasValidPresetSelected]) { + // for mapping names via libhb + int intValue; + const char *strValue; chosenPreset = [self selectedPreset]; [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]]; @@ -6413,7 +6433,9 @@ return YES; } /* File Format */ - [fDstFormatPopUp selectItemWithTitle:[chosenPreset objectForKey:@"FileFormat"]]; + /* map legacy container names via libhb */ + strValue = hb_container_sanitize_name([[chosenPreset objectForKey:@"FileFormat"] UTF8String]); + [fDstFormatPopUp selectItemWithTitle:[NSString stringWithFormat:@"%s", strValue]]; [self formatPopUpChanged:nil]; /* Chapter Markers*/ @@ -6427,10 +6449,12 @@ return YES; [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]]; /* Video encoder */ - [fVidEncoderPopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoEncoder"]]; + /* map legacy encoder names via libhb */ + strValue = hb_video_encoder_sanitize_name([[chosenPreset objectForKey:@"VideoEncoder"] UTF8String]); + [fVidEncoderPopUp selectItemWithTitle:[NSString stringWithFormat:@"%s", strValue]]; [self videoEncoderPopUpChanged:nil]; - if ([[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"H.264 (x264)"]) + if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264) { if (![chosenPreset objectForKey:@"x264UseAdvancedOptions"] || [[chosenPreset objectForKey:@"x264UseAdvancedOptions"] intValue]) @@ -6549,7 +6573,9 @@ return YES; [fFramerateMatrix selectCellAtRow:1 column:0]; // we want cfr } } - [fVidRatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoFramerate"]]; + /* map legacy names via libhb */ + intValue = hb_video_framerate_get_from_name([[chosenPreset objectForKey:@"VideoFramerate"] UTF8String]); + [fVidRatePopUp selectItemWithTag:intValue]; [self videoFrameRateChanged:nil]; /* 2 Pass Encoding */ @@ -6604,11 +6630,13 @@ return YES; } if ((tempObject = [chosenPreset objectForKey:@"AudioEncoderFallback"]) != nil) { - [fAudioFallbackPopUp selectItemWithTitle:tempObject]; + /* map legacy encoder names via libhb */ + strValue = hb_audio_encoder_sanitize_name([tempObject UTF8String]); + [fAudioFallbackPopUp selectItemWithTitle:[NSString stringWithFormat:@"%s", strValue]]; } else { - [fAudioFallbackPopUp selectItemWithTitle:@"AC3 (ffmpeg)"]; + [fAudioFallbackPopUp selectItemWithTag:HB_ACODEC_AC3]; } /* Audio */ @@ -7131,13 +7159,11 @@ return YES; [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"]; [preset setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"]; - /* Video framerate */ - /* Set the Video Frame Rate Mode */ + /* Video framerate and framerate mode */ if ([fFramerateMatrix selectedRow] == 1) { [preset setObject:@"cfr" forKey:@"VideoFramerateMode"]; } - /* Set the actual framerate from popup overriding the cfr setting as needed */ if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected { [preset setObject:@"Same as source" forKey:@"VideoFramerate"]; @@ -7147,9 +7173,11 @@ return YES; [preset setObject:@"vfr" forKey:@"VideoFramerateMode"]; } } - else // we can record the actual titleOfSelectedItem + else // translate the rate (selected item's tag) to the official libhb name { - [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"]; + [preset setObject:[NSString stringWithFormat:@"%s", + hb_video_framerate_get_name([[fVidRatePopUp selectedItem] tag])] + forKey:@"VideoFramerate"]; if ([fFramerateMatrix selectedRow] == 0) { diff --git a/macosx/HBAudio.m b/macosx/HBAudio.m index fdd7b5462..999aa9434 100644 --- a/macosx/HBAudio.m +++ b/macosx/HBAudio.m @@ -10,8 +10,7 @@ #import "hb.h" NSString *keyAudioCodecName = @"keyAudioCodecName"; -NSString *keyAudioMP4 = @"keyAudioMP4"; -NSString *keyAudioMKV = @"keyAudioMKV"; +NSString *keyAudioSupportedMuxers = @"keyAudioSupportedMuxers"; NSString *keyAudioSampleRateName = @"keyAudioSampleRateName"; NSString *keyAudioBitrateName = @"keyAudioBitrateName"; NSString *keyAudioMustMatchTrack = @"keyAudioMustMatchTrack"; @@ -70,83 +69,70 @@ static NSMutableArray *masterBitRateArray = nil; { if ([HBAudio class] == self) { - int i, audioMustMatch; - BOOL muxMKV, muxMP4; - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSDictionary *dict; - masterCodecArray = [[NSMutableArray alloc] init]; // knowingly leaked - for (i = 0; i < hb_audio_encoders_count; i++) + for (const hb_encoder_t *audio_encoder = hb_audio_encoder_get_next(NULL); + audio_encoder != NULL; + audio_encoder = hb_audio_encoder_get_next(audio_encoder)) { - if ((hb_audio_encoders[i].encoder & HB_ACODEC_PASS_FLAG) && - (hb_audio_encoders[i].encoder != HB_ACODEC_AUTO_PASS)) + id audioMustMatchTrack; + if ((audio_encoder->codec & HB_ACODEC_PASS_FLAG) && + (audio_encoder->codec != HB_ACODEC_AUTO_PASS)) { - audioMustMatch = (hb_audio_encoders[i].encoder & ~HB_ACODEC_PASS_FLAG); + audioMustMatchTrack = [NSNumber + numberWithInt:(audio_encoder->codec & + ~HB_ACODEC_PASS_FLAG)]; } else { - audioMustMatch = 0; - } - muxMKV = (hb_audio_encoders[i].muxers & HB_MUX_MKV) ? YES : NO; - muxMP4 = (hb_audio_encoders[i].muxers & HB_MUX_MP4) ? YES : NO; - if (audioMustMatch) - { - [masterCodecArray addObject: [NSDictionary dictionaryWithObjectsAndKeys: - [NSString stringWithUTF8String: hb_audio_encoders[i].human_readable_name], keyAudioCodecName, - [NSNumber numberWithInt: hb_audio_encoders[i].encoder], keyAudioCodec, - [NSNumber numberWithBool: muxMP4], keyAudioMP4, - [NSNumber numberWithBool: muxMKV], keyAudioMKV, - [NSNumber numberWithInt: audioMustMatch], keyAudioMustMatchTrack, - nil]]; - } - else - { - [masterCodecArray addObject: [NSDictionary dictionaryWithObjectsAndKeys: - [NSString stringWithUTF8String: hb_audio_encoders[i].human_readable_name], keyAudioCodecName, - [NSNumber numberWithInt: hb_audio_encoders[i].encoder], keyAudioCodec, - [NSNumber numberWithBool: muxMP4], keyAudioMP4, - [NSNumber numberWithBool: muxMKV], keyAudioMKV, - [NSNumber numberWithBool: NO], keyAudioMustMatchTrack, - nil]]; + audioMustMatchTrack = [NSNumber numberWithBool:NO]; } + [masterCodecArray addObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithUTF8String:audio_encoder->name], keyAudioCodecName, + [NSNumber numberWithInt:audio_encoder->codec], keyAudioCodec, + [NSNumber numberWithInt:audio_encoder->muxers], keyAudioSupportedMuxers, + audioMustMatchTrack, keyAudioMustMatchTrack, + nil]]; } masterMixdownArray = [[NSMutableArray alloc] init]; // knowingly leaked - for (i = 0; i < hb_audio_mixdowns_count; i++) + for (const hb_mixdown_t *mixdown = hb_mixdown_get_next(NULL); + mixdown != NULL; + mixdown = hb_mixdown_get_next(mixdown)) { - [masterMixdownArray addObject: [NSDictionary dictionaryWithObjectsAndKeys: - [NSString stringWithUTF8String: hb_audio_mixdowns[i].human_readable_name], keyAudioMixdownName, - [NSNumber numberWithInt: hb_audio_mixdowns[i].amixdown], keyAudioMixdown, - nil]]; + [masterMixdownArray addObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithUTF8String:mixdown->name], keyAudioMixdownName, + [NSNumber numberWithInt:mixdown->amixdown], keyAudioMixdown, + nil]]; } // Note that for the Auto value we use 0 for the sample rate because our controller will give back the track's // input sample rate when it finds this 0 value as the selected sample rate. We do this because the input // sample rate depends on the track, which means it depends on the title, so cannot be nicely set up here. masterSampleRateArray = [[NSMutableArray alloc] init]; // knowingly leaked - [masterSampleRateArray addObject: [NSDictionary dictionaryWithObjectsAndKeys: - NSLocalizedString(@"Auto", @"Auto"), keyAudioSampleRateName, - [NSNumber numberWithInt: 0], keyAudioSamplerate, - nil]]; - for (i = 0; i < hb_audio_rates_count; i++) + [masterSampleRateArray addObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithString:@"Auto"], keyAudioSampleRateName, + [NSNumber numberWithInt:0], keyAudioSamplerate, + nil]]; + for (const hb_rate_t *audio_samplerate = hb_audio_samplerate_get_next(NULL); + audio_samplerate != NULL; + audio_samplerate = hb_audio_samplerate_get_next(audio_samplerate)) { - [masterSampleRateArray addObject: [NSDictionary dictionaryWithObjectsAndKeys: - [NSString stringWithUTF8String: hb_audio_rates[i].string], keyAudioSampleRateName, - [NSNumber numberWithInt: hb_audio_rates[i].rate], keyAudioSamplerate, - nil]]; + [masterSampleRateArray addObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithUTF8String:audio_samplerate->name], keyAudioSampleRateName, + [NSNumber numberWithInt:audio_samplerate->rate], keyAudioSamplerate, + nil]]; } masterBitRateArray = [[NSMutableArray alloc] init]; // knowingly leaked - for (i = 0; i < hb_audio_bitrates_count; i++) + for (const hb_rate_t *audio_bitrate = hb_audio_bitrate_get_next(NULL); + audio_bitrate != NULL; + audio_bitrate = hb_audio_bitrate_get_next(audio_bitrate)) { - dict = [NSDictionary dictionaryWithObjectsAndKeys: - [NSString stringWithUTF8String: hb_audio_bitrates[i].string], keyAudioBitrateName, - [NSNumber numberWithInt: hb_audio_bitrates[i].rate], keyAudioBitrate, - nil]; - [masterBitRateArray addObject: dict]; + [masterBitRateArray addObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithUTF8String:audio_bitrate->name], keyAudioBitrateName, + [NSNumber numberWithInt:audio_bitrate->rate], keyAudioBitrate, + nil]]; } - - [pool release]; } } @@ -158,21 +144,6 @@ static NSMutableArray *masterBitRateArray = nil; NSMutableArray *permittedCodecs = [NSMutableArray array]; unsigned int count = [masterCodecArray count]; NSDictionary *dict; - NSString *keyThatAllows = nil; - - // Determine which key we use to see which codecs are permitted - switch ([videoContainerTag intValue]) - { - case HB_MUX_MP4: - keyThatAllows = keyAudioMP4; - break; - case HB_MUX_MKV: - keyThatAllows = keyAudioMKV; - break; - default: - keyThatAllows = @"error condition"; - break; - } // First get a list of the permitted codecs based on the internal rules if (nil != track && [self enabled]) @@ -184,7 +155,8 @@ static NSMutableArray *masterBitRateArray = nil; dict = [masterCodecArray objectAtIndex: i]; // First make sure only codecs permitted by the container are here - goodToAdd = [[dict objectForKey: keyThatAllows] boolValue]; + goodToAdd = !!([[dict objectForKey:keyAudioSupportedMuxers] intValue] & + [videoContainerTag intValue]); // Now we make sure if DTS or AC3 is not available in the track it is not put in the codec list, but in a general way if ([[dict objectForKey: keyAudioMustMatchTrack] boolValue]) @@ -227,9 +199,9 @@ static NSMutableArray *masterBitRateArray = nil; int currentMixdown; unsigned long long channelLayout = [[track objectForKey: keyAudioInputChannelLayout] unsignedLongLongValue]; - unsigned int count = [masterMixdownArray count]; - int codecCodec = [[codec objectForKey: keyAudioCodec] intValue]; - int theDefaultMixdown = hb_get_default_mixdown(codecCodec, channelLayout); + unsigned int count = [masterMixdownArray count]; + int codecCodec = [[codec objectForKey: keyAudioCodec] intValue]; + int theDefaultMixdown = hb_mixdown_get_default(codecCodec, channelLayout); for (unsigned int i = 0; i < count; i++) { @@ -282,10 +254,10 @@ static NSMutableArray *masterBitRateArray = nil; theSampleRate = [[[self track] objectForKey: keyAudioInputSampleRate] intValue]; } - int ourCodec = [[codec objectForKey: keyAudioCodec] intValue]; - int ourMixdown = [[[self mixdown] objectForKey: keyAudioMixdown] intValue]; - hb_get_audio_bitrate_limits(ourCodec, theSampleRate, ourMixdown, &minBitRate, &maxBitRate); - int theDefaultBitRate = hb_get_default_audio_bitrate(ourCodec, theSampleRate, ourMixdown); + int ourCodec = [[codec objectForKey:keyAudioCodec] intValue]; + int ourMixdown = [[[self mixdown] objectForKey:keyAudioMixdown] intValue]; + int theDefaultBitRate = hb_audio_bitrate_get_default(ourCodec, theSampleRate, ourMixdown); + hb_audio_bitrate_get_limits(ourCodec, theSampleRate, ourMixdown, &minBitRate, &maxBitRate); BOOL codecIsPassthru = ([[codec objectForKey: keyAudioCodec] intValue] & HB_ACODEC_PASS_FLAG) ? YES : NO; BOOL codecIsLossless = (theDefaultBitRate == -1) ? YES : NO; @@ -582,8 +554,8 @@ static NSMutableArray *masterBitRateArray = nil; if (retval) { - int myCodecCodec = [[[self codec] objectForKey: keyAudioCodec] intValue]; - int myCodecDefaultBitrate = hb_get_default_audio_bitrate(myCodecCodec, 0, 0); + int myCodecCodec = [[[self codec] objectForKey:keyAudioCodec] intValue]; + int myCodecDefaultBitrate = hb_audio_bitrate_get_default(myCodecCodec, 0, 0); if (myCodecDefaultBitrate < 0) { retval = NO; diff --git a/macosx/HBAudioController.m b/macosx/HBAudioController.m index 353e24f16..52cc8284b 100644 --- a/macosx/HBAudioController.m +++ b/macosx/HBAudioController.m @@ -163,7 +163,7 @@ NSString *HBMixdownChangedNotification = @"HBMixdownChangedNotification"; /* We go ahead and assign values to our audio->out.<properties> */ audio->out.track = audio->in.track; audio->out.codec = [[[anAudio codec] objectForKey:keyAudioCodec] intValue]; - audio->out.compression_level = hb_get_default_audio_compression(audio->out.codec); + audio->out.compression_level = hb_audio_compression_get_default(audio->out.codec); audio->out.mixdown = [[[anAudio mixdown] objectForKey:keyAudioMixdown] intValue]; audio->out.normalize_mix_level = 0; audio->out.bitrate = [[[anAudio bitRate] objectForKey:keyAudioBitrate] intValue]; @@ -295,46 +295,38 @@ NSString *HBMixdownChangedNotification = @"HBMixdownChangedNotification"; [newAudio setVideoContainerTag: [self videoContainerTag]]; [newAudio setTrackFromIndex: trackIndex]; - key = [dict objectForKey: @"AudioEncoder"]; - // map faac to ca_aac for built-in presets (can be disabled in preferences) if (0 == aType && - [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] && - [key isEqualToString: @"AAC (faac)"]) + [[NSUserDefaults standardUserDefaults] boolForKey:@"UseCoreAudio"] && + [[dict objectForKey:@"AudioEncoder"] isEqualToString:@"AAC (faac)"]) { - [dict setObject: @"AAC (CoreAudio)" forKey: @"AudioEncoder"]; + key = @"AAC (CoreAudio)"; } - - // passthru fallbacks - if ([key isEqualToString: @"AAC Passthru"]) + else { - if (![newAudio setCodecFromName: key]) - { - [dict setObject: @"AAC (CoreAudio)" forKey: @"AudioEncoder"]; - fallenBack = YES; - } + key = [dict objectForKey:@"AudioEncoder"]; } - else if ([key isEqualToString: @"AC3 Passthru"]) + + // map legacy encoder names via libhb + if (key != nil) { - if (![newAudio setCodecFromName: key]) + const char *name; + // passthru fallbacks + if ([key hasSuffix:@"Passthru"] && + ![newAudio setCodecFromName:key]) { - [dict setObject: @"AC3 (ffmpeg)" forKey: @"AudioEncoder"]; + int passthru, fallback; fallenBack = YES; + passthru = hb_audio_encoder_get_from_name([key UTF8String]); + fallback = hb_audio_encoder_get_fallback_for_passthru(passthru); + name = hb_audio_encoder_get_name(fallback); } - } - else if ([key isEqualToString: @"MP3 Passthru"]) - { - if (![newAudio setCodecFromName: key]) + else { - [dict setObject: @"MP3 (lame)" forKey: @"AudioEncoder"]; - fallenBack = YES; + name = hb_audio_encoder_sanitize_name([key UTF8String]); } - } - - // map legacy encoder names - if ([key isEqualToString: @"AC3"]) - { - [dict setObject: @"AC3 (ffmpeg)" forKey: @"AudioEncoder"]; + [dict setObject:[NSString stringWithFormat:@"%s", name] + forKey:@"AudioEncoder"]; } // If our preset does not contain a drc or gain value set it to a default of 0.0 @@ -347,17 +339,13 @@ NSString *HBMixdownChangedNotification = @"HBMixdownChangedNotification"; [dict setObject:[NSNumber numberWithFloat:0.0] forKey:@"AudioTrackGainSlider"]; } - // map legacy mixdowns + // map legacy mixdowns via libhb key = [dict objectForKey: @"AudioMixdown"]; - if ([key isEqualToString: @"AC3 Passthru"] || - [key isEqualToString: @"DTS Passthru"] || - [key isEqualToString: @"DTS-HD Passthru"]) - { - [dict setObject: @"None" forKey: @"AudioMixdown"]; - } - else if ([key isEqualToString: @"6-channel discrete"]) + if (key != nil) { - [dict setObject: @"5.1 Channels" forKey: @"AudioMixdown"]; + [dict setObject:[NSString stringWithFormat:@"%s", + hb_mixdown_sanitize_name([key UTF8String])] + forKey:@"AudioMixdown"]; } // If our preset wants us to support a codec that the track does not support, instead diff --git a/macosx/HBPreviewController.m b/macosx/HBPreviewController.m index aaf6ec36c..5d132a12e 100644 --- a/macosx/HBPreviewController.m +++ b/macosx/HBPreviewController.m @@ -729,12 +729,12 @@ } /* Destination file. We set this to our preview directory * changing the extension appropriately.*/ - if (fTitle->job->mux == HB_MUX_MP4) // MP4 file + if (fTitle->job->mux & HB_MUX_MASK_MP4) // MP4 file { /* we use .m4v for our mp4 files so that ac3 and chapters in mp4 will play properly */ fPreviewMoviePath = [PreviewDirectory stringByAppendingString:@"/preview_temp.m4v"]; } - else if (fTitle->job->mux == HB_MUX_MKV) // MKV file + else if (fTitle->job->mux & HB_MUX_MASK_MKV) // MKV file { fPreviewMoviePath = [PreviewDirectory stringByAppendingString:@"/preview_temp.mkv"]; } diff --git a/macosx/HBSubtitles.m b/macosx/HBSubtitles.m index d8b4b6989..989374e37 100644 --- a/macosx/HBSubtitles.m +++ b/macosx/HBSubtitles.m @@ -801,7 +801,7 @@ * by default to avoid massive confusion and anarchy. However we also want to guard against multiple burned in subtitle tracks * as libhb would ignore all but the first one anyway. Plus it would probably be stupid. */ - if (container == HB_MUX_MP4 && [anObject intValue] != 0) + if ((container & HB_MUX_MASK_MP4) && ([anObject intValue] != 0)) { if ([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackType"] intValue] == VOBSUB) { diff --git a/scripts/manicure.rb b/scripts/manicure.rb index 4dc9b8db7..c6d2cdc3f 100755 --- a/scripts/manicure.rb +++ b/scripts/manicure.rb @@ -273,9 +273,9 @@ class Display #Filename suffix case hash["FileFormat"] - when /MP4/ + when /MPEG-4/, /MP4/ commandString << "mp4 " - when /MKV/ + when /Matroska/, /MKV/ commandString << "mkv " end @@ -477,8 +477,12 @@ class Display #Container commandString << " -f " case hash["FileFormat"] + when "MPEG-4 (mp4v2)" + commandString << "mp4v2" when /MP4/ commandString << "mp4" + when "Matroska (libmkv)" + commandString << "libmkv" when /MKV/ commandString << "mkv" end @@ -861,8 +865,12 @@ class Display #Container commandString << " -f " case hash["FileFormat"] + when "MPEG-4 (mp4v2)" + commandString << "mp4v2" when /MP4/ commandString << "mp4" + when "Matroska (libmkv)" + commandString << "libmkv" when /MKV/ commandString << "mkv" end @@ -1020,13 +1028,17 @@ class Display commandString = "if (!strcasecmp(preset_name, \"" << hash["PresetName"] << "\"))\n{\n " - #Filename suffix + #Container commandString << "if( !mux )\n " commandString << "{\n " case hash["FileFormat"] + when "MPEG-4 (mp4v2)" + commandString << " mux = " << "HB_MUX_MP4V2;\n " when /MP4/ commandString << " mux = " << "HB_MUX_MP4;\n " + when "Matroska (libmkv)" + commandString << " mux = " << "HB_MUX_LIBMKV;\n " when /MKV/ commandString << " mux = " << "HB_MUX_MKV;\n " end @@ -1693,8 +1705,12 @@ class Display #Container commandString << " -f " case hash["FileFormat"] + when "MPEG-4 (mp4v2)" + commandString << "mp4v2" when /MP4/ commandString << "mp4" + when "Matroska (libmkv)" + commandString << "libmkv" when /MKV/ commandString << "mkv" end diff --git a/test/test.c b/test/test.c index efa7ae227..05b415043 100644 --- a/test/test.c +++ b/test/test.c @@ -153,11 +153,6 @@ static int ParseOptions( int argc, char ** argv ); static int CheckOptions( int argc, char ** argv ); static int HandleEvents( hb_handle_t * h ); -static int get_dither_for_string(const char *dither); -static int get_acodec_for_string(const char *codec); -static const char* get_string_for_acodec(int acodec); - -static int is_sample_rate_valid(int rate); static void str_vfree( char **strv ); static char** str_split( char *str, char delem ); @@ -578,7 +573,7 @@ static int HandleEvents( hb_handle_t * h ) { hb_state_t s; int tmp_num_audio_tracks; - int filter_vrate, filter_vrate_base, filter_cfr; + int filter_cfr, filter_vrate, filter_vrate_base; hb_get_state( h, &s ); switch( s.state ) @@ -688,10 +683,10 @@ static int HandleEvents( hb_handle_t * h ) PrintTitleInfo( title, title_set->feature ); /* Set job settings */ - job = hb_job_init( title ); - filter_vrate = job->vrate; + job = hb_job_init(title); + filter_cfr = job->cfr; + filter_vrate = job->vrate; filter_vrate_base = job->vrate_base; - filter_cfr = job->cfr; if( chapter_start && chapter_end && !stop_at_pts && !start_at_preview && !stop_at_frame && !start_at_pts && !start_at_frame ) @@ -1756,25 +1751,25 @@ static int HandleEvents( hb_handle_t * h ) free( filter_str ); // Add framerate shaping filter - if( vrate ) + if (vrate) { - filter_cfr = cfr; - filter_vrate = 27000000; + filter_cfr = cfr; + filter_vrate = 27000000; filter_vrate_base = vrate; } - else if ( cfr ) + else if (cfr) { // cfr or pfr flag with no rate specified implies // use the title rate. - filter_cfr = cfr; - filter_vrate = title->rate; + filter_cfr = cfr; + filter_vrate = title->rate; filter_vrate_base = title->rate_base; } - filter_str = hb_strdup_printf("%d:%d:%d", - filter_cfr, filter_vrate, filter_vrate_base ); - filter = hb_filter_init( HB_FILTER_VFR ); - hb_add_filter( job, filter, filter_str ); - free( filter_str ); + filter = hb_filter_init(HB_FILTER_VFR); + filter_str = hb_strdup_printf("%d:%d:%d", filter_cfr, filter_vrate, + filter_vrate_base); + hb_add_filter(job, filter, filter_str); + free(filter_str); // hb_job_init() will set a default muxer for us // only override it if a specific muxer has been set @@ -1966,10 +1961,10 @@ static int HandleEvents( hb_handle_t * h ) token = acodecs; while ( token != NULL ) { - if ((acodec = get_acodec_for_string(token)) == -1) + if ((acodec = hb_audio_encoder_get_from_name(token)) == -1) { fprintf(stderr, "Invalid codec %s, using default for container.\n", token); - acodec = hb_get_default_audio_encoder(job->mux); + acodec = hb_audio_encoder_get_default(job->mux); } if( i < num_audio_tracks ) { @@ -2007,7 +2002,7 @@ static int HandleEvents( hb_handle_t * h ) * then use that codec instead. */ if (i != 1) - acodec = hb_get_default_audio_encoder(job->mux); + acodec = hb_audio_encoder_get_default(job->mux); for ( ; i < num_audio_tracks; i++) { audio = hb_list_audio_config_item(job->list_audio, i); @@ -2026,18 +2021,7 @@ static int HandleEvents( hb_handle_t * h ) token = arates; while ( token != NULL ) { - arate = atoi(token); audio = hb_list_audio_config_item(job->list_audio, i); - int j; - - for( j = 0; j < hb_audio_rates_count; j++ ) - { - if( !strcmp( token, hb_audio_rates[j].string ) ) - { - arate = hb_audio_rates[j].rate; - break; - } - } if( audio != NULL ) { @@ -2046,9 +2030,15 @@ static int HandleEvents( hb_handle_t * h ) arate = audio->in.samplerate; auto_sample_rate = 1; } - if (!is_sample_rate_valid(arate)) + else + { + arate = hb_audio_samplerate_get_from_name(token); + } + if (arate == -1) { - fprintf(stderr, "Invalid sample rate %d, using input rate %d\n", arate, audio->in.samplerate); + fprintf(stderr, + "Invalid sample rate %s, using input rate %d\n", + token, audio->in.samplerate); arate = audio->in.samplerate; } @@ -2091,8 +2081,8 @@ static int HandleEvents( hb_handle_t * h ) token = mixdowns; while ( token != NULL ) { - mixdown = hb_mixdown_get_mixdown_from_short_name(token); - audio = hb_list_audio_config_item(job->list_audio, i); + mixdown = hb_mixdown_get_from_name(token); + audio = hb_list_audio_config_item(job->list_audio, i); if( audio != NULL ) { audio->out.mixdown = mixdown; @@ -2299,8 +2289,8 @@ static int HandleEvents( hb_handle_t * h ) int dither_method = hb_audio_dither_get_default(); for (i = 0; audio_dither[i] != NULL; i++) { - dither_method = get_dither_for_string(audio_dither[i]); - audio = hb_list_audio_config_item(job->list_audio, i); + dither_method = hb_audio_dither_get_from_name(audio_dither[i]); + audio = hb_list_audio_config_item(job->list_audio, i); if (audio != NULL) { if (hb_audio_dither_is_supported(audio->out.codec)) @@ -2422,45 +2412,41 @@ static int HandleEvents( hb_handle_t * h ) { // Auto Passthru job->acodec_copy_mask = allowed_audio_copy == -1 ? HB_ACODEC_PASS_MASK : allowed_audio_copy; - job->acodec_fallback = acodec_fallback ? get_acodec_for_string( acodec_fallback ) : 0; + job->acodec_fallback = hb_audio_encoder_get_from_name(acodec_fallback); // sanitize the fallback; -1 isn't a valid HB_ACODEC_* value - if( job->acodec_fallback == -1 ) - job->acodec_fallback = 0; + if (job->acodec_fallback == -1) + job->acodec_fallback = 0; } else if( ( audio->out.codec & HB_ACODEC_PASS_FLAG ) && !( audio->out.codec & audio->in.codec & HB_ACODEC_PASS_MASK ) ) { - if( audio->out.codec == HB_ACODEC_AAC_PASS ) - { - fprintf( stderr, "AAC Passthru requested and input codec is not AAC for track %d, using AAC encoder\n", - audio->out.track ); - audio->out.codec = hb_audio_encoders[0].encoder; - } - else if( audio->out.codec == HB_ACODEC_AC3_PASS ) - { - fprintf( stderr, "AC3 Passthru requested and input codec is not AC3 for track %d, using AC3 encoder\n", - audio->out.track ); - audio->out.codec = HB_ACODEC_AC3; - } - else if( audio->out.codec == HB_ACODEC_MP3_PASS ) - { - fprintf( stderr, "MP3 Passthru requested and input codec is not MP3 for track %d, using MP3 encoder\n", - audio->out.track ); - audio->out.codec = HB_ACODEC_LAME; - } - else + // passthru fallbacks + int requested_passthru = audio->out.codec; + audio->out.codec = + hb_audio_encoder_get_fallback_for_passthru(requested_passthru); + if (!(audio->out.codec & HB_ACODEC_MASK)) { // Passthru not possible, drop audio. - fprintf( stderr, "Passthru requested and input codec is not the same as output codec for track %d, dropping track\n", - audio->out.track ); - hb_audio_t * item = hb_list_item( job->list_audio, i ); - hb_list_rem( job->list_audio, item ); + fprintf(stderr, + "Passthru requested and input codec is not the same as output codec for track %d, dropping track\n", + audio->out.track); + hb_audio_t *item = hb_list_item(job->list_audio, i); + hb_list_rem(job->list_audio, item); + hb_audio_close(&item); continue; } + fprintf(stderr, + "%s requested and input codec is not compatible for track %d, using %s encoder\n", + hb_audio_encoder_get_name(requested_passthru), audio->out.track, + hb_audio_encoder_get_name(audio->out.codec)); // we didn't drop the track, set the mixdown and bitrate from libhb defaults - audio->out.mixdown = hb_get_default_mixdown( audio->out.codec, audio->in.channel_layout ); - audio->out.bitrate = hb_get_default_audio_bitrate( audio->out.codec, audio->out.samplerate, - audio->out.mixdown ); + audio->out.mixdown = + hb_mixdown_get_default(audio->out.codec, + audio->in.channel_layout); + audio->out.bitrate = + hb_audio_bitrate_get_default(audio->out.codec, + audio->out.samplerate, + audio->out.mixdown); } // we didn't drop the track i++; @@ -2891,7 +2877,13 @@ void SigHandler( int i_signal ) ****************************************************************************/ static void ShowHelp() { - int i, j, len; + int i, len; + const char *name; + const hb_rate_t *rate; + const hb_dither_t *dither; + const hb_mixdown_t *mixdown; + const hb_encoder_t *encoder; + const hb_container_t *container; FILE* const out = stdout; const char * const *x264_opts; char tmp[80]; @@ -2934,8 +2926,22 @@ static void ShowHelp() "### Destination Options------------------------------------------------------\n\n" " -o, --output <string> Set output file name\n" - " -f, --format <string> Set output format (mp4/mkv, default:\n" - " autodetected from file name)\n" + " -f, --format <string> Set output container format ("); + container = NULL; + while ((container = hb_container_get_next(container)) != NULL) + { + fprintf(out, "%s", container->short_name); + if (hb_container_get_next(container) != NULL) + { + fprintf(out, "/"); + } + else + { + fprintf(out, ")\n"); + } + } + fprintf(out, + " (default: autodetected from file name)\n" " -m, --markers Add chapter markers\n" " -4, --large-file Create 64-bit mp4 files that can hold more than 4 GB\n" " of data. Note: breaks pre-iOS iPod compatibility.\n" @@ -2947,23 +2953,24 @@ static void ShowHelp() "### Video Options------------------------------------------------------------\n\n" " -e, --encoder <string> Set video library encoder\n" " Options: " ); - for( i = 0; i < hb_video_encoders_count; i++ ) + encoder = NULL; + while ((encoder = hb_video_encoder_get_next(encoder)) != NULL) { - fprintf( out, "%s", hb_video_encoders[i].short_name ); - if( i != hb_video_encoders_count - 1 ) - fprintf( out, "/" ); + fprintf(out, "%s", encoder->short_name); + if (hb_video_encoder_get_next(encoder) != NULL) + { + fprintf(out, "/"); + } else - fprintf( out, "\n" ); - } - for( i = 0; i < hb_video_encoders_count; i++ ) - { - if( hb_video_encoders[i].encoder == vcodec ) { - fprintf( out, " (default: %s)\n", - hb_video_encoders[i].short_name ); - break; + fprintf(out, "\n"); + } + if (encoder->codec == vcodec) + { + name = encoder->short_name; } } + fprintf(out, " (default: %s)\n", name); fprintf( out, " --x264-preset When using x264, selects the x264 preset:\n" " <string> "); @@ -3060,11 +3067,14 @@ static void ShowHelp() " -T, --turbo When using 2-pass use \"turbo\" options on the\n" " 1st pass to improve speed (only works with x264)\n" " -r, --rate Set video framerate (" ); - for( i = 0; i < hb_video_rates_count; i++ ) + rate = NULL; + while ((rate = hb_video_framerate_get_next(rate)) != NULL) { - fprintf( out, "%s", hb_video_rates[i].string ); - if( i != hb_video_rates_count - 1 ) - fprintf( out, "/" ); + fprintf(out, "%s", rate->name); + if (hb_video_framerate_get_next(rate) != NULL) + { + fprintf(out, "/"); + } } fprintf( out, ")\n" " Be aware that not specifying a framerate lets\n" @@ -3088,33 +3098,44 @@ static void ShowHelp() " tracks, default: first one).\n" " Multiple output tracks can be used for one input.\n" " -E, --aencoder <string> Audio encoder(s):\n" ); - for (i = 0; i < hb_audio_encoders_count; i++) + encoder = NULL; + while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) { fprintf(out, " %s\n", - hb_audio_encoders[i].short_name); + encoder->short_name); } fprintf(out, " copy:* will passthrough the corresponding\n" " audio unmodified to the muxer if it is a\n" " supported passthrough audio type.\n" " Separated by commas for more than one audio track.\n" - " (default: %s for mp4, %s for mkv)\n", - get_string_for_acodec(hb_get_default_audio_encoder(HB_MUX_MP4)), - get_string_for_acodec(hb_get_default_audio_encoder(HB_MUX_MKV))); + " Defaults:\n"); + container = NULL; + while ((container = hb_container_get_next(container)) != NULL) + { + int audio_encoder = hb_audio_encoder_get_default(container->format); + fprintf(out, " %-8s %s\n", + container->short_name, + hb_audio_encoder_get_short_name(audio_encoder)); + } fprintf(out, " --audio-copy-mask Set audio codecs that are permitted when the\n" " <string> \"copy\" audio encoder option is specified\n" " (" ); - for (i = j = 0; i < hb_audio_encoders_count; i++) + i = 0; + encoder = NULL; + while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) { - if ((hb_audio_encoders[i].encoder & HB_ACODEC_PASS_FLAG) && - (hb_audio_encoders[i].encoder != HB_ACODEC_AUTO_PASS)) + if ((encoder->codec & HB_ACODEC_PASS_FLAG) && + (encoder->codec != HB_ACODEC_AUTO_PASS)) { - if (j) + if (i) + { fprintf(out, "/"); + } + i = 1; // skip "copy:" - fprintf(out, "%s", hb_audio_encoders[i].short_name + 5); - j = 1; + fprintf(out, "%s", encoder->short_name + 5); } } fprintf(out, ", default: all).\n" @@ -3132,25 +3153,25 @@ static void ShowHelp() " Separated by commas for more than one audio track.\n" " -6, --mixdown <string> Format(s) for audio downmixing/upmixing:\n"); // skip HB_AMIXDOWN_NONE - for (i = 1; i < hb_audio_mixdowns_count; i++) + mixdown = hb_mixdown_get_next(NULL); + while((mixdown = hb_mixdown_get_next(mixdown)) != NULL) { fprintf(out, " %s\n", - hb_audio_mixdowns[i].short_name); + mixdown->short_name); } fprintf(out, " Separated by commas for more than one audio track.\n" " Defaults:\n"); - for (i = 0; i < hb_audio_encoders_count; i++) + encoder = NULL; + while((encoder = hb_audio_encoder_get_next(encoder)) != NULL) { - if (!(hb_audio_encoders[i].encoder & HB_ACODEC_PASS_FLAG)) + if (!(encoder->codec & HB_ACODEC_PASS_FLAG)) { // layout: UINT64_MAX (all channels) should work with any mixdown - int mixdown = hb_get_default_mixdown(hb_audio_encoders[i].encoder, - UINT64_MAX); + int mixdown = hb_mixdown_get_default(encoder->codec, UINT64_MAX); // assumes that the encoder short name is <= 16 characters long fprintf(out, " %-16s up to %s\n", - hb_audio_encoders[i].short_name, - hb_mixdown_get_short_name_from_mixdown(mixdown)); + encoder->short_name, hb_mixdown_get_short_name(mixdown)); } } fprintf(out, @@ -3159,11 +3180,14 @@ static void ShowHelp() " 0 = Disable Normalization (default)\n" " 1 = Enable Normalization\n" " -R, --arate Set audio samplerate(s) (" ); - for( i = 0; i < hb_audio_rates_count; i++ ) + rate = NULL; + while ((rate = hb_audio_samplerate_get_next(rate)) != NULL) { - fprintf( out, "%s", hb_audio_rates[i].string ); - if( i != hb_audio_rates_count - 1 ) - fprintf( out, "/" ); + fprintf(out, "%s", rate->name); + if (hb_audio_samplerate_get_next(rate) != NULL) + { + fprintf(out, "/"); + } } fprintf( out, " kHz)\n" " Separated by commas for more than one audio track.\n" @@ -3178,30 +3202,35 @@ static void ShowHelp() " --adither <string> Apply dithering to the audio before encoding.\n" " Separated by commas for more than one audio track.\n" " Only supported by some encoders ("); - for (i = j = 0; i < hb_audio_encoders_count; i++) + i = 0; + encoder = NULL; + while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) { - if (hb_audio_dither_is_supported(hb_audio_encoders[i].encoder)) + if (hb_audio_dither_is_supported(encoder->codec)) { - if (j) + if (i) + { fprintf(out, "/"); - fprintf(out, "%s", hb_audio_encoders[i].short_name); - j = 1; + } + i = 1; + fprintf(out, "%s", encoder->short_name); } } fprintf(out, ").\n"); fprintf(out, " Options:\n"); - for (i = 0; i < hb_audio_dithers_count; i++) + dither = NULL; + while ((dither = hb_audio_dither_get_next(dither)) != NULL) { - if (hb_audio_dithers[i].method == hb_audio_dither_get_default()) + if (dither->method == hb_audio_dither_get_default()) { fprintf(out, " %s (default)\n", - hb_audio_dithers[i].short_name); + dither->short_name); } else { fprintf(out, " %s\n", - hb_audio_dithers[i].short_name); + dither->short_name); } } fprintf(out, @@ -3894,18 +3923,10 @@ static int ParseOptions( int argc, char ** argv ) break; case 'e': { - int i; - for( i = 0, vcodec = 0; i < hb_video_encoders_count; i++ ) - { - if( !strcasecmp( hb_video_encoders[i].short_name, optarg ) ) - { - vcodec = hb_video_encoders[i].encoder; - break; - } - } - if( !vcodec ) + vcodec = hb_video_encoder_get_from_name(optarg); + if (vcodec <= 0) { - fprintf( stderr, "invalid codec (%s)\n", optarg ); + fprintf(stderr, "invalid codec (%s)\n", optarg); return -1; } break; @@ -3940,21 +3961,13 @@ static int ParseOptions( int argc, char ** argv ) break; case 'r': { - int i; - vrate = 0; - for( i = 0; i < hb_video_rates_count; i++ ) - { - if( !strcmp( optarg, hb_video_rates[i].string ) ) - { - vrate = hb_video_rates[i].rate; - break; - } - } - if( !vrate ) + vrate = hb_video_framerate_get_from_name(optarg); + if (vrate <= 0) { - fprintf( stderr, "invalid framerate %s\n", optarg ); + vrate = 0; + fprintf(stderr, "invalid framerate %s\n", optarg); } - else if ( cfr == 0 ) + else if (!cfr) { cfr = 1; } @@ -4067,28 +4080,33 @@ static int ParseOptions( int argc, char ** argv ) } case ALLOWED_AUDIO_COPY: { - int i, j; - char **allowed = str_split( optarg, ',' ); + allowed_audio_copy = 0; + const hb_encoder_t *audio_encoder = NULL; + char **allowed = str_split(optarg, ','); - allowed_audio_copy = 0; - for( i = 0; allowed[i]; i++ ) + while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) { - for( j = 0; j < hb_audio_encoders_count; j++ ) + if ((audio_encoder->codec & HB_ACODEC_PASS_FLAG) && + (audio_encoder->codec != HB_ACODEC_AUTO_PASS)) { - const char *encoder = hb_audio_encoders[j].short_name; - // skip "copy:" - if( strlen( encoder ) > 5 ) - encoder += 5; - if( !strcmp( allowed[i], encoder ) ) + int i = -1; + while(allowed[++i] != NULL) { - allowed_audio_copy |= hb_audio_encoders[j].encoder; - break; + // skip "copy:" + if (!strcasecmp(allowed[i], + audio_encoder->short_name + 5)) + { + allowed_audio_copy |= audio_encoder->codec; + break; + } } } } + allowed_audio_copy &= HB_ACODEC_PASS_MASK; - str_vfree( allowed ); - } break; + str_vfree(allowed); + break; + } case AUDIO_FALLBACK: acodec_fallback = strdup( optarg ); break; @@ -4139,94 +4157,50 @@ static int CheckOptions( int argc, char ** argv ) return 1; } - if( !format ) + if (format == NULL) { - char * p = strrchr( output, '.' ); - /* autodetect */ - if( p && ( !strcasecmp( p, ".mp4" ) || - !strcasecmp( p, ".m4v" ) ) ) - { - mux = HB_MUX_MP4; - } - else if( p && !strcasecmp(p, ".mkv" ) ) + const char *extension = strrchr(output, '.'); + if (extension != NULL) { - mux = HB_MUX_MKV; + // skip '.' + mux = hb_container_get_from_extension(extension + 1); } - else + if (mux <= 0) { - fprintf( stderr, "Output format couldn't be guessed " - "from file name, using default.\n" ); + fprintf(stderr, + "Output format can't be guessed from file name (%s), " + "using default.\n", output); + // reset the muxer (use default) + mux = 0; return 0; } } - else if( !strcasecmp( format, "mp4" ) || - !strcasecmp( format, "m4v" ) ) - { - mux = HB_MUX_MP4; - } - else if( !strcasecmp( format, "mkv" ) ) - { - mux = HB_MUX_MKV; - } else { - fprintf( stderr, "Invalid output format (%s). Possible " - "choices are mp4, m4v and mkv\n.", format ); - return 1; - } - } - - return 0; -} - -static int get_dither_for_string(const char *dither) -{ - int i; - for (i = 0; i < hb_audio_dithers_count; i++) - { - if (!strcasecmp(hb_audio_dithers[i].short_name, dither)) - { - return hb_audio_dithers[i].method; - } - } - return hb_audio_dither_get_default(); -} - -static int get_acodec_for_string(const char *codec) -{ - int i; - for (i = 0; i < hb_audio_encoders_count; i++) - { - if (!strcasecmp(hb_audio_encoders[i].short_name, codec)) - { - return hb_audio_encoders[i].encoder; - } - } - return -1; -} - -static const char* get_string_for_acodec(int acodec) -{ - int i; - for (i = 0; i < hb_audio_encoders_count; i++) - { - if (hb_audio_encoders[i].encoder == acodec) - { - return hb_audio_encoders[i].short_name; + mux = hb_container_get_from_name(format); + if (mux <= 0) + { + fprintf(stderr, "Invalid output format (%s).", format); + fprintf(stderr, "Possible choices are: "); + const hb_container_t *container = NULL; + while ((container = hb_container_get_next(container)) != NULL) + { + fprintf(stderr, "%s", container->short_name); + if (hb_container_get_next(container) != NULL) + { + fprintf(stderr, ", "); + } + else + { + fprintf(stderr, "\n"); + } + } + return 1; + } } } - return NULL; -} -static int is_sample_rate_valid(int rate) -{ - int i; - for( i = 0; i < hb_audio_rates_count; i++ ) - { - if (rate == hb_audio_rates[i].rate) - return 1; - } return 0; } |