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