Imported Debian version 2.4.3~trusty1
[deb_ffmpeg.git] / ffmpeg / libavfilter / af_silencedetect.c
CommitLineData
2ba45a60
DM
1/*
2 * Copyright (c) 2012 Clément Bœsch <u pkh me>
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21/**
22 * @file
23 * Audio silence detector
24 */
25
26#include <float.h> /* DBL_MAX */
27
28#include "libavutil/opt.h"
29#include "libavutil/timestamp.h"
30#include "audio.h"
31#include "formats.h"
32#include "avfilter.h"
33#include "internal.h"
34
35typedef struct SilenceDetectContext {
36 const AVClass *class;
37 double noise; ///< noise amplitude ratio
38 double duration; ///< minimum duration of silence until notification
39 int64_t nb_null_samples; ///< current number of continuous zero samples
40 int64_t start; ///< if silence is detected, this value contains the time of the first zero sample
41 int last_sample_rate; ///< last sample rate to check for sample rate changes
42
43 void (*silencedetect)(struct SilenceDetectContext *s, AVFrame *insamples,
44 int nb_samples, int64_t nb_samples_notify,
45 AVRational time_base);
46} SilenceDetectContext;
47
48#define OFFSET(x) offsetof(SilenceDetectContext, x)
49#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM
50static const AVOption silencedetect_options[] = {
51 { "n", "set noise tolerance", OFFSET(noise), AV_OPT_TYPE_DOUBLE, {.dbl=0.001}, 0, DBL_MAX, FLAGS },
52 { "noise", "set noise tolerance", OFFSET(noise), AV_OPT_TYPE_DOUBLE, {.dbl=0.001}, 0, DBL_MAX, FLAGS },
53 { "d", "set minimum duration in seconds", OFFSET(duration), AV_OPT_TYPE_DOUBLE, {.dbl=2.}, 0, 24*60*60, FLAGS },
54 { "duration", "set minimum duration in seconds", OFFSET(duration), AV_OPT_TYPE_DOUBLE, {.dbl=2.}, 0, 24*60*60, FLAGS },
55 { NULL }
56};
57
58AVFILTER_DEFINE_CLASS(silencedetect);
59
60static char *get_metadata_val(AVFrame *insamples, const char *key)
61{
62 AVDictionaryEntry *e = av_dict_get(insamples->metadata, key, NULL, 0);
63 return e && e->value ? e->value : NULL;
64}
65
66static av_always_inline void update(SilenceDetectContext *s, AVFrame *insamples,
67 int is_silence, int64_t nb_samples_notify,
68 AVRational time_base)
69{
70 if (is_silence) {
71 if (!s->start) {
72 s->nb_null_samples++;
73 if (s->nb_null_samples >= nb_samples_notify) {
74 s->start = insamples->pts - (int64_t)(s->duration / av_q2d(time_base) + .5);
75 av_dict_set(&insamples->metadata, "lavfi.silence_start",
76 av_ts2timestr(s->start, &time_base), 0);
77 av_log(s, AV_LOG_INFO, "silence_start: %s\n",
78 get_metadata_val(insamples, "lavfi.silence_start"));
79 }
80 }
81 } else {
82 if (s->start) {
83 av_dict_set(&insamples->metadata, "lavfi.silence_end",
84 av_ts2timestr(insamples->pts, &time_base), 0);
85 av_dict_set(&insamples->metadata, "lavfi.silence_duration",
86 av_ts2timestr(insamples->pts - s->start, &time_base), 0);
87 av_log(s, AV_LOG_INFO,
88 "silence_end: %s | silence_duration: %s\n",
89 get_metadata_val(insamples, "lavfi.silence_end"),
90 get_metadata_val(insamples, "lavfi.silence_duration"));
91 }
92 s->nb_null_samples = s->start = 0;
93 }
94}
95
96#define SILENCE_DETECT(name, type) \
97static void silencedetect_##name(SilenceDetectContext *s, AVFrame *insamples, \
98 int nb_samples, int64_t nb_samples_notify, \
99 AVRational time_base) \
100{ \
101 const type *p = (const type *)insamples->data[0]; \
102 const type noise = s->noise; \
103 int i; \
104 \
105 for (i = 0; i < nb_samples; i++, p++) \
106 update(s, insamples, *p < noise && *p > -noise, \
107 nb_samples_notify, time_base); \
108}
109
110SILENCE_DETECT(dbl, double)
111SILENCE_DETECT(flt, float)
112SILENCE_DETECT(s32, int32_t)
113SILENCE_DETECT(s16, int16_t)
114
115static int config_input(AVFilterLink *inlink)
116{
117 AVFilterContext *ctx = inlink->dst;
118 SilenceDetectContext *s = ctx->priv;
119
120 switch (inlink->format) {
121 case AV_SAMPLE_FMT_DBL: s->silencedetect = silencedetect_dbl; break;
122 case AV_SAMPLE_FMT_FLT: s->silencedetect = silencedetect_flt; break;
123 case AV_SAMPLE_FMT_S32:
124 s->noise *= INT32_MAX;
125 s->silencedetect = silencedetect_s32;
126 break;
127 case AV_SAMPLE_FMT_S16:
128 s->noise *= INT16_MAX;
129 s->silencedetect = silencedetect_s16;
130 break;
131 }
132
133 return 0;
134}
135
136static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
137{
138 SilenceDetectContext *s = inlink->dst->priv;
139 const int nb_channels = inlink->channels;
140 const int srate = inlink->sample_rate;
141 const int nb_samples = insamples->nb_samples * nb_channels;
142 const int64_t nb_samples_notify = srate * s->duration * nb_channels;
143
144 // scale number of null samples to the new sample rate
145 if (s->last_sample_rate && s->last_sample_rate != srate)
146 s->nb_null_samples = srate * s->nb_null_samples / s->last_sample_rate;
147 s->last_sample_rate = srate;
148
149 // TODO: document metadata
150 s->silencedetect(s, insamples, nb_samples, nb_samples_notify,
151 inlink->time_base);
152
153 return ff_filter_frame(inlink->dst->outputs[0], insamples);
154}
155
156static int query_formats(AVFilterContext *ctx)
157{
158 AVFilterFormats *formats = NULL;
159 AVFilterChannelLayouts *layouts = NULL;
160 static const enum AVSampleFormat sample_fmts[] = {
161 AV_SAMPLE_FMT_DBL,
162 AV_SAMPLE_FMT_FLT,
163 AV_SAMPLE_FMT_S32,
164 AV_SAMPLE_FMT_S16,
165 AV_SAMPLE_FMT_NONE
166 };
167
168 layouts = ff_all_channel_layouts();
169 if (!layouts)
170 return AVERROR(ENOMEM);
171 ff_set_common_channel_layouts(ctx, layouts);
172
173 formats = ff_make_format_list(sample_fmts);
174 if (!formats)
175 return AVERROR(ENOMEM);
176 ff_set_common_formats(ctx, formats);
177
178 formats = ff_all_samplerates();
179 if (!formats)
180 return AVERROR(ENOMEM);
181 ff_set_common_samplerates(ctx, formats);
182
183 return 0;
184}
185
186static const AVFilterPad silencedetect_inputs[] = {
187 {
188 .name = "default",
189 .type = AVMEDIA_TYPE_AUDIO,
190 .config_props = config_input,
191 .filter_frame = filter_frame,
192 },
193 { NULL }
194};
195
196static const AVFilterPad silencedetect_outputs[] = {
197 {
198 .name = "default",
199 .type = AVMEDIA_TYPE_AUDIO,
200 },
201 { NULL }
202};
203
204AVFilter ff_af_silencedetect = {
205 .name = "silencedetect",
206 .description = NULL_IF_CONFIG_SMALL("Detect silence."),
207 .priv_size = sizeof(SilenceDetectContext),
208 .query_formats = query_formats,
209 .inputs = silencedetect_inputs,
210 .outputs = silencedetect_outputs,
211 .priv_class = &silencedetect_class,
212};