But at expense of additional delay.
Showing the changes to libavfilter (1 of 2 files). Show all
Unified · Split
| @@ -21,6 +21,7 @@ · 20 unmodified lines | ||
| 21 | 21 | #include "libavutil/opt.h" |
| 22 | 22 | #include "avfilter.h" |
| 23 | 23 | #include "audio.h" |
| 24 | +#include "filters.h" | |
| 24 | 25 | #include "formats.h" |
| 25 | 26 | |
| 26 | 27 | typedef struct CrossfeedContext { |
| @@ -31,11 +32,16 @@ typedef struct CrossfeedContext { · 4 unmodified lines | ||
| 31 | 32 | double slope; |
| 32 | 33 | double level_in; |
| 33 | 34 | double level_out; |
| 35 | + int block_samples; | |
| 36 | + int block_size; | |
| 34 | 37 | |
| 35 | 38 | double a0, a1, a2; |
| 36 | 39 | double b0, b1, b2; |
| 37 | 40 | |
| 38 | 41 | double w1, w2; |
| 42 | + | |
| 43 | + double *mid; | |
| 44 | + double *side[3]; | |
| 39 | 45 | } CrossfeedContext; |
| 40 | 46 | |
| 41 | 47 | static int query_formats(AVFilterContext *ctx) |
| @@ -77,9 +83,49 @@ static int config_input(AVFilterLink *inlink) · 35 unmodified lines | ||
| 77 | 83 | s->b1 /= s->a0; |
| 78 | 84 | s->b2 /= s->a0; |
| 79 | 85 | |
| 86 | + if (s->block_samples == 0 && s->block_size > 0) { | |
| 87 | + s->block_samples = s->block_size; | |
| 88 | + s->mid = av_calloc(s->block_samples * 2, sizeof(*s->mid)); | |
| 89 | + for (int i = 0; i < 3; i++) { | |
| 90 | + s->side[i] = av_calloc(s->block_samples * 2, sizeof(*s->side[0])); | |
| 91 | + if (!s->side[i]) | |
| 92 | + return AVERROR(ENOMEM); | |
| 93 | + } | |
| 94 | + } | |
| 95 | + | |
| 80 | 96 | return 0; |
| 81 | 97 | } |
| 82 | 98 | |
| 99 | +static void reverse_samples(double *dst, const double *src, | |
| 100 | + int nb_samples) | |
| 101 | +{ | |
| 102 | + for (int i = 0, j = nb_samples - 1; i < nb_samples; i++, j--) | |
| 103 | + dst[i] = src[j]; | |
| 104 | +} | |
| 105 | + | |
| 106 | +static void filter_samples(double *dst, const double *src, | |
| 107 | + int nb_samples, | |
| 108 | + double b0, double b1, double b2, | |
| 109 | + double a1, double a2, | |
| 110 | + double *sw1, double *sw2) | |
| 111 | +{ | |
| 112 | + double w1 = *sw1; | |
| 113 | + double w2 = *sw2; | |
| 114 | + | |
| 115 | + for (int n = 0; n < nb_samples; n++) { | |
| 116 | + double side = src[n]; | |
| 117 | + double oside = side * b0 + w1; | |
| 118 | + | |
| 119 | + w1 = b1 * side + w2 + a1 * oside; | |
| 120 | + w2 = b2 * side + a2 * oside; | |
| 121 | + | |
| 122 | + dst[n] = oside; | |
| 123 | + } | |
| 124 | + | |
| 125 | + *sw1 = w1; | |
| 126 | + *sw2 = w2; | |
| 127 | +} | |
| 128 | + | |
| 83 | 129 | static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
| 84 | 130 | { |
| 85 | 131 | AVFilterContext *ctx = inlink->dst; |
| @@ -95,7 +141,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) · 9 unmodified lines | ||
| 95 | 141 | const double a2 = -s->a2; |
| 96 | 142 | AVFrame *out; |
| 97 | 143 | double *dst; |
| 98 | − int n; | |
| 99 | 144 | |
| 100 | 145 | if (av_frame_is_writable(in)) { |
| 101 | 146 | out = in; |
| @@ -109,21 +154,79 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) · 7 unmodified lines | ||
| 109 | 154 | } |
| 110 | 155 | dst = (double *)out->data[0]; |
| 111 | 156 | |
| 112 | − for (n = 0; n < out->nb_samples; n++, src += 2, dst += 2) { | |
| 113 | − double mid = (src[0] + src[1]) * level_in * .5; | |
| 114 | − double side = (src[0] - src[1]) * level_in * .5; | |
| 115 | − double oside = side * b0 + s->w1; | |
| 157 | + if (s->block_samples == 0) { | |
| 158 | + double w1 = s->w1; | |
| 159 | + double w2 = s->w2; | |
| 160 | + | |
| 161 | + for (int n = 0; n < out->nb_samples; n++, src += 2, dst += 2) { | |
| 162 | + double mid = (src[0] + src[1]) * level_in * .5; | |
| 163 | + double side = (src[0] - src[1]) * level_in * .5; | |
| 164 | + double oside = side * b0 + w1; | |
| 165 | + | |
| 166 | + w1 = b1 * side + w2 + a1 * oside; | |
| 167 | + w2 = b2 * side + a2 * oside; | |
| 168 | + | |
| 169 | + if (ctx->is_disabled) { | |
| 170 | + dst[0] = src[0]; | |
| 171 | + dst[1] = src[1]; | |
| 172 | + } else { | |
| 173 | + dst[0] = (mid + oside) * level_out; | |
| 174 | + dst[1] = (mid - oside) * level_out; | |
| 175 | + } | |
| 176 | + } | |
| 177 | + | |
| 178 | + s->w1 = w1; | |
| 179 | + s->w2 = w2; | |
| 180 | + } else { | |
| 181 | + double *mdst = s->mid + s->block_samples; | |
| 182 | + double *sdst = s->side[0] + s->block_samples; | |
| 183 | + double *ssrc = s->side[0]; | |
| 184 | + double *msrc = s->mid; | |
| 185 | + double w1 = s->w1; | |
| 186 | + double w2 = s->w2; | |
| 187 | + | |
| 188 | + for (int n = 0; n < out->nb_samples; n++, src += 2) { | |
| 189 | + mdst[n] = (src[0] + src[1]) * level_in * .5; | |
| 190 | + sdst[n] = (src[0] - src[1]) * level_in * .5; | |
| 191 | + } | |
| 192 | + | |
| 193 | + sdst = s->side[1]; | |
| 194 | + filter_samples(sdst, ssrc, s->block_samples, | |
| 195 | + b0, b1, b2, a1, a2, | |
| 196 | + &w1, &w2); | |
| 197 | + s->w1 = w1; | |
| 198 | + s->w2 = w2; | |
| 116 | 199 | |
| 117 | − s->w1 = b1 * side + s->w2 + a1 * oside; | |
| 118 | − s->w2 = b2 * side + a2 * oside; | |
| 200 | + ssrc = s->side[0] + s->block_samples; | |
| 201 | + sdst = s->side[1] + s->block_samples; | |
| 202 | + filter_samples(sdst, ssrc, s->block_samples, | |
| 203 | + b0, b1, b2, a1, a2, | |
| 204 | + &w1, &w2); | |
| 119 | 205 | |
| 120 | − if (ctx->is_disabled) { | |
| 121 | − dst[0] = src[0]; | |
| 122 | − dst[1] = src[1]; | |
| 123 | − } else { | |
| 124 | − dst[0] = (mid + oside) * level_out; | |
| 125 | − dst[1] = (mid - oside) * level_out; | |
| 206 | + reverse_samples(s->side[2], s->side[1], s->block_samples * 2); | |
| 207 | + w1 = w2 = 0.; | |
| 208 | + filter_samples(s->side[2], s->side[2], s->block_samples * 2, | |
| 209 | + b0, b1, b2, a1, a2, | |
| 210 | + &w1, &w2); | |
| 211 | + | |
| 212 | + reverse_samples(s->side[1], s->side[2] + s->block_samples, s->block_samples); | |
| 213 | + | |
| 214 | + src = (const double *)in->data[0]; | |
| 215 | + ssrc = s->side[1]; | |
| 216 | + for (int n = 0; n < out->nb_samples; n++, src += 2, dst += 2) { | |
| 217 | + if (ctx->is_disabled) { | |
| 218 | + dst[0] = src[0]; | |
| 219 | + dst[1] = src[1]; | |
| 220 | + } else { | |
| 221 | + dst[0] = (msrc[n] + ssrc[n]) * level_out; | |
| 222 | + dst[1] = (msrc[n] - ssrc[n]) * level_out; | |
| 223 | + } | |
| 126 | 224 | } |
| 225 | + | |
| 226 | + memmove(s->mid, s->mid + s->block_samples, | |
| 227 | + s->block_samples * sizeof(*s->mid)); | |
| 228 | + memmove(s->side[0], s->side[0] + s->block_samples, | |
| 229 | + s->block_samples * sizeof(*s->side[0])); | |
| 127 | 230 | } |
| 128 | 231 | |
| 129 | 232 | if (out != in) |
| @@ -131,6 +234,37 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) · 1 unmodified line | ||
| 131 | 234 | return ff_filter_frame(outlink, out); |
| 132 | 235 | } |
| 133 | 236 | |
| 237 | +static int activate(AVFilterContext *ctx) | |
| 238 | +{ | |
| 239 | + AVFilterLink *inlink = ctx->inputs[0]; | |
| 240 | + AVFilterLink *outlink = ctx->outputs[0]; | |
| 241 | + CrossfeedContext *s = ctx->priv; | |
| 242 | + AVFrame *in = NULL; | |
| 243 | + int ret; | |
| 244 | + | |
| 245 | + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); | |
| 246 | + | |
| 247 | + if (s->block_samples > 0) { | |
| 248 | + ret = ff_inlink_consume_samples(inlink, s->block_samples, s->block_samples, &in); | |
| 249 | + } else { | |
| 250 | + ret = ff_inlink_consume_frame(inlink, &in); | |
| 251 | + } | |
| 252 | + if (ret < 0) | |
| 253 | + return ret; | |
| 254 | + if (ret > 0) | |
| 255 | + return filter_frame(inlink, in); | |
| 256 | + | |
| 257 | + if (s->block_samples > 0 && ff_inlink_queued_samples(inlink) >= s->block_samples) { | |
| 258 | + ff_filter_set_ready(ctx, 10); | |
| 259 | + return 0; | |
| 260 | + } | |
| 261 | + | |
| 262 | + FF_FILTER_FORWARD_STATUS(inlink, outlink); | |
| 263 | + FF_FILTER_FORWARD_WANTED(outlink, inlink); | |
| 264 | + | |
| 265 | + return FFERROR_NOT_READY; | |
| 266 | +} | |
| 267 | + | |
| 134 | 268 | static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, |
| 135 | 269 | char *res, int res_len, int flags) |
| 136 | 270 | { |
| @@ -143,8 +277,18 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar · 6 unmodified lines | ||
| 143 | 277 | return config_input(ctx->inputs[0]); |
| 144 | 278 | } |
| 145 | 279 | |
| 280 | +static av_cold void uninit(AVFilterContext *ctx) | |
| 281 | +{ | |
| 282 | + CrossfeedContext *s = ctx->priv; | |
| 283 | + | |
| 284 | + av_freep(&s->mid); | |
| 285 | + for (int i = 0; i < 3; i++) | |
| 286 | + av_freep(&s->side[i]); | |
| 287 | +} | |
| 288 | + | |
| 146 | 289 | #define OFFSET(x) offsetof(CrossfeedContext, x) |
| 147 | 290 | #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM |
| 291 | +#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM | |
| 148 | 292 | |
| 149 | 293 | static const AVOption crossfeed_options[] = { |
| 150 | 294 | { "strength", "set crossfeed strength", OFFSET(strength), AV_OPT_TYPE_DOUBLE, {.dbl=.2}, 0, 1, FLAGS }, |
| @@ -152,6 +296,7 @@ static const AVOption crossfeed_options[] = { · 1 unmodified line | ||
| 152 | 296 | { "slope", "set curve slope", OFFSET(slope), AV_OPT_TYPE_DOUBLE, {.dbl=.5}, .01, 1, FLAGS }, |
| 153 | 297 | { "level_in", "set level in", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=.9}, 0, 1, FLAGS }, |
| 154 | 298 | { "level_out", "set level out", OFFSET(level_out), AV_OPT_TYPE_DOUBLE, {.dbl=1.}, 0, 1, FLAGS }, |
| 299 | + { "block_size", "set the block size", OFFSET(block_size),AV_OPT_TYPE_INT, {.i64=0}, 0, 32768, AF }, | |
| 155 | 300 | { NULL } |
| 156 | 301 | }; |
| 157 | 302 | |
| @@ -161,7 +306,6 @@ static const AVFilterPad inputs[] = { · 3 unmodified lines | ||
| 161 | 306 | { |
| 162 | 307 | .name = "default", |
| 163 | 308 | .type = AVMEDIA_TYPE_AUDIO, |
| 164 | − .filter_frame = filter_frame, | |
| 165 | 309 | .config_props = config_input, |
| 166 | 310 | }, |
| 167 | 311 | }; |
| @@ -178,6 +322,8 @@ const AVFilter ff_af_crossfeed = { · 10 unmodified lines | ||
| 178 | 322 | .description = NULL_IF_CONFIG_SMALL("Apply headphone crossfeed filter."), |
| 179 | 323 | .priv_size = sizeof(CrossfeedContext), |
| 180 | 324 | .priv_class = &crossfeed_class, |
| 325 | + .activate = activate, | |
| 326 | + .uninit = uninit, | |
| 181 | 327 | FILTER_INPUTS(inputs), |
| 182 | 328 | FILTER_OUTPUTS(outputs), |
| 183 | 329 | FILTER_QUERY_FUNC(query_formats), |