| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2002 Anders Johansson <ajh@atri.curtin.edu.au> | ||
| 3 | * Copyright (c) 2011 Clément Bœsch <u pkh me> | ||
| 4 | * Copyright (c) 2011 Nicolas George <nicolas.george@normalesup.org> | ||
| 5 | * | ||
| 6 | * This file is part of FFmpeg. | ||
| 7 | * | ||
| 8 | * FFmpeg is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU Lesser General Public | ||
| 10 | * License as published by the Free Software Foundation; either | ||
| 11 | * version 2.1 of the License, or (at your option) any later version. | ||
| 12 | * | ||
| 13 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU Lesser General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU Lesser General Public | ||
| 19 | * License along with FFmpeg; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 21 | */ | ||
| 22 | |||
| 23 | /** | ||
| 24 | * @file | ||
| 25 | * Audio panning filter (channels mixing) | ||
| 26 | * Original code written by Anders Johansson for MPlayer, | ||
| 27 | * reimplemented for FFmpeg. | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include <stdio.h> | ||
| 31 | #include "libavutil/avstring.h" | ||
| 32 | #include "libavutil/channel_layout.h" | ||
| 33 | #include "libavutil/mem.h" | ||
| 34 | #include "libavutil/opt.h" | ||
| 35 | #include "libswresample/swresample.h" | ||
| 36 | #include "audio.h" | ||
| 37 | #include "avfilter.h" | ||
| 38 | #include "filters.h" | ||
| 39 | #include "formats.h" | ||
| 40 | |||
| 41 | #define MAX_CHANNELS 64 | ||
| 42 | |||
| 43 | typedef struct PanContext { | ||
| 44 | const AVClass *class; | ||
| 45 | char *args; | ||
| 46 | AVChannelLayout out_channel_layout; | ||
| 47 | double gain[MAX_CHANNELS][MAX_CHANNELS]; | ||
| 48 | int64_t need_renorm; | ||
| 49 | int need_renumber; | ||
| 50 | int nb_output_channels; | ||
| 51 | |||
| 52 | int pure_gains; | ||
| 53 | /* channel mapping specific */ | ||
| 54 | int channel_map[MAX_CHANNELS]; | ||
| 55 | struct SwrContext *swr; | ||
| 56 | } PanContext; | ||
| 57 | |||
| 58 | 692 | static void skip_spaces(char **arg) | |
| 59 | { | ||
| 60 | 692 | int len = 0; | |
| 61 | |||
| 62 | 692 | sscanf(*arg, " %n", &len); | |
| 63 | 692 | *arg += len; | |
| 64 | 692 | } | |
| 65 | |||
| 66 | 346 | static int parse_channel_name(char **arg, int *rchannel, int *rnamed) | |
| 67 | { | ||
| 68 | char buf[8]; | ||
| 69 | 346 | int len, channel_id = 0; | |
| 70 | |||
| 71 | 346 | skip_spaces(arg); | |
| 72 | /* try to parse a channel name, e.g. "FL" */ | ||
| 73 |
2/2✓ Branch 0 taken 88 times.
✓ Branch 1 taken 258 times.
|
346 | if (sscanf(*arg, "%7[A-Z]%n", buf, &len) >= 1) { |
| 74 | 88 | channel_id = av_channel_from_string(buf); | |
| 75 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 88 times.
|
88 | if (channel_id < 0) |
| 76 | ✗ | return channel_id; | |
| 77 | |||
| 78 | 88 | *rchannel = channel_id; | |
| 79 | 88 | *rnamed = 1; | |
| 80 | 88 | *arg += len; | |
| 81 | 88 | return 0; | |
| 82 | } | ||
| 83 | /* try to parse a channel number, e.g. "c2" */ | ||
| 84 |
1/2✓ Branch 0 taken 258 times.
✗ Branch 1 not taken.
|
258 | if (sscanf(*arg, "c%d%n", &channel_id, &len) >= 1 && |
| 85 |
2/4✓ Branch 0 taken 258 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 258 times.
✗ Branch 3 not taken.
|
258 | channel_id >= 0 && channel_id < MAX_CHANNELS) { |
| 86 | 258 | *rchannel = channel_id; | |
| 87 | 258 | *rnamed = 0; | |
| 88 | 258 | *arg += len; | |
| 89 | 258 | return 0; | |
| 90 | } | ||
| 91 | ✗ | return AVERROR(EINVAL); | |
| 92 | } | ||
| 93 | |||
| 94 | 52 | static int are_gains_pure(const PanContext *pan) | |
| 95 | { | ||
| 96 | int i, j; | ||
| 97 | |||
| 98 |
2/2✓ Branch 0 taken 2320 times.
✓ Branch 1 taken 36 times.
|
2356 | for (i = 0; i < MAX_CHANNELS; i++) { |
| 99 | 2320 | int nb_gain = 0; | |
| 100 | |||
| 101 |
2/2✓ Branch 0 taken 147488 times.
✓ Branch 1 taken 2304 times.
|
149792 | for (j = 0; j < MAX_CHANNELS; j++) { |
| 102 | 147488 | double gain = pan->gain[i][j]; | |
| 103 | |||
| 104 | /* channel mapping is effective only if 0% or 100% of a channel is | ||
| 105 | * selected... */ | ||
| 106 |
4/4✓ Branch 0 taken 122 times.
✓ Branch 1 taken 147366 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 108 times.
|
147488 | if (gain != 0. && gain != 1.) |
| 107 | 14 | return 0; | |
| 108 | /* ...and if the output channel is only composed of one input */ | ||
| 109 |
4/4✓ Branch 0 taken 108 times.
✓ Branch 1 taken 147366 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 106 times.
|
147474 | if (gain && nb_gain++) |
| 110 | 2 | return 0; | |
| 111 | } | ||
| 112 | } | ||
| 113 | 36 | return 1; | |
| 114 | } | ||
| 115 | |||
| 116 | 52 | static av_cold int init(AVFilterContext *ctx) | |
| 117 | { | ||
| 118 | 52 | PanContext *const pan = ctx->priv; | |
| 119 | 52 | char *arg, *arg0, *tokenizer, *args = av_strdup(pan->args); | |
| 120 | 52 | int out_ch_id, in_ch_id, len, named, ret, sign = 1; | |
| 121 | 52 | int nb_in_channels[2] = { 0, 0 }; // number of unnamed and named input channels | |
| 122 | 52 | int used_out_ch[MAX_CHANNELS] = {0}; | |
| 123 | double gain; | ||
| 124 | |||
| 125 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | if (!pan->args) { |
| 126 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 127 | "pan filter needs a channel layout and a set " | ||
| 128 | "of channel definitions as parameter\n"); | ||
| 129 | ✗ | return AVERROR(EINVAL); | |
| 130 | } | ||
| 131 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | if (!args) |
| 132 | ✗ | return AVERROR(ENOMEM); | |
| 133 | 52 | arg = av_strtok(args, "|", &tokenizer); | |
| 134 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | if (!arg) { |
| 135 | ✗ | av_log(ctx, AV_LOG_ERROR, "Channel layout not specified\n"); | |
| 136 | ✗ | ret = AVERROR(EINVAL); | |
| 137 | ✗ | goto fail; | |
| 138 | } | ||
| 139 | 52 | ret = ff_parse_channel_layout(&pan->out_channel_layout, | |
| 140 | &pan->nb_output_channels, arg, ctx); | ||
| 141 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | if (ret < 0) |
| 142 | ✗ | goto fail; | |
| 143 | |||
| 144 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | if (pan->nb_output_channels > MAX_CHANNELS) { |
| 145 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 146 | "af_pan supports a maximum of %d channels. " | ||
| 147 | "Feel free to ask for a higher limit.\n", MAX_CHANNELS); | ||
| 148 | ✗ | ret = AVERROR_PATCHWELCOME; | |
| 149 | ✗ | goto fail; | |
| 150 | } | ||
| 151 | |||
| 152 | /* parse channel specifications */ | ||
| 153 |
2/2✓ Branch 1 taken 148 times.
✓ Branch 2 taken 52 times.
|
200 | while ((arg = arg0 = av_strtok(NULL, "|", &tokenizer))) { |
| 154 | 148 | int used_in_ch[MAX_CHANNELS] = {0}; | |
| 155 | /* channel name */ | ||
| 156 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 148 times.
|
148 | if (parse_channel_name(&arg, &out_ch_id, &named)) { |
| 157 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 158 | "Expected out channel name, got \"%.8s\"\n", arg); | ||
| 159 | ✗ | ret = AVERROR(EINVAL); | |
| 160 | ✗ | goto fail; | |
| 161 | } | ||
| 162 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 108 times.
|
148 | if (named) { |
| 163 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 40 times.
|
40 | if ((out_ch_id = av_channel_layout_index_from_channel(&pan->out_channel_layout, out_ch_id)) < 0) { |
| 164 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 165 | "Channel \"%.8s\" does not exist in the chosen layout\n", arg0); | ||
| 166 | ✗ | ret = AVERROR(EINVAL); | |
| 167 | ✗ | goto fail; | |
| 168 | } | ||
| 169 | } | ||
| 170 |
2/4✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 148 times.
|
148 | if (out_ch_id < 0 || out_ch_id >= pan->nb_output_channels) { |
| 171 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 172 | "Invalid out channel name \"%.8s\"\n", arg0); | ||
| 173 | ✗ | ret = AVERROR(EINVAL); | |
| 174 | ✗ | goto fail; | |
| 175 | } | ||
| 176 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 148 times.
|
148 | if (used_out_ch[out_ch_id]) { |
| 177 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 178 | "Can not reference out channel %d twice\n", out_ch_id); | ||
| 179 | ✗ | ret = AVERROR(EINVAL); | |
| 180 | ✗ | goto fail; | |
| 181 | } | ||
| 182 | 148 | used_out_ch[out_ch_id] = 1; | |
| 183 | 148 | skip_spaces(&arg); | |
| 184 |
2/2✓ Branch 0 taken 140 times.
✓ Branch 1 taken 8 times.
|
148 | if (*arg == '=') { |
| 185 | 140 | arg++; | |
| 186 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | } else if (*arg == '<') { |
| 187 | 8 | pan->need_renorm |= (int64_t)1 << out_ch_id; | |
| 188 | 8 | arg++; | |
| 189 | } else { | ||
| 190 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 191 | "Syntax error after channel name in \"%.8s\"\n", arg0); | ||
| 192 | ✗ | ret = AVERROR(EINVAL); | |
| 193 | ✗ | goto fail; | |
| 194 | } | ||
| 195 | /* gains */ | ||
| 196 | 148 | sign = 1; | |
| 197 | while (1) { | ||
| 198 | 198 | gain = 1; | |
| 199 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 162 times.
|
198 | if (sscanf(arg, "%lf%n *%n", &gain, &len, &len) >= 1) |
| 200 | 36 | arg += len; | |
| 201 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 198 times.
|
198 | if (parse_channel_name(&arg, &in_ch_id, &named)){ |
| 202 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 203 | "Expected in channel name, got \"%.8s\"\n", arg); | ||
| 204 | ✗ | ret = AVERROR(EINVAL); | |
| 205 | ✗ | goto fail; | |
| 206 | } | ||
| 207 | 198 | nb_in_channels[named]++; | |
| 208 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 198 times.
|
198 | if (nb_in_channels[!named]) { |
| 209 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 210 | "Can not mix named and numbered channels\n"); | ||
| 211 | ✗ | ret = AVERROR(EINVAL); | |
| 212 | ✗ | goto fail; | |
| 213 | } | ||
| 214 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 198 times.
|
198 | if (used_in_ch[in_ch_id]) { |
| 215 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 216 | "Can not reference in channel %d twice\n", in_ch_id); | ||
| 217 | ✗ | ret = AVERROR(EINVAL); | |
| 218 | ✗ | goto fail; | |
| 219 | } | ||
| 220 | 198 | used_in_ch[in_ch_id] = 1; | |
| 221 | 198 | pan->gain[out_ch_id][in_ch_id] = sign * gain; | |
| 222 | 198 | skip_spaces(&arg); | |
| 223 |
2/2✓ Branch 0 taken 148 times.
✓ Branch 1 taken 50 times.
|
198 | if (!*arg) |
| 224 | 148 | break; | |
| 225 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 32 times.
|
50 | if (*arg == '-') { |
| 226 | 18 | sign = -1; | |
| 227 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | } else if (*arg != '+') { |
| 228 | ✗ | av_log(ctx, AV_LOG_ERROR, "Syntax error near \"%.8s\"\n", arg); | |
| 229 | ✗ | ret = AVERROR(EINVAL); | |
| 230 | ✗ | goto fail; | |
| 231 | } else { | ||
| 232 | 32 | sign = 1; | |
| 233 | } | ||
| 234 | 50 | arg++; | |
| 235 | } | ||
| 236 | } | ||
| 237 | 52 | pan->need_renumber = !!nb_in_channels[1]; | |
| 238 | 52 | pan->pure_gains = are_gains_pure(pan); | |
| 239 | |||
| 240 | 52 | ret = 0; | |
| 241 | 52 | fail: | |
| 242 | 52 | av_free(args); | |
| 243 | 52 | return ret; | |
| 244 | } | ||
| 245 | |||
| 246 | 26 | static int query_formats(const AVFilterContext *ctx, | |
| 247 | AVFilterFormatsConfig **cfg_in, | ||
| 248 | AVFilterFormatsConfig **cfg_out) | ||
| 249 | { | ||
| 250 | 26 | const PanContext *pan = ctx->priv; | |
| 251 | AVFilterChannelLayouts *layouts; | ||
| 252 | int ret; | ||
| 253 | |||
| 254 | // inlink supports any channel layout | ||
| 255 | 26 | layouts = ff_all_channel_counts(); | |
| 256 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
|
26 | if ((ret = ff_channel_layouts_ref(layouts, &cfg_in[0]->channel_layouts)) < 0) |
| 257 | ✗ | return ret; | |
| 258 | |||
| 259 | // outlink supports only requested output channel layout | ||
| 260 | 26 | layouts = NULL; | |
| 261 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
|
26 | if ((ret = ff_add_channel_layout(&layouts, &pan->out_channel_layout)) < 0) |
| 262 | ✗ | return ret; | |
| 263 | 26 | return ff_channel_layouts_ref(layouts, &cfg_out[0]->channel_layouts); | |
| 264 | } | ||
| 265 | |||
| 266 | 26 | static int config_props(AVFilterLink *link) | |
| 267 | { | ||
| 268 | 26 | AVFilterContext *ctx = link->dst; | |
| 269 | 26 | PanContext *pan = ctx->priv; | |
| 270 | char buf[1024], *cur; | ||
| 271 | int i, j, k, r, ret; | ||
| 272 | double t; | ||
| 273 | |||
| 274 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 21 times.
|
26 | if (pan->need_renumber) { |
| 275 | // input channels were given by their name: renumber them | ||
| 276 |
2/2✓ Branch 0 taken 320 times.
✓ Branch 1 taken 5 times.
|
325 | for (i = j = 0; i < MAX_CHANNELS; i++) { |
| 277 |
2/2✓ Branch 1 taken 23 times.
✓ Branch 2 taken 297 times.
|
320 | if (av_channel_layout_index_from_channel(&link->ch_layout, i) >= 0) { |
| 278 |
2/2✓ Branch 0 taken 108 times.
✓ Branch 1 taken 23 times.
|
131 | for (k = 0; k < pan->nb_output_channels; k++) |
| 279 | 108 | pan->gain[k][j] = pan->gain[k][i]; | |
| 280 | 23 | j++; | |
| 281 | } | ||
| 282 | } | ||
| 283 | } | ||
| 284 | |||
| 285 | // sanity check; can't be done in query_formats since the inlink | ||
| 286 | // channel layout is unknown at that time | ||
| 287 |
1/2✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
|
26 | if (link->ch_layout.nb_channels > MAX_CHANNELS || |
| 288 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | pan->nb_output_channels > MAX_CHANNELS) { |
| 289 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 290 | "af_pan supports a maximum of %d channels. " | ||
| 291 | "Feel free to ask for a higher limit.\n", MAX_CHANNELS); | ||
| 292 | ✗ | return AVERROR_PATCHWELCOME; | |
| 293 | } | ||
| 294 | |||
| 295 | // init libswresample context | ||
| 296 | 26 | ret = swr_alloc_set_opts2(&pan->swr, | |
| 297 | 26 | &pan->out_channel_layout, link->format, link->sample_rate, | |
| 298 | 26 | &link->ch_layout, link->format, link->sample_rate, | |
| 299 | 0, ctx); | ||
| 300 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | if (ret < 0) |
| 301 | ✗ | return AVERROR(ENOMEM); | |
| 302 | |||
| 303 | // gains are pure, init the channel mapping | ||
| 304 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 8 times.
|
26 | if (pan->pure_gains) { |
| 305 | |||
| 306 | // get channel map from the pure gains | ||
| 307 |
2/2✓ Branch 0 taken 47 times.
✓ Branch 1 taken 18 times.
|
65 | for (i = 0; i < pan->nb_output_channels; i++) { |
| 308 | 47 | int ch_id = -1; | |
| 309 |
2/2✓ Branch 0 taken 88 times.
✓ Branch 1 taken 7 times.
|
95 | for (j = 0; j < link->ch_layout.nb_channels; j++) { |
| 310 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 48 times.
|
88 | if (pan->gain[i][j]) { |
| 311 | 40 | ch_id = j; | |
| 312 | 40 | break; | |
| 313 | } | ||
| 314 | } | ||
| 315 | 47 | pan->channel_map[i] = ch_id; | |
| 316 | } | ||
| 317 | |||
| 318 | 18 | av_opt_set_chlayout(pan->swr, "uchl", &pan->out_channel_layout, 0); | |
| 319 | 18 | swr_set_channel_mapping(pan->swr, pan->channel_map); | |
| 320 | } else { | ||
| 321 | // renormalize | ||
| 322 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 8 times.
|
35 | for (i = 0; i < pan->nb_output_channels; i++) { |
| 323 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 4 times.
|
27 | if (!((pan->need_renorm >> i) & 1)) |
| 324 | 23 | continue; | |
| 325 | 4 | t = 0; | |
| 326 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
|
16 | for (j = 0; j < link->ch_layout.nb_channels; j++) |
| 327 | 12 | t += fabs(pan->gain[i][j]); | |
| 328 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | if (t > -1E-5 && t < 1E-5) { |
| 329 | // t is almost 0 but not exactly, this is probably a mistake | ||
| 330 | ✗ | if (t) | |
| 331 | ✗ | av_log(ctx, AV_LOG_WARNING, | |
| 332 | "Degenerate coefficients while renormalizing\n"); | ||
| 333 | ✗ | continue; | |
| 334 | } | ||
| 335 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
|
16 | for (j = 0; j < link->ch_layout.nb_channels; j++) |
| 336 | 12 | pan->gain[i][j] /= t; | |
| 337 | } | ||
| 338 | 8 | swr_set_matrix(pan->swr, pan->gain[0], pan->gain[1] - pan->gain[0]); | |
| 339 | } | ||
| 340 | |||
| 341 | 26 | r = swr_init(pan->swr); | |
| 342 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | if (r < 0) |
| 343 | ✗ | return r; | |
| 344 | |||
| 345 | // summary | ||
| 346 |
2/2✓ Branch 0 taken 74 times.
✓ Branch 1 taken 26 times.
|
100 | for (i = 0; i < pan->nb_output_channels; i++) { |
| 347 | 74 | cur = buf; | |
| 348 |
2/2✓ Branch 0 taken 254 times.
✓ Branch 1 taken 74 times.
|
328 | for (j = 0; j < link->ch_layout.nb_channels; j++) { |
| 349 |
2/2✓ Branch 0 taken 180 times.
✓ Branch 1 taken 74 times.
|
254 | r = snprintf(cur, buf + sizeof(buf) - cur, "%s%.3g i%d", |
| 350 | j ? " + " : "", pan->gain[i][j], j); | ||
| 351 |
1/2✓ Branch 0 taken 254 times.
✗ Branch 1 not taken.
|
254 | cur += FFMIN(buf + sizeof(buf) - cur, r); |
| 352 | } | ||
| 353 | 74 | av_log(ctx, AV_LOG_VERBOSE, "o%d = %s\n", i, buf); | |
| 354 | } | ||
| 355 | // add channel mapping summary if possible | ||
| 356 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 8 times.
|
26 | if (pan->pure_gains) { |
| 357 | 18 | av_log(ctx, AV_LOG_INFO, "Pure channel mapping detected:"); | |
| 358 |
2/2✓ Branch 0 taken 47 times.
✓ Branch 1 taken 18 times.
|
65 | for (i = 0; i < pan->nb_output_channels; i++) |
| 359 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 40 times.
|
47 | if (pan->channel_map[i] < 0) |
| 360 | 7 | av_log(ctx, AV_LOG_INFO, " M"); | |
| 361 | else | ||
| 362 | 40 | av_log(ctx, AV_LOG_INFO, " %d", pan->channel_map[i]); | |
| 363 | 18 | av_log(ctx, AV_LOG_INFO, "\n"); | |
| 364 | 18 | return 0; | |
| 365 | } | ||
| 366 | 8 | return 0; | |
| 367 | } | ||
| 368 | |||
| 369 | 1164 | static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) | |
| 370 | { | ||
| 371 | int ret; | ||
| 372 | 1164 | int n = insamples->nb_samples; | |
| 373 | 1164 | AVFilterLink *const outlink = inlink->dst->outputs[0]; | |
| 374 | 1164 | AVFrame *outsamples = ff_get_audio_buffer(outlink, n); | |
| 375 | 1164 | PanContext *pan = inlink->dst->priv; | |
| 376 | |||
| 377 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1164 times.
|
1164 | if (!outsamples) { |
| 378 | ✗ | av_frame_free(&insamples); | |
| 379 | ✗ | return AVERROR(ENOMEM); | |
| 380 | } | ||
| 381 | 1164 | swr_convert(pan->swr, outsamples->extended_data, n, | |
| 382 | 1164 | (void *)insamples->extended_data, n); | |
| 383 | 1164 | av_frame_copy_props(outsamples, insamples); | |
| 384 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1164 times.
|
1164 | if ((ret = av_channel_layout_copy(&outsamples->ch_layout, &outlink->ch_layout)) < 0) { |
| 385 | ✗ | av_frame_free(&outsamples); | |
| 386 | ✗ | av_frame_free(&insamples); | |
| 387 | ✗ | return ret; | |
| 388 | } | ||
| 389 | |||
| 390 | 1164 | av_frame_free(&insamples); | |
| 391 | 1164 | return ff_filter_frame(outlink, outsamples); | |
| 392 | } | ||
| 393 | |||
| 394 | 52 | static av_cold void uninit(AVFilterContext *ctx) | |
| 395 | { | ||
| 396 | 52 | PanContext *pan = ctx->priv; | |
| 397 | 52 | swr_free(&pan->swr); | |
| 398 | 52 | av_channel_layout_uninit(&pan->out_channel_layout); | |
| 399 | 52 | } | |
| 400 | |||
| 401 | #define OFFSET(x) offsetof(PanContext, x) | ||
| 402 | |||
| 403 | static const AVOption pan_options[] = { | ||
| 404 | { "args", NULL, OFFSET(args), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM }, | ||
| 405 | { NULL } | ||
| 406 | }; | ||
| 407 | |||
| 408 | AVFILTER_DEFINE_CLASS(pan); | ||
| 409 | |||
| 410 | static const AVFilterPad pan_inputs[] = { | ||
| 411 | { | ||
| 412 | .name = "default", | ||
| 413 | .type = AVMEDIA_TYPE_AUDIO, | ||
| 414 | .config_props = config_props, | ||
| 415 | .filter_frame = filter_frame, | ||
| 416 | }, | ||
| 417 | }; | ||
| 418 | |||
| 419 | const FFFilter ff_af_pan = { | ||
| 420 | .p.name = "pan", | ||
| 421 | .p.description = NULL_IF_CONFIG_SMALL("Remix channels with coefficients (panning)."), | ||
| 422 | .p.priv_class = &pan_class, | ||
| 423 | .priv_size = sizeof(PanContext), | ||
| 424 | .init = init, | ||
| 425 | .uninit = uninit, | ||
| 426 | FILTER_INPUTS(pan_inputs), | ||
| 427 | FILTER_OUTPUTS(ff_audio_default_filterpad), | ||
| 428 | FILTER_QUERY_FUNC2(query_formats), | ||
| 429 | }; | ||
| 430 |