+static void hdcd_process_stereo(HDCDContext *ctx, int32_t *samples, int count)
+{
+ const int stride = 2;
+ int32_t *samples_end = samples + count * stride;
+ int gain[2] = {ctx->state[0].running_gain, ctx->state[1].running_gain};
+ int peak_extend[2];
+ int lead = 0;
+ int ctlret;
+
+ if (ctx->analyze_mode) {
+ hdcd_analyze_prepare(&ctx->state[0], samples, count, stride);
+ hdcd_analyze_prepare(&ctx->state[1], samples + 1, count, stride);
+ }
+
+ ctlret = hdcd_control_stereo(ctx, &peak_extend[0], &peak_extend[1]);
+ while (count > lead) {
+ int envelope_run, run;
+
+ av_assert0(samples + lead * stride + stride * (count - lead) <= samples_end);
+ run = hdcd_scan_stereo(ctx, samples + lead * stride, count - lead) + lead;
+ envelope_run = run - 1;
+
+ av_assert0(samples + envelope_run * stride <= samples_end);
+
+ if (ctx->analyze_mode) {
+ gain[0] = hdcd_analyze(samples, envelope_run, stride, gain[0], ctx->val_target_gain, peak_extend[0],
+ ctx->analyze_mode,
+ ctx->state[0].sustain,
+ (ctlret == HDCD_TG_MISMATCH) );
+ gain[1] = hdcd_analyze(samples + 1, envelope_run, stride, gain[1], ctx->val_target_gain, peak_extend[1],
+ ctx->analyze_mode,
+ 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]);
+ }
+
+ samples += envelope_run * stride;
+ count -= envelope_run;
+ lead = run - envelope_run;
+
+ ctlret = hdcd_control_stereo(ctx, &peak_extend[0], &peak_extend[1]);
+ }
+ if (lead > 0) {
+ av_assert0(samples + lead * stride <= samples_end);
+ if (ctx->analyze_mode) {
+ gain[0] = hdcd_analyze(samples, lead, stride, gain[0], ctx->val_target_gain, peak_extend[0],
+ ctx->analyze_mode,
+ ctx->state[0].sustain,
+ (ctlret == HDCD_TG_MISMATCH) );
+ gain[1] = hdcd_analyze(samples + 1, lead, stride, gain[1], ctx->val_target_gain, peak_extend[1],
+ ctx->analyze_mode,
+ 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]);
+ }
+ }
+
+ ctx->state[0].running_gain = gain[0];
+ ctx->state[1].running_gain = gain[1];
+}
+
+static void hdcd_detect_reset(hdcd_detection_data *detect) {
+ detect->hdcd_detected = HDCD_NONE;
+ detect->packet_type = HDCD_PVER_NONE;
+ detect->total_packets = 0;
+ detect->errors = 0;
+ detect->peak_extend = HDCD_PE_NEVER;
+ detect->uses_transient_filter = 0;
+ detect->max_gain_adjustment = 0.0;
+ detect->cdt_expirations = -1;
+ detect->_active_count = 0;
+}
+
+static void hdcd_detect_start(hdcd_detection_data *detect) {
+ detect->errors = 0; /* re-sum every pass */
+ detect->total_packets = 0;
+ detect->_active_count = 0; /* will need to match channels at hdcd_detect_end() */
+ detect->cdt_expirations = -1;
+}
+
+static void hdcd_detect_onech(hdcd_state *state, hdcd_detection_data *detect) {
+ hdcd_pe pe = HDCD_PE_NEVER;
+ detect->uses_transient_filter |= !!(state->count_transient_filter);
+ detect->total_packets += state->code_counterA + state->code_counterB;
+ if (state->code_counterA) detect->packet_type |= HDCD_PVER_A;
+ if (state->code_counterB) detect->packet_type |= HDCD_PVER_B;
+ if (state->count_peak_extend) {
+ /* if every valid packet has used PE, call it permanent */
+ if (state->count_peak_extend == state->code_counterA + state->code_counterB)
+ pe = HDCD_PE_PERMANENT;
+ else
+ pe = HDCD_PE_INTERMITTENT;
+ if (detect->peak_extend != HDCD_PE_INTERMITTENT)
+ detect->peak_extend = pe;
+ }
+ detect->max_gain_adjustment = FFMIN(detect->max_gain_adjustment, GAINTOFLOAT(state->max_gain));
+ detect->errors += state->code_counterA_almost
+ + state->code_counterB_checkfails
+ + state->code_counterC_unmatched;
+ if (state->sustain) detect->_active_count++;
+ if (state->count_sustain_expired >= 0) {
+ if (detect->cdt_expirations == -1) detect->cdt_expirations = 0;
+ detect->cdt_expirations += state->count_sustain_expired;
+ }
+}
+
+static void hdcd_detect_end(hdcd_detection_data *detect, int channels) {
+ /* HDCD is detected if a valid packet is active in all
+ * channels at the same time. */
+ if (detect->_active_count == channels) {
+ if (detect->max_gain_adjustment || detect->peak_extend)
+ detect->hdcd_detected = HDCD_EFFECTUAL;
+ else
+ detect->hdcd_detected = HDCD_NO_EFFECT;
+ }
+}