| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2011 Stefano Sabatini | ||
| 3 | * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> | ||
| 4 | * | ||
| 5 | * This file is part of FFmpeg. | ||
| 6 | * | ||
| 7 | * FFmpeg is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with FFmpeg; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | /** | ||
| 23 | * @file | ||
| 24 | * audio volume filter | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include "libavutil/channel_layout.h" | ||
| 28 | #include "libavutil/common.h" | ||
| 29 | #include "libavutil/eval.h" | ||
| 30 | #include "libavutil/ffmath.h" | ||
| 31 | #include "libavutil/float_dsp.h" | ||
| 32 | #include "libavutil/mem.h" | ||
| 33 | #include "libavutil/opt.h" | ||
| 34 | #include "libavutil/replaygain.h" | ||
| 35 | |||
| 36 | #include "audio.h" | ||
| 37 | #include "avfilter.h" | ||
| 38 | #include "filters.h" | ||
| 39 | #include "formats.h" | ||
| 40 | #include "af_volume.h" | ||
| 41 | |||
| 42 | static const char * const precision_str[] = { | ||
| 43 | "fixed", "float", "double" | ||
| 44 | }; | ||
| 45 | |||
| 46 | static const char *const var_names[] = { | ||
| 47 | "n", ///< frame number (starting at zero) | ||
| 48 | "nb_channels", ///< number of channels | ||
| 49 | "nb_consumed_samples", ///< number of samples consumed by the filter | ||
| 50 | "nb_samples", ///< number of samples in the current frame | ||
| 51 | "pts", ///< frame presentation timestamp | ||
| 52 | "sample_rate", ///< sample rate | ||
| 53 | "startpts", ///< PTS at start of stream | ||
| 54 | "startt", ///< time at start of stream | ||
| 55 | "t", ///< time in the file of the frame | ||
| 56 | "tb", ///< timebase | ||
| 57 | "volume", ///< last set value | ||
| 58 | NULL | ||
| 59 | }; | ||
| 60 | |||
| 61 | #define OFFSET(x) offsetof(VolumeContext, x) | ||
| 62 | #define A AV_OPT_FLAG_AUDIO_PARAM | ||
| 63 | #define F AV_OPT_FLAG_FILTERING_PARAM | ||
| 64 | #define T AV_OPT_FLAG_RUNTIME_PARAM | ||
| 65 | |||
| 66 | static const AVOption volume_options[] = { | ||
| 67 | { "volume", "set volume adjustment expression", | ||
| 68 | OFFSET(volume_expr), AV_OPT_TYPE_STRING, { .str = "1.0" }, .flags = A|F|T }, | ||
| 69 | { "precision", "select mathematical precision", | ||
| 70 | OFFSET(precision), AV_OPT_TYPE_INT, { .i64 = PRECISION_FLOAT }, PRECISION_FIXED, PRECISION_DOUBLE, A|F, .unit = "precision" }, | ||
| 71 | { "fixed", "select 8-bit fixed-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FIXED }, INT_MIN, INT_MAX, A|F, .unit = "precision" }, | ||
| 72 | { "float", "select 32-bit floating-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FLOAT }, INT_MIN, INT_MAX, A|F, .unit = "precision" }, | ||
| 73 | { "double", "select 64-bit floating-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_DOUBLE }, INT_MIN, INT_MAX, A|F, .unit = "precision" }, | ||
| 74 | { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_ONCE}, 0, EVAL_MODE_NB-1, .flags = A|F, .unit = "eval" }, | ||
| 75 | { "once", "eval volume expression once", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_ONCE}, .flags = A|F, .unit = "eval" }, | ||
| 76 | { "frame", "eval volume expression per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = A|F, .unit = "eval" }, | ||
| 77 | { "replaygain", "Apply replaygain side data when present", | ||
| 78 | OFFSET(replaygain), AV_OPT_TYPE_INT, { .i64 = REPLAYGAIN_DROP }, REPLAYGAIN_DROP, REPLAYGAIN_ALBUM, A|F, .unit = "replaygain" }, | ||
| 79 | { "drop", "replaygain side data is dropped", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_DROP }, 0, 0, A|F, .unit = "replaygain" }, | ||
| 80 | { "ignore", "replaygain side data is ignored", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_IGNORE }, 0, 0, A|F, .unit = "replaygain" }, | ||
| 81 | { "track", "track gain is preferred", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_TRACK }, 0, 0, A|F, .unit = "replaygain" }, | ||
| 82 | { "album", "album gain is preferred", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_ALBUM }, 0, 0, A|F, .unit = "replaygain" }, | ||
| 83 | { "replaygain_preamp", "Apply replaygain pre-amplification", | ||
| 84 | OFFSET(replaygain_preamp), AV_OPT_TYPE_DOUBLE, { .dbl = 0.0 }, -15.0, 15.0, A|F }, | ||
| 85 | { "replaygain_noclip", "Apply replaygain clipping prevention", | ||
| 86 | OFFSET(replaygain_noclip), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, A|F }, | ||
| 87 | { NULL } | ||
| 88 | }; | ||
| 89 | |||
| 90 | AVFILTER_DEFINE_CLASS(volume); | ||
| 91 | |||
| 92 | 6 | static int set_expr(AVExpr **pexpr, const char *expr, void *log_ctx) | |
| 93 | { | ||
| 94 | int ret; | ||
| 95 | 6 | AVExpr *old = NULL; | |
| 96 | |||
| 97 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (*pexpr) |
| 98 | ✗ | old = *pexpr; | |
| 99 | 6 | ret = av_expr_parse(pexpr, expr, var_names, | |
| 100 | NULL, NULL, NULL, NULL, 0, log_ctx); | ||
| 101 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (ret < 0) { |
| 102 | ✗ | av_log(log_ctx, AV_LOG_ERROR, | |
| 103 | "Error when evaluating the volume expression '%s'\n", expr); | ||
| 104 | ✗ | *pexpr = old; | |
| 105 | ✗ | return ret; | |
| 106 | } | ||
| 107 | |||
| 108 | 6 | av_expr_free(old); | |
| 109 | 6 | return 0; | |
| 110 | } | ||
| 111 | |||
| 112 | 6 | static av_cold int init(AVFilterContext *ctx) | |
| 113 | { | ||
| 114 | 6 | VolumeContext *vol = ctx->priv; | |
| 115 | |||
| 116 | 6 | vol->fdsp = avpriv_float_dsp_alloc(0); | |
| 117 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (!vol->fdsp) |
| 118 | ✗ | return AVERROR(ENOMEM); | |
| 119 | |||
| 120 | 6 | return set_expr(&vol->volume_pexpr, vol->volume_expr, ctx); | |
| 121 | } | ||
| 122 | |||
| 123 | 6 | static av_cold void uninit(AVFilterContext *ctx) | |
| 124 | { | ||
| 125 | 6 | VolumeContext *vol = ctx->priv; | |
| 126 | 6 | av_expr_free(vol->volume_pexpr); | |
| 127 | 6 | av_freep(&vol->fdsp); | |
| 128 | 6 | } | |
| 129 | |||
| 130 | 3 | static int query_formats(const AVFilterContext *ctx, | |
| 131 | AVFilterFormatsConfig **cfg_in, | ||
| 132 | AVFilterFormatsConfig **cfg_out) | ||
| 133 | { | ||
| 134 | 3 | const VolumeContext *vol = ctx->priv; | |
| 135 | static const enum AVSampleFormat sample_fmts[][7] = { | ||
| 136 | [PRECISION_FIXED] = { | ||
| 137 | AV_SAMPLE_FMT_U8, | ||
| 138 | AV_SAMPLE_FMT_U8P, | ||
| 139 | AV_SAMPLE_FMT_S16, | ||
| 140 | AV_SAMPLE_FMT_S16P, | ||
| 141 | AV_SAMPLE_FMT_S32, | ||
| 142 | AV_SAMPLE_FMT_S32P, | ||
| 143 | AV_SAMPLE_FMT_NONE | ||
| 144 | }, | ||
| 145 | [PRECISION_FLOAT] = { | ||
| 146 | AV_SAMPLE_FMT_FLT, | ||
| 147 | AV_SAMPLE_FMT_FLTP, | ||
| 148 | AV_SAMPLE_FMT_NONE | ||
| 149 | }, | ||
| 150 | [PRECISION_DOUBLE] = { | ||
| 151 | AV_SAMPLE_FMT_DBL, | ||
| 152 | AV_SAMPLE_FMT_DBLP, | ||
| 153 | AV_SAMPLE_FMT_NONE | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | int ret; | ||
| 157 | |||
| 158 | 3 | ret = ff_set_common_formats_from_list2(ctx, cfg_in, cfg_out, sample_fmts[vol->precision]); | |
| 159 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (ret < 0) |
| 160 | ✗ | return ret; | |
| 161 | |||
| 162 | 3 | return 0; | |
| 163 | } | ||
| 164 | |||
| 165 | ✗ | static inline void scale_samples_u8(uint8_t *dst, const uint8_t *src, | |
| 166 | int nb_samples, int volume) | ||
| 167 | { | ||
| 168 | int i; | ||
| 169 | ✗ | for (i = 0; i < nb_samples; i++) | |
| 170 | ✗ | dst[i] = av_clip_uint8(((((int64_t)src[i] - 128) * volume + 128) >> 8) + 128); | |
| 171 | ✗ | } | |
| 172 | |||
| 173 | ✗ | static inline void scale_samples_u8_small(uint8_t *dst, const uint8_t *src, | |
| 174 | int nb_samples, int volume) | ||
| 175 | { | ||
| 176 | int i; | ||
| 177 | ✗ | for (i = 0; i < nb_samples; i++) | |
| 178 | ✗ | dst[i] = av_clip_uint8((((src[i] - 128) * volume + 128) >> 8) + 128); | |
| 179 | ✗ | } | |
| 180 | |||
| 181 | ✗ | static inline void scale_samples_s16(uint8_t *dst, const uint8_t *src, | |
| 182 | int nb_samples, int volume) | ||
| 183 | { | ||
| 184 | int i; | ||
| 185 | ✗ | int16_t *smp_dst = (int16_t *)dst; | |
| 186 | ✗ | const int16_t *smp_src = (const int16_t *)src; | |
| 187 | ✗ | for (i = 0; i < nb_samples; i++) | |
| 188 | ✗ | smp_dst[i] = av_clip_int16(((int64_t)smp_src[i] * volume + 128) >> 8); | |
| 189 | ✗ | } | |
| 190 | |||
| 191 | 65 | static inline void scale_samples_s16_small(uint8_t *dst, const uint8_t *src, | |
| 192 | int nb_samples, int volume) | ||
| 193 | { | ||
| 194 | int i; | ||
| 195 | 65 | int16_t *smp_dst = (int16_t *)dst; | |
| 196 | 65 | const int16_t *smp_src = (const int16_t *)src; | |
| 197 |
2/2✓ Branch 0 taken 529200 times.
✓ Branch 1 taken 65 times.
|
529265 | for (i = 0; i < nb_samples; i++) |
| 198 | 529200 | smp_dst[i] = av_clip_int16((smp_src[i] * volume + 128) >> 8); | |
| 199 | 65 | } | |
| 200 | |||
| 201 | ✗ | static inline void scale_samples_s32(uint8_t *dst, const uint8_t *src, | |
| 202 | int nb_samples, int volume) | ||
| 203 | { | ||
| 204 | int i; | ||
| 205 | ✗ | int32_t *smp_dst = (int32_t *)dst; | |
| 206 | ✗ | const int32_t *smp_src = (const int32_t *)src; | |
| 207 | ✗ | for (i = 0; i < nb_samples; i++) | |
| 208 | ✗ | smp_dst[i] = av_clipl_int32((((int64_t)smp_src[i] * volume + 128) >> 8)); | |
| 209 | ✗ | } | |
| 210 | |||
| 211 | 3 | static av_cold void volume_init(VolumeContext *vol) | |
| 212 | { | ||
| 213 | 3 | vol->samples_align = 1; | |
| 214 | |||
| 215 |
3/6✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
3 | switch (av_get_packed_sample_fmt(vol->sample_fmt)) { |
| 216 | ✗ | case AV_SAMPLE_FMT_U8: | |
| 217 | ✗ | if (vol->volume_i < 0x1000000) | |
| 218 | ✗ | vol->scale_samples = scale_samples_u8_small; | |
| 219 | else | ||
| 220 | ✗ | vol->scale_samples = scale_samples_u8; | |
| 221 | ✗ | break; | |
| 222 | 1 | case AV_SAMPLE_FMT_S16: | |
| 223 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (vol->volume_i < 0x10000) |
| 224 | 1 | vol->scale_samples = scale_samples_s16_small; | |
| 225 | else | ||
| 226 | ✗ | vol->scale_samples = scale_samples_s16; | |
| 227 | 1 | break; | |
| 228 | 1 | case AV_SAMPLE_FMT_S32: | |
| 229 | 1 | vol->scale_samples = scale_samples_s32; | |
| 230 | 1 | break; | |
| 231 | 1 | case AV_SAMPLE_FMT_FLT: | |
| 232 | 1 | vol->samples_align = 4; | |
| 233 | 1 | break; | |
| 234 | ✗ | case AV_SAMPLE_FMT_DBL: | |
| 235 | ✗ | vol->samples_align = 8; | |
| 236 | ✗ | break; | |
| 237 | } | ||
| 238 | |||
| 239 | #if ARCH_X86 | ||
| 240 | 3 | ff_volume_init_x86(vol); | |
| 241 | #endif | ||
| 242 | 3 | } | |
| 243 | |||
| 244 | 3 | static int set_volume(AVFilterContext *ctx) | |
| 245 | { | ||
| 246 | 3 | VolumeContext *vol = ctx->priv; | |
| 247 | |||
| 248 | 3 | vol->volume = av_expr_eval(vol->volume_pexpr, vol->var_values, NULL); | |
| 249 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (isnan(vol->volume)) { |
| 250 | ✗ | if (vol->eval_mode == EVAL_MODE_ONCE) { | |
| 251 | ✗ | av_log(ctx, AV_LOG_ERROR, "Invalid value NaN for volume\n"); | |
| 252 | ✗ | return AVERROR(EINVAL); | |
| 253 | } else { | ||
| 254 | ✗ | av_log(ctx, AV_LOG_WARNING, "Invalid value NaN for volume, setting to 0\n"); | |
| 255 | ✗ | vol->volume = 0; | |
| 256 | } | ||
| 257 | } | ||
| 258 | 3 | vol->var_values[VAR_VOLUME] = vol->volume; | |
| 259 | |||
| 260 | 3 | av_log(ctx, AV_LOG_VERBOSE, "n:%f t:%f pts:%f precision:%s ", | |
| 261 | vol->var_values[VAR_N], vol->var_values[VAR_T], vol->var_values[VAR_PTS], | ||
| 262 | 3 | precision_str[vol->precision]); | |
| 263 | |||
| 264 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | if (vol->precision == PRECISION_FIXED) { |
| 265 | 2 | vol->volume_i = (int)(vol->volume * 256 + 0.5); | |
| 266 | 2 | vol->volume = vol->volume_i / 256.0; | |
| 267 | 2 | av_log(ctx, AV_LOG_VERBOSE, "volume_i:%d/255 ", vol->volume_i); | |
| 268 | } | ||
| 269 | 3 | av_log(ctx, AV_LOG_VERBOSE, "volume:%f volume_dB:%f\n", | |
| 270 | 3 | vol->volume, 20.0*log10(vol->volume)); | |
| 271 | |||
| 272 | 3 | volume_init(vol); | |
| 273 | 3 | return 0; | |
| 274 | } | ||
| 275 | |||
| 276 | 3 | static int config_output(AVFilterLink *outlink) | |
| 277 | { | ||
| 278 | 3 | AVFilterContext *ctx = outlink->src; | |
| 279 | 3 | VolumeContext *vol = ctx->priv; | |
| 280 | 3 | AVFilterLink *inlink = ctx->inputs[0]; | |
| 281 | |||
| 282 | 3 | vol->sample_fmt = inlink->format; | |
| 283 | 3 | vol->channels = inlink->ch_layout.nb_channels; | |
| 284 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
|
3 | vol->planes = av_sample_fmt_is_planar(inlink->format) ? vol->channels : 1; |
| 285 | |||
| 286 | 3 | vol->var_values[VAR_N] = | |
| 287 | 3 | vol->var_values[VAR_NB_CONSUMED_SAMPLES] = | |
| 288 | 3 | vol->var_values[VAR_NB_SAMPLES] = | |
| 289 | 3 | vol->var_values[VAR_PTS] = | |
| 290 | 3 | vol->var_values[VAR_STARTPTS] = | |
| 291 | 3 | vol->var_values[VAR_STARTT] = | |
| 292 | 3 | vol->var_values[VAR_T] = | |
| 293 | 3 | vol->var_values[VAR_VOLUME] = NAN; | |
| 294 | |||
| 295 | 3 | vol->var_values[VAR_NB_CHANNELS] = inlink->ch_layout.nb_channels; | |
| 296 | 3 | vol->var_values[VAR_TB] = av_q2d(inlink->time_base); | |
| 297 | 3 | vol->var_values[VAR_SAMPLE_RATE] = inlink->sample_rate; | |
| 298 | |||
| 299 | 3 | av_log(inlink->src, AV_LOG_VERBOSE, "tb:%f sample_rate:%f nb_channels:%f\n", | |
| 300 | vol->var_values[VAR_TB], | ||
| 301 | vol->var_values[VAR_SAMPLE_RATE], | ||
| 302 | vol->var_values[VAR_NB_CHANNELS]); | ||
| 303 | |||
| 304 | 3 | return set_volume(ctx); | |
| 305 | } | ||
| 306 | |||
| 307 | ✗ | static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, | |
| 308 | char *res, int res_len, int flags) | ||
| 309 | { | ||
| 310 | ✗ | VolumeContext *vol = ctx->priv; | |
| 311 | ✗ | int ret = AVERROR(ENOSYS); | |
| 312 | |||
| 313 | ✗ | if (!strcmp(cmd, "volume")) { | |
| 314 | ✗ | if ((ret = set_expr(&vol->volume_pexpr, args, ctx)) < 0) | |
| 315 | ✗ | return ret; | |
| 316 | ✗ | if (vol->eval_mode == EVAL_MODE_ONCE) | |
| 317 | ✗ | set_volume(ctx); | |
| 318 | } | ||
| 319 | |||
| 320 | ✗ | return ret; | |
| 321 | } | ||
| 322 | |||
| 323 | 199 | static int filter_frame(AVFilterLink *inlink, AVFrame *buf) | |
| 324 | { | ||
| 325 | 199 | FilterLink *inl = ff_filter_link(inlink); | |
| 326 | 199 | AVFilterContext *ctx = inlink->dst; | |
| 327 | 199 | VolumeContext *vol = inlink->dst->priv; | |
| 328 | 199 | AVFilterLink *outlink = inlink->dst->outputs[0]; | |
| 329 | 199 | int nb_samples = buf->nb_samples; | |
| 330 | AVFrame *out_buf; | ||
| 331 | 199 | AVFrameSideData *sd = av_frame_get_side_data(buf, AV_FRAME_DATA_REPLAYGAIN); | |
| 332 | int ret; | ||
| 333 | |||
| 334 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 199 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
199 | if (sd && vol->replaygain != REPLAYGAIN_IGNORE) { |
| 335 | ✗ | if (vol->replaygain != REPLAYGAIN_DROP) { | |
| 336 | ✗ | AVReplayGain *replaygain = (AVReplayGain*)sd->data; | |
| 337 | ✗ | int32_t gain = 100000; | |
| 338 | ✗ | uint32_t peak = 100000; | |
| 339 | float g, p; | ||
| 340 | |||
| 341 | ✗ | if (vol->replaygain == REPLAYGAIN_TRACK && | |
| 342 | ✗ | replaygain->track_gain != INT32_MIN) { | |
| 343 | ✗ | gain = replaygain->track_gain; | |
| 344 | |||
| 345 | ✗ | if (replaygain->track_peak != 0) | |
| 346 | ✗ | peak = replaygain->track_peak; | |
| 347 | ✗ | } else if (replaygain->album_gain != INT32_MIN) { | |
| 348 | ✗ | gain = replaygain->album_gain; | |
| 349 | |||
| 350 | ✗ | if (replaygain->album_peak != 0) | |
| 351 | ✗ | peak = replaygain->album_peak; | |
| 352 | } else { | ||
| 353 | ✗ | av_log(inlink->dst, AV_LOG_WARNING, "Both ReplayGain gain " | |
| 354 | "values are unknown.\n"); | ||
| 355 | } | ||
| 356 | ✗ | g = gain / 100000.0f; | |
| 357 | ✗ | p = peak / 100000.0f; | |
| 358 | |||
| 359 | ✗ | av_log(inlink->dst, AV_LOG_VERBOSE, | |
| 360 | "Using gain %f dB from replaygain side data.\n", g); | ||
| 361 | |||
| 362 | ✗ | vol->volume = ff_exp10((g + vol->replaygain_preamp) / 20); | |
| 363 | ✗ | if (vol->replaygain_noclip) | |
| 364 | ✗ | vol->volume = FFMIN(vol->volume, 1.0 / p); | |
| 365 | ✗ | vol->volume_i = (int)(vol->volume * 256 + 0.5); | |
| 366 | |||
| 367 | ✗ | volume_init(vol); | |
| 368 | } | ||
| 369 | ✗ | av_frame_remove_side_data(buf, AV_FRAME_DATA_REPLAYGAIN); | |
| 370 | } | ||
| 371 | |||
| 372 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 196 times.
|
199 | if (isnan(vol->var_values[VAR_STARTPTS])) { |
| 373 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | vol->var_values[VAR_STARTPTS] = TS2D(buf->pts); |
| 374 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | vol->var_values[VAR_STARTT ] = TS2T(buf->pts, inlink->time_base); |
| 375 | } | ||
| 376 |
1/2✓ Branch 0 taken 199 times.
✗ Branch 1 not taken.
|
199 | vol->var_values[VAR_PTS] = TS2D(buf->pts); |
| 377 |
1/2✓ Branch 0 taken 199 times.
✗ Branch 1 not taken.
|
199 | vol->var_values[VAR_T ] = TS2T(buf->pts, inlink->time_base); |
| 378 | 199 | vol->var_values[VAR_N ] = inl->frame_count_out; | |
| 379 | |||
| 380 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 199 times.
|
199 | if (vol->eval_mode == EVAL_MODE_FRAME) |
| 381 | ✗ | set_volume(ctx); | |
| 382 | |||
| 383 |
2/4✓ Branch 0 taken 199 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 199 times.
|
199 | if (vol->volume == 1.0 || vol->volume_i == 256) { |
| 384 | ✗ | out_buf = buf; | |
| 385 | ✗ | goto end; | |
| 386 | } | ||
| 387 | |||
| 388 | /* do volume scaling in-place if input buffer is writable */ | ||
| 389 |
1/2✓ Branch 1 taken 199 times.
✗ Branch 2 not taken.
|
199 | if (av_frame_is_writable(buf) |
| 390 |
4/4✓ Branch 0 taken 158 times.
✓ Branch 1 taken 41 times.
✓ Branch 2 taken 65 times.
✓ Branch 3 taken 93 times.
|
199 | && (vol->precision != PRECISION_FIXED || vol->volume_i > 0)) { |
| 391 | 106 | out_buf = buf; | |
| 392 | } else { | ||
| 393 | 93 | out_buf = ff_get_audio_buffer(outlink, nb_samples); | |
| 394 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
|
93 | if (!out_buf) { |
| 395 | ✗ | av_frame_free(&buf); | |
| 396 | ✗ | return AVERROR(ENOMEM); | |
| 397 | } | ||
| 398 | 93 | ret = av_frame_copy_props(out_buf, buf); | |
| 399 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
|
93 | if (ret < 0) { |
| 400 | ✗ | av_frame_free(&out_buf); | |
| 401 | ✗ | av_frame_free(&buf); | |
| 402 | ✗ | return ret; | |
| 403 | } | ||
| 404 | } | ||
| 405 | |||
| 406 |
4/4✓ Branch 0 taken 158 times.
✓ Branch 1 taken 41 times.
✓ Branch 2 taken 65 times.
✓ Branch 3 taken 93 times.
|
199 | if (vol->precision != PRECISION_FIXED || vol->volume_i > 0) { |
| 407 | int p, plane_samples; | ||
| 408 | |||
| 409 |
2/2✓ Branch 1 taken 41 times.
✓ Branch 2 taken 65 times.
|
106 | if (av_sample_fmt_is_planar(buf->format)) |
| 410 | 41 | plane_samples = FFALIGN(nb_samples, vol->samples_align); | |
| 411 | else | ||
| 412 | 65 | plane_samples = FFALIGN(nb_samples * vol->channels, vol->samples_align); | |
| 413 | |||
| 414 |
2/2✓ Branch 0 taken 65 times.
✓ Branch 1 taken 41 times.
|
106 | if (vol->precision == PRECISION_FIXED) { |
| 415 |
2/2✓ Branch 0 taken 65 times.
✓ Branch 1 taken 65 times.
|
130 | for (p = 0; p < vol->planes; p++) { |
| 416 | 65 | vol->scale_samples(out_buf->extended_data[p], | |
| 417 | 65 | buf->extended_data[p], plane_samples, | |
| 418 | vol->volume_i); | ||
| 419 | } | ||
| 420 |
1/2✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
|
41 | } else if (av_get_packed_sample_fmt(vol->sample_fmt) == AV_SAMPLE_FMT_FLT) { |
| 421 |
2/2✓ Branch 0 taken 82 times.
✓ Branch 1 taken 41 times.
|
123 | for (p = 0; p < vol->planes; p++) { |
| 422 | 82 | vol->fdsp->vector_fmul_scalar((float *)out_buf->extended_data[p], | |
| 423 | 82 | (const float *)buf->extended_data[p], | |
| 424 | 82 | vol->volume, plane_samples); | |
| 425 | } | ||
| 426 | } else { | ||
| 427 | ✗ | for (p = 0; p < vol->planes; p++) { | |
| 428 | ✗ | vol->fdsp->vector_dmul_scalar((double *)out_buf->extended_data[p], | |
| 429 | ✗ | (const double *)buf->extended_data[p], | |
| 430 | vol->volume, plane_samples); | ||
| 431 | } | ||
| 432 | } | ||
| 433 | } | ||
| 434 | |||
| 435 |
2/2✓ Branch 0 taken 106 times.
✓ Branch 1 taken 93 times.
|
199 | if (buf != out_buf) |
| 436 | 93 | av_frame_free(&buf); | |
| 437 | |||
| 438 | 106 | end: | |
| 439 | 199 | vol->var_values[VAR_NB_CONSUMED_SAMPLES] += out_buf->nb_samples; | |
| 440 | 199 | return ff_filter_frame(outlink, out_buf); | |
| 441 | } | ||
| 442 | |||
| 443 | static const AVFilterPad avfilter_af_volume_inputs[] = { | ||
| 444 | { | ||
| 445 | .name = "default", | ||
| 446 | .type = AVMEDIA_TYPE_AUDIO, | ||
| 447 | .filter_frame = filter_frame, | ||
| 448 | }, | ||
| 449 | }; | ||
| 450 | |||
| 451 | static const AVFilterPad avfilter_af_volume_outputs[] = { | ||
| 452 | { | ||
| 453 | .name = "default", | ||
| 454 | .type = AVMEDIA_TYPE_AUDIO, | ||
| 455 | .config_props = config_output, | ||
| 456 | }, | ||
| 457 | }; | ||
| 458 | |||
| 459 | const FFFilter ff_af_volume = { | ||
| 460 | .p.name = "volume", | ||
| 461 | .p.description = NULL_IF_CONFIG_SMALL("Change input volume."), | ||
| 462 | .p.priv_class = &volume_class, | ||
| 463 | .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, | ||
| 464 | .priv_size = sizeof(VolumeContext), | ||
| 465 | .init = init, | ||
| 466 | .uninit = uninit, | ||
| 467 | FILTER_INPUTS(avfilter_af_volume_inputs), | ||
| 468 | FILTER_OUTPUTS(avfilter_af_volume_outputs), | ||
| 469 | FILTER_QUERY_FUNC2(query_formats), | ||
| 470 | .process_command = process_command, | ||
| 471 | }; | ||
| 472 |