diff options
author | Rodeo <[email protected]> | 2013-02-05 17:53:03 +0000 |
---|---|---|
committer | Rodeo <[email protected]> | 2013-02-05 17:53:03 +0000 |
commit | 5ca5ec3e6f4ebb47e92984a4ef859510df56b537 (patch) | |
tree | 22dbb88491ce5e6d7b8c844ebdb79581993f2e52 /libhb | |
parent | 4017dfc2050336c4476f89fd4c4e358449b9376e (diff) |
Audio dithering.
Works with encoders that accept 16-bit signed integers as input (currently, only ffflac).
When supported, the default method is standard triangular.
CLI users can request a specific dither algorithm via the --adither option.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@5241 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb')
-rw-r--r-- | libhb/common.c | 57 | ||||
-rw-r--r-- | libhb/common.h | 18 | ||||
-rw-r--r-- | libhb/encavcodecaudio.c | 10 | ||||
-rw-r--r-- | libhb/work.c | 24 |
4 files changed, 109 insertions, 0 deletions
diff --git a/libhb/common.c b/libhb/common.c index 355b458e5..cfbeb6185 100644 --- a/libhb/common.c +++ b/libhb/common.c @@ -83,6 +83,17 @@ 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, }, + { "none", "none", AV_RESAMPLE_DITHER_NONE, }, + { "rectangular", "rectangular", AV_RESAMPLE_DITHER_RECTANGULAR, }, + { "triangular", "triangular", AV_RESAMPLE_DITHER_TRIANGULAR, }, + { "triangular with high pass", "triangular_hp", AV_RESAMPLE_DITHER_TRIANGULAR_HP, }, + { "triangular with noise shaping", "triangular_ns", AV_RESAMPLE_DITHER_TRIANGULAR_NS, }, +}; +int hb_audio_dithers_count = sizeof(hb_audio_dithers) / sizeof(hb_dither_t); + hb_mixdown_t hb_audio_mixdowns[] = { { "None", "HB_AMIXDOWN_NONE", "none", HB_AMIXDOWN_NONE }, @@ -138,6 +149,8 @@ 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; } @@ -145,6 +158,47 @@ 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() +{ + // "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: + return 1; + default: + return 0; + } +} + +const char* hb_audio_dither_get_description(int method) +{ + int i; + for (i = 0; i < hb_audio_dithers_count; i++) + { + if (hb_audio_dithers[i].method == method) + { + return hb_audio_dithers[i].description; + } + } + return ""; +} + + int hb_mixdown_is_supported(int mixdown, uint32_t codec, uint64_t layout) { return (hb_mixdown_has_codec_support(mixdown, codec) && @@ -2240,6 +2294,7 @@ void hb_audio_config_init(hb_audio_config_t * audiocfg) audiocfg->out.dynamic_range_compression = 0; audiocfg->out.gain = 0; audiocfg->out.normalize_mix_level = 0; + audiocfg->out.dither_method = hb_audio_dither_get_default(); audiocfg->out.name = NULL; } @@ -2290,6 +2345,7 @@ int hb_audio_add(const hb_job_t * job, const hb_audio_config_t * audiocfg) audio->config.out.normalize_mix_level = 0; audio->config.out.compression_level = -1; audio->config.out.quality = HB_INVALID_AUDIO_QUALITY; + audio->config.out.dither_method = AV_RESAMPLE_DITHER_NONE; } else { @@ -2303,6 +2359,7 @@ int hb_audio_add(const hb_job_t * job, const hb_audio_config_t * audiocfg) audio->config.out.mixdown = audiocfg->out.mixdown; audio->config.out.gain = audiocfg->out.gain; audio->config.out.normalize_mix_level = audiocfg->out.normalize_mix_level; + audio->config.out.dither_method = audiocfg->out.dither_method; } if (audiocfg->out.name && *audiocfg->out.name) { diff --git a/libhb/common.h b/libhb/common.h index ac7d46287..aace4b688 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -70,6 +70,7 @@ typedef struct hb_handle_s hb_handle_t; typedef struct hb_list_s hb_list_t; 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_job_s hb_job_t; @@ -174,6 +175,13 @@ struct hb_rate_s int rate; }; +struct hb_dither_s +{ + const char *description; + const char *short_name; + int method; +}; + struct hb_mixdown_s { const char *human_readable_name; @@ -211,6 +219,8 @@ 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[]; @@ -226,6 +236,8 @@ 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(); @@ -233,6 +245,11 @@ 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); @@ -519,6 +536,7 @@ struct hb_audio_config_s double dynamic_range_compression; /* Amount of DRC applied to this track */ double gain; /* Gain (in dB), negative is quieter */ int normalize_mix_level; /* mix level normalization (boolean) */ + int dither_method; /* dither algorithm */ char * name; /* Output track name */ } out; diff --git a/libhb/encavcodecaudio.c b/libhb/encavcodecaudio.c index 669691dc1..ecd75ff98 100644 --- a/libhb/encavcodecaudio.c +++ b/libhb/encavcodecaudio.c @@ -148,6 +148,16 @@ static int encavcodecaInit(hb_work_object_t *w, hb_job_t *job) context->channel_layout, 0); av_opt_set_int(pv->avresample, "out_channel_layout", context->channel_layout, 0); + if (hb_audio_dither_is_supported(audio->config.out.codec)) + { + // dithering needs the sample rate + av_opt_set_int(pv->avresample, "in_sample_rate", + context->sample_rate, 0); + av_opt_set_int(pv->avresample, "out_sample_rate", + context->sample_rate, 0); + av_opt_set_int(pv->avresample, "dither_method", + audio->config.out.dither_method, 0); + } if (avresample_open(pv->avresample)) { hb_error("encavcodecaInit: avresample_open() failed"); diff --git a/libhb/work.c b/libhb/work.c index 9ee2b0593..575bebdbd 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -486,6 +486,11 @@ void hb_display_job_info( hb_job_t * job ) { hb_log( " + dynamic range compression: %f", audio->config.out.dynamic_range_compression ); } + if (hb_audio_dither_is_supported(audio->config.out.codec)) + { + hb_log(" + dither: %s", + hb_audio_dither_get_description(audio->config.out.dither_method)); + } for( j = 0; j < hb_audio_encoders_count; j++ ) { if( hb_audio_encoders[j].encoder == audio->config.out.codec ) @@ -992,6 +997,25 @@ static void do_job( hb_job_t * job ) audio->config.out.bitrate = best_bitrate; } } + + /* sense-check the requested dither */ + if (hb_audio_dither_is_supported(audio->config.out.codec)) + { + if (audio->config.out.dither_method == + hb_audio_dither_get_default()) + { + /* "auto", enable with default settings */ + audio->config.out.dither_method = + hb_audio_dither_get_default_method(); + } + } + else if (audio->config.out.dither_method != + hb_audio_dither_get_default()) + { + /* specific dither requested but dithering not supported */ + hb_log("work: track %d, dithering not supported by codec", + audio->config.out.track); + } } } |