[FFmpeg-devel] [PATCH 5/5] af_hdcd: add experimental 20 and 24-bit decoding support

Burt P pburt0 at gmail.com
Sun Oct 2 10:46:33 EEST 2016


As in this libhdcd commit: [0]
I don't have any legitimate 20 or 24-bit HDCD to test. It is known
that the PM Model Two would insert packets into 20 and 24-bit output,
but I have no idea what differences in behavior existed when decoding
20 or 24-bit. For now, as with 16-bit, PE (if enabled) will expand the
top 3dB into 9dB and LLE (gain adjust) will be applied if signaled.

Also,
As all known valid HDCD sample formats and sample rates are now handled
by the filter, remove the scan that "invades the privacy" of the filter graph
and turn off autoconvert by default as requested by Nicolas George. [1]

[0] https://github.com/bp0/libhdcd/commit/dc32b383efab5608820af4f641db292f9a776778
[1] http://ffmpeg.org/pipermail/ffmpeg-devel/2016-August/197571.html

Signed-off-by: Burt P <pburt0 at gmail.com>
---
 libavfilter/af_hdcd.c | 135 ++++++++++++++++++++++++++------------------------
 1 file changed, 69 insertions(+), 66 deletions(-)

diff --git a/libavfilter/af_hdcd.c b/libavfilter/af_hdcd.c
index b5aad85..91f84ae 100644
--- a/libavfilter/af_hdcd.c
+++ b/libavfilter/af_hdcd.c
@@ -964,6 +964,8 @@ typedef struct HDCDContext {
     int cdt_ms;               /**< code detect timer period in ms */
 
     int disable_autoconvert;  /**< disable any format conversion or resampling in the filter graph */
+
+    int bits_per_sample;      /**< bits per sample 16, 20, or 24 */
     /* end AVOption members */
 
     /** config_input() and config_output() scan links for any resampling
@@ -983,7 +985,7 @@ typedef struct HDCDContext {
 #define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
 static const AVOption hdcd_options[] = {
     { "disable_autoconvert", "Disable any format conversion or resampling in the filter graph.",
-        OFFSET(disable_autoconvert), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, A },
+        OFFSET(disable_autoconvert), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, A },
     { "process_stereo", "Process stereo channels together. Only apply target_gain when both channels match.",
         OFFSET(process_stereo), AV_OPT_TYPE_BOOL, { .i64 = HDCD_PROCESS_STEREO_DEFAULT }, 0, 1, A },
     { "cdt_ms", "Code detect timer period in ms.",
@@ -997,6 +999,11 @@ static const AVOption hdcd_options[] = {
         { "pe",  HDCD_ANA_PE_DESC,  0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_PE},  0, 0, A, "analyze_mode" },
         { "cdt", HDCD_ANA_CDT_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_CDT}, 0, 0, A, "analyze_mode" },
         { "tgm", HDCD_ANA_TGM_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_TGM}, 0, 0, A, "analyze_mode" },
+    { "bits_per_sample",  "Valid bits per sample (location of the true LSB).",
+        OFFSET(bits_per_sample), AV_OPT_TYPE_INT, { .i64=16 }, 16, 24, A, "bits_per_sample"},
+        { "16", "16-bit (in s32 or s16)", 0, AV_OPT_TYPE_CONST, {.i64=16}, 0, 0, A, "bits_per_sample" },
+        { "20", "20-bit (in s32)", 0, AV_OPT_TYPE_CONST, {.i64=20}, 0, 0, A, "bits_per_sample" },
+        { "24", "24-bit (in s32)", 0, AV_OPT_TYPE_CONST, {.i64=24},  0, 0, A, "bits_per_sample" },
     {NULL}
 };
 
@@ -1253,29 +1260,34 @@ static int hdcd_analyze(int32_t *samples, int count, int stride, int gain, int t
 }
 
 /** apply HDCD decoding parameters to a series of samples */
-static int hdcd_envelope(int32_t *samples, int count, int stride, int gain, int target_gain, int extend)
+static int hdcd_envelope(int32_t *samples, int count, int stride, int vbits, int gain, int target_gain, int extend)
 {
     static const int max_asample = sizeof(peaktab) / sizeof(peaktab[0]) - 1;
     int32_t *samples_end = samples + stride * count;
     int i;
 
+    int pe_level = PEAK_EXT_LEVEL, shft = 15;
+    if (vbits != 16) {
+        pe_level = (1 << (vbits - 1)) - (0x8000 - PEAK_EXT_LEVEL);
+        shft = 32 - vbits - 1;
+    }
     av_assert0(PEAK_EXT_LEVEL + max_asample == 0x8000);
 
     if (extend) {
         for (i = 0; i < count; i++) {
             int32_t sample = samples[i * stride];
-            int32_t asample = abs(sample) - PEAK_EXT_LEVEL;
+            int32_t asample = abs(sample) - pe_level;
             if (asample >= 0) {
                 av_assert0(asample <= max_asample);
                 sample = sample >= 0 ? peaktab[asample] : -peaktab[asample];
             } else
-                sample <<= 15;
+                sample <<= shft;
 
             samples[i * stride] = sample;
         }
     } else {
         for (i = 0; i < count; i++)
-            samples[i * stride] <<= 15;
+            samples[i * stride] <<= shft;
     }
 
     if (gain <= target_gain) {
@@ -1370,7 +1382,7 @@ static void hdcd_process(HDCDContext *ctx, hdcd_state *state, int32_t *samples,
         if (ctx->analyze_mode)
             gain = hdcd_analyze(samples, envelope_run, stride, gain, target_gain, peak_extend, ctx->analyze_mode, state->sustain, -1);
         else
-            gain = hdcd_envelope(samples, envelope_run, stride, gain, target_gain, peak_extend);
+            gain = hdcd_envelope(samples, envelope_run, stride, ctx->bits_per_sample, gain, target_gain, peak_extend);
 
         samples += envelope_run * stride;
         count -= envelope_run;
@@ -1382,7 +1394,7 @@ static void hdcd_process(HDCDContext *ctx, hdcd_state *state, int32_t *samples,
         if (ctx->analyze_mode)
             gain = hdcd_analyze(samples, lead, stride, gain, target_gain, peak_extend, ctx->analyze_mode, state->sustain, -1);
         else
-            gain = hdcd_envelope(samples, lead, stride, gain, target_gain, peak_extend);
+            gain = hdcd_envelope(samples, lead, stride, ctx->bits_per_sample, gain, target_gain, peak_extend);
     }
 
     state->running_gain = gain;
@@ -1422,8 +1434,8 @@ static void hdcd_process_stereo(HDCDContext *ctx, int32_t *samples, int count)
                 ctx->state[1].sustain,
                 (ctlret == HDCD_TG_MISMATCH) );
         } else {
-            gain[0] = hdcd_envelope(samples, envelope_run, stride, gain[0], ctx->val_target_gain, peak_extend[0]);
-            gain[1] = hdcd_envelope(samples + 1, envelope_run, stride, gain[1], ctx->val_target_gain, peak_extend[1]);
+            gain[0] = hdcd_envelope(samples, envelope_run, stride, ctx->bits_per_sample, gain[0], ctx->val_target_gain, peak_extend[0]);
+            gain[1] = hdcd_envelope(samples + 1, envelope_run, stride, ctx->bits_per_sample, gain[1], ctx->val_target_gain, peak_extend[1]);
         }
 
         samples += envelope_run * stride;
@@ -1444,8 +1456,8 @@ static void hdcd_process_stereo(HDCDContext *ctx, int32_t *samples, int count)
                 ctx->state[1].sustain,
                 (ctlret == HDCD_TG_MISMATCH) );
         } else {
-            gain[0] = hdcd_envelope(samples, lead, stride, gain[0], ctx->val_target_gain, peak_extend[0]);
-            gain[1] = hdcd_envelope(samples + 1, lead, stride, gain[1], ctx->val_target_gain, peak_extend[1]);
+            gain[0] = hdcd_envelope(samples, lead, stride, ctx->bits_per_sample, gain[0], ctx->val_target_gain, peak_extend[0]);
+            gain[1] = hdcd_envelope(samples + 1, lead, stride, ctx->bits_per_sample, gain[1], ctx->val_target_gain, peak_extend[1]);
         }
     }
 
