From ba69a81019a2642969b108c39e3bea7d2f8ffbfa Mon Sep 17 00:00:00 2001 From: Burt P Date: Tue, 5 Jul 2016 12:23:33 -0500 Subject: libavfilter/af_hdcd.c: Collect HDCD stats and report The new HDCD filter really does nothing to show that it is working or that HDCD control information was even detected in the stream. This patch collects information about the decode, like which features were used, and reports it to the user at the end. Also, * Fixes low-level gain adjustment * Updates the documentation Signed-off-by: Burt P Reviewed-by: Paul B Mahol Signed-off-by: Michael Niedermayer --- libavfilter/af_hdcd.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 3 deletions(-) (limited to 'libavfilter/af_hdcd.c') diff --git a/libavfilter/af_hdcd.c b/libavfilter/af_hdcd.c index 16bdcb01ea..89012eb53e 100644 --- a/libavfilter/af_hdcd.c +++ b/libavfilter/af_hdcd.c @@ -31,7 +31,8 @@ */ /* - More information about high definition audio cds: + HDCD is High Definition Compatible Digital + More information about HDCD-encoded audio CDs: http://www.audiomisc.co.uk/HFN/HDCD/Enigma.html http://www.audiomisc.co.uk/HFN/HDCD/Examined.html */ @@ -823,11 +824,29 @@ typedef struct { int code_counterA; int code_counterB; int code_counterC; + + /* For user information/stats, pulled up into HDCDContext + * by filter_frame() */ + int count_peak_extend; + int count_transient_filter; + /* target_gain is a 4-bit (3.1) fixed-point value, always + * negative, but stored positive. + * The 16 possible values range from -7.5 to 0.0 dB in + * steps of 0.5, but no value below -6.0 dB should appear. */ + int gain_counts[16]; /* for cursiosity, mostly */ + int max_gain; + int cb6, cb7; /* watch bits 6 and 7 of the control code, for curiosity */ } hdcd_state_t; typedef struct HDCDContext { const AVClass *class; hdcd_state_t state[2]; + + /* User information/stats */ + int hdcd_detected; + int uses_peak_extend; + int uses_transient_filter; /* detected, but not implemented */ + float max_gain_adjustment; /* in dB, expected in the range -6.0 to 0.0 */ } HDCDContext; static const AVOption hdcd_options[] = { @@ -840,6 +859,8 @@ AVFILTER_DEFINE_CLASS(hdcd); static void hdcd_reset(hdcd_state_t *state, unsigned rate) { + int i; + state->window = 0; state->readahead = 32; state->arg = 0; @@ -853,6 +874,13 @@ static void hdcd_reset(hdcd_state_t *state, unsigned rate) state->code_counterA = 0; state->code_counterB = 0; state->code_counterC = 0; + + state->count_peak_extend = 0; + state->count_transient_filter = 0; + for(i = 0; i < 16; i++) state->gain_counts[i] = 0; + state->max_gain = 0; + state->cb6 = 0; + state->cb7 = 0; } static int integrate(hdcd_state_t *state, int *flag, const int32_t *samples, int count, int stride) @@ -949,6 +977,7 @@ static int hdcd_envelope(int32_t *samples, int count, int stride, int gain, int int len = FFMIN(count, target_gain - gain); /* attenuate slowly */ for (i = 0; i < len; i++) { + ++gain; APPLY_GAIN(*samples, gain); samples += stride; } @@ -982,6 +1011,18 @@ static int hdcd_envelope(int32_t *samples, int count, int stride, int gain, int return gain; } +/* update the user info/flags */ +static void hdcd_update_info(hdcd_state_t *state) +{ + if (state->control & 16) state->count_peak_extend++; + if (state->control & 32) state->count_transient_filter++; + state->gain_counts[state->control & 15]++; + state->max_gain = FFMAX(state->max_gain, (state->control & 15)); + + if (state->control & 64) state->cb6++; + if (state->control & 128) state->cb7++; +} + static void hdcd_process(hdcd_state_t *state, int32_t *samples, int count, int stride) { int32_t *samples_end = samples + count * stride; @@ -990,6 +1031,8 @@ static void hdcd_process(hdcd_state_t *state, int32_t *samples, int count, int s int target_gain = (state->control & 15) << 7; int lead = 0; + hdcd_update_info(state); + while (count > lead) { int envelope_run; int run; @@ -1006,6 +1049,7 @@ static void hdcd_process(hdcd_state_t *state, int32_t *samples, int count, int s lead = run - envelope_run; peak_extend = (state->control & 16); target_gain = (state->control & 15) << 7; + hdcd_update_info(state); } if (lead > 0) { av_assert0(samples + lead * stride <= samples_end); @@ -1015,6 +1059,10 @@ static void hdcd_process(hdcd_state_t *state, int32_t *samples, int count, int s state->running_gain = gain; } +/* convert to float from 4-bit (3.1) fixed-point + * the always-negative value is stored positive, so make it negative */ +#define GAINTOFLOAT(g) (g) ? -(float)(g>>1) - ((g & 1) ? 0.5 : 0.0) : 0.0 + static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; @@ -1042,6 +1090,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) for (c = 0; c < inlink->channels; c++) { hdcd_state_t *state = &s->state[c]; hdcd_process(state, out_data + c, in->nb_samples, out->channels); + + s->uses_peak_extend |= !!state->count_peak_extend; + s->uses_transient_filter |= !!state->count_transient_filter; + s->max_gain_adjustment = FFMIN(s->max_gain_adjustment, GAINTOFLOAT(state->max_gain)); + s->hdcd_detected |= state->code_counterC || state->code_counterB || state->code_counterA; } av_frame_free(&in); @@ -1097,13 +1150,28 @@ static int query_formats(AVFilterContext *ctx) static av_cold void uninit(AVFilterContext *ctx) { HDCDContext *s = ctx->priv; - int i; + int i, j; + /* dump the state for each channel for AV_LOG_VERBOSE */ for (i = 0; i < 2; i++) { hdcd_state_t *state = &s->state[i]; av_log(ctx, AV_LOG_VERBOSE, "Channel %d: counter A: %d, B: %d, C: %d\n", i, state->code_counterA, state->code_counterB, state->code_counterC); + av_log(ctx, AV_LOG_VERBOSE, "Channel %d: c(pe): %d, c(tf): %d, cb6: %d, cb7: %d\n", i, + state->count_peak_extend, state->count_transient_filter, state->cb6, state->cb7); + for (j = 0; j <= state->max_gain; j++) { + av_log(ctx, AV_LOG_VERBOSE, "Channel %d: tg %0.1f - %d\n", i, GAINTOFLOAT(j), state->gain_counts[j]); + } } + + /* log the HDCD decode information */ + av_log(ctx, AV_LOG_INFO, + "HDCD detected: %s, peak_extend: %s, max_gain_adj: %0.1f dB, transient_filter: %s\n", + (s->hdcd_detected) ? "yes" : "no", + (s->uses_peak_extend) ? "enabled" : "never enabled", + s->max_gain_adjustment, + (s->uses_transient_filter) ? "detected" : "not detected" + ); } static av_cold int init(AVFilterContext *ctx) @@ -1112,6 +1180,8 @@ static av_cold int init(AVFilterContext *ctx) HDCDContext *s = ctx->priv; int c; + s->max_gain_adjustment = 0.0; + for (c = 0; c < 2; c++) { hdcd_reset(&s->state[c], 44100); } @@ -1138,7 +1208,7 @@ static const AVFilterPad avfilter_af_hdcd_outputs[] = { AVFilter ff_af_hdcd = { .name = "hdcd", - .description = NULL_IF_CONFIG_SMALL("Apply high definition audio cd decoding."), + .description = NULL_IF_CONFIG_SMALL("Apply High Definition Compatible Digital (HDCD) decoding."), .priv_size = sizeof(HDCDContext), .priv_class = &hdcd_class, .init = init, -- cgit v1.2.3