libavfilter/af_hdcd.c: Collect HDCD stats and report
[ffmpeg.git] / libavfilter / af_hdcd.c
index 16bdcb0..89012eb 100644 (file)
@@ -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,