@@ -1516,8 +1528,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     AVFilterLink *outlink = ctx->outputs[0];
     AVFrame *out;
     const int16_t *in_data;
+    const int32_t *in_data32;
     int32_t *out_data;
     int n, c, result;
+    int a = 32 - s->bits_per_sample;
 
     out = ff_get_audio_buffer(outlink, in->nb_samples);
     if (!out) {
@@ -1533,16 +1547,32 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     out->format = outlink->format; // is this needed?
 
     out_data = (int32_t*)out->data[0];
-    if (inlink->format == AV_SAMPLE_FMT_S16P) {
-        for (n = 0; n < in->nb_samples; n++)
-            for (c = 0; c < in->channels; c++) {
-                in_data = (int16_t*)in->extended_data[c];
-                out_data[(n * in->channels) + c] = in_data[n];
-            }
-    } else {
-        in_data  = (int16_t*)in->data[0];
-        for (n = 0; n < in->nb_samples * in->channels; n++)
-            out_data[n] = in_data[n];
+    switch (inlink->format) {
+        case AV_SAMPLE_FMT_S16P:
+            for (n = 0; n < in->nb_samples; n++)
+                for (c = 0; c < in->channels; c++) {
+                    in_data = (int16_t*)in->extended_data[c];
+                    out_data[(n * in->channels) + c] = in_data[n];
+                }
+            break;
+        case AV_SAMPLE_FMT_S16:
+            in_data  = (int16_t*)in->data[0];
+            for (n = 0; n < in->nb_samples * in->channels; n++)
+                out_data[n] = in_data[n];
+            break;
+
+        case AV_SAMPLE_FMT_S32P:
+            for (n = 0; n < in->nb_samples; n++)
+                for (c = 0; c < in->channels; c++) {
+                    in_data32 = (int32_t*)in->extended_data[c];
+                    out_data[(n * in->channels) + c] = in_data32[n] >> a;
+                }
+            break;
+        case AV_SAMPLE_FMT_S32:
+            in_data32  = (int32_t*)in->data[0];
+            for (n = 0; n < in->nb_samples * in->channels; n++)
+                out_data[n] = in_data32[n] >> a;
+            break;
     }
 
     if (s->process_stereo) {
@@ -1583,6 +1613,8 @@ static int query_formats(AVFilterContext *ctx)
     static const enum AVSampleFormat sample_fmts_in[] = {
         AV_SAMPLE_FMT_S16,
         AV_SAMPLE_FMT_S16P,
+        AV_SAMPLE_FMT_S32,
+        AV_SAMPLE_FMT_S32P,
         AV_SAMPLE_FMT_NONE
     };
     static const enum AVSampleFormat sample_fmts_out[] = {
@@ -1678,12 +1710,27 @@ static av_cold int init(AVFilterContext *ctx)
 static int config_input(AVFilterLink *inlink) {
     AVFilterContext *ctx = inlink->dst;
     HDCDContext *s = ctx->priv;
-    AVFilterLink *lk;
     int c;
 
     av_log(ctx, AV_LOG_VERBOSE, "Auto-convert: %s\n",
         (ctx->graph->disable_auto_convert) ? "disabled" : "enabled");
 
+    if ((inlink->format == AV_SAMPLE_FMT_S16 ||
+         inlink->format == AV_SAMPLE_FMT_S16P) &&
+         s->bits_per_sample != 16) {
+            av_log(ctx, AV_LOG_WARNING, "bits_per_sample %d does not fit into sample format %s, falling back to 16\n",
+                s->bits_per_sample, av_get_sample_fmt_name(inlink->format) );
+        s->bits_per_sample = 16;
+    } else {
+        av_log(ctx, AV_LOG_VERBOSE, "Looking for %d-bit HDCD in sample format %s\n",
+            s->bits_per_sample, av_get_sample_fmt_name(inlink->format) );
+    }
+
+    if (s->bits_per_sample != 16)
+        av_log(ctx, AV_LOG_WARNING, "20 and 24-bit HDCD decoding is experimental\n");
+    if (inlink->sample_rate != 44100)
+        av_log(ctx, AV_LOG_WARNING, "HDCD decoding for sample rates other than 44100 is experimental\n");
+
     hdcd_detect_reset(&s->detect);
     for (c = 0; c < HDCD_MAX_CHANNELS; c++) {
         hdcd_reset(&s->state[c], inlink->sample_rate, s->cdt_ms);
@@ -1703,24 +1750,6 @@ static int config_input(AVFilterLink *inlink) {
     av_log(ctx, AV_LOG_VERBOSE, "Analyze mode: [%d] %s\n",
         s->analyze_mode, ana_mode_str[s->analyze_mode] );
 
-    lk = inlink;
-    while(lk != NULL) {
-        AVFilterContext *nextf = lk->src;
-        if (lk->type == AVMEDIA_TYPE_AUDIO) {
-            int sfok = (lk->format == AV_SAMPLE_FMT_S16 ||
-                        lk->format == AV_SAMPLE_FMT_S16P);
-            if ( !sfok || lk->sample_rate != 44100) {
-                av_log(ctx, AV_LOG_WARNING, "An input format is %s@%dHz at %s. It will truncated/resampled to s16 at 44100Hz.\n",
-                    av_get_sample_fmt_name(lk->format), lk->sample_rate,
-                    (nextf->name) ? nextf->name : "<unknown>"
-                    );
-                s->bad_config = 1;
-                break;
-            }
-        }
-        lk = (nextf->inputs) ? nextf->inputs[0] : NULL;
-    }
-    /* more warning will appear after config_output() */
     return 0;
 }
 
@@ -1734,36 +1763,10 @@ static const AVFilterPad avfilter_af_hdcd_inputs[] = {
     { NULL }
 };
 
-static int config_output(AVFilterLink *outlink) {
-    static const char hdcd_baduse[] =
-        "The HDCD filter is unlikely to produce a desirable result in this context.";
-    AVFilterContext *ctx = outlink->src;
-    HDCDContext *s = ctx->priv;
-    AVFilterLink *lk = outlink;
-    while(lk != NULL) {
-        AVFilterContext *nextf = lk->dst;
-        if (lk->type == AVMEDIA_TYPE_AUDIO) {
-            if (lk->format == AV_SAMPLE_FMT_S16 || lk->format == AV_SAMPLE_FMT_U8) {
-                av_log(ctx, AV_LOG_WARNING, "s24 output is being truncated to %s at %s.\n",
-                    av_get_sample_fmt_name(lk->format),
-                    (nextf->name) ? nextf->name : "<unknown>"
-                    );
-                s->bad_config = 1;
-                break;
-            }
-        }
-        lk = (nextf->outputs) ? nextf->outputs[0] : NULL;
-    }
-    if (s->bad_config)
-        av_log(ctx, AV_LOG_WARNING, "%s\n", hdcd_baduse);
-    return 0;
-}
-
 static const AVFilterPad avfilter_af_hdcd_outputs[] = {
     {
         .name = "default",
         .type = AVMEDIA_TYPE_AUDIO,
-        .config_props = config_output,
     },
     { NULL }
 };
-- 
2.7.4



More information about the ffmpeg-devel mailing list