vflip: switch to filter_frame
[ffmpeg.git] / libavfilter / af_silencedetect.c
index 5ef5f96..f5fccc5 100644 (file)
@@ -23,7 +23,7 @@
  * Audio silence detector
  */
 
-#include "libavutil/audioconvert.h"
+#include "libavutil/channel_layout.h"
 #include "libavutil/opt.h"
 #include "libavutil/timestamp.h"
 #include "audio.h"
@@ -35,24 +35,25 @@ typedef struct {
     const AVClass *class;
     char *noise_str;            ///< noise option string
     double noise;               ///< noise amplitude ratio
-    int duration;               ///< minimum duration of silence until notification
+    double duration;            ///< minimum duration of silence until notification
     int64_t nb_null_samples;    ///< current number of continuous zero samples
     int64_t start;              ///< if silence is detected, this value contains the time of the first zero sample
     int last_sample_rate;       ///< last sample rate to check for sample rate changes
 } SilenceDetectContext;
 
 #define OFFSET(x) offsetof(SilenceDetectContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM
 static const AVOption silencedetect_options[] = {
-    { "n",         "set noise tolerance",              OFFSET(noise_str), AV_OPT_TYPE_STRING, {.str="-60dB"}, CHAR_MIN, CHAR_MAX },
-    { "noise",     "set noise tolerance",              OFFSET(noise_str), AV_OPT_TYPE_STRING, {.str="-60dB"}, CHAR_MIN, CHAR_MAX },
-    { "d",         "set minimum duration in seconds",  OFFSET(duration),  AV_OPT_TYPE_INT,    {.dbl=2},    0, INT_MAX},
-    { "duration",  "set minimum duration in seconds",  OFFSET(duration),  AV_OPT_TYPE_INT,    {.dbl=2},    0, INT_MAX},
+    { "n",         "set noise tolerance",              OFFSET(noise_str), AV_OPT_TYPE_STRING, {.str="-60dB"}, CHAR_MIN, CHAR_MAX, FLAGS },
+    { "noise",     "set noise tolerance",              OFFSET(noise_str), AV_OPT_TYPE_STRING, {.str="-60dB"}, CHAR_MIN, CHAR_MAX, FLAGS },
+    { "d",         "set minimum duration in seconds",  OFFSET(duration),  AV_OPT_TYPE_DOUBLE, {.dbl=2.},             0, 24*60*60, FLAGS },
+    { "duration",  "set minimum duration in seconds",  OFFSET(duration),  AV_OPT_TYPE_DOUBLE, {.dbl=2.},             0, 24*60*60, FLAGS },
     { NULL },
 };
 
 AVFILTER_DEFINE_CLASS(silencedetect);
 
-static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
+static av_cold int init(AVFilterContext *ctx, const char *args)
 {
     int ret;
     char *tail;
@@ -61,10 +62,8 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
     silence->class = &silencedetect_class;
     av_opt_set_defaults(silence);
 
-    if ((ret = av_set_options_string(silence, args, "=", ":")) < 0) {
-        av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
+    if ((ret = av_set_options_string(silence, args, "=", ":")) < 0)
         return ret;
-    }
 
     silence->noise = strtod(silence->noise_str, &tail);
     if (!strcmp(tail, "dB")) {
@@ -74,11 +73,18 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
                silence->noise_str);
         return AVERROR(EINVAL);
     }
+    av_opt_free(silence);
 
     return 0;
 }
 
-static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
+static char *get_metadata_val(AVFilterBufferRef *insamples, const char *key)
+{
+    AVDictionaryEntry *e = av_dict_get(insamples->metadata, key, NULL, 0);
+    return e && e->value ? e->value : NULL;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *insamples)
 {
     int i;
     SilenceDetectContext *silence = inlink->dst->priv;
@@ -94,6 +100,7 @@ static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
     silence->last_sample_rate = srate;
 
     // TODO: support more sample formats
+    // TODO: document metadata
     if (insamples->format == AV_SAMPLE_FMT_DBL) {
         double *p = (double *)insamples->data[0];
 
@@ -102,23 +109,30 @@ static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
                 if (!silence->start) {
                     silence->nb_null_samples++;
                     if (silence->nb_null_samples >= nb_samples_notify) {
-                        silence->start = insamples->pts - silence->duration / av_q2d(inlink->time_base);
-                        av_log(silence, AV_LOG_INFO,
-                               "silence_start: %s\n", av_ts2timestr(silence->start, &inlink->time_base));
+                        silence->start = insamples->pts - (int64_t)(silence->duration / av_q2d(inlink->time_base) + .5);
+                        av_dict_set(&insamples->metadata, "lavfi.silence_start",
+                                    av_ts2timestr(silence->start, &inlink->time_base), 0);
+                        av_log(silence, AV_LOG_INFO, "silence_start: %s\n",
+                               get_metadata_val(insamples, "lavfi.silence_start"));
                     }
                 }
             } else {
-                if (silence->start)
+                if (silence->start) {
+                    av_dict_set(&insamples->metadata, "lavfi.silence_end",
+                                av_ts2timestr(insamples->pts, &inlink->time_base), 0);
+                    av_dict_set(&insamples->metadata, "lavfi.silence_duration",
+                                av_ts2timestr(insamples->pts - silence->start, &inlink->time_base), 0);
                     av_log(silence, AV_LOG_INFO,
                            "silence_end: %s | silence_duration: %s\n",
-                           av_ts2timestr(insamples->pts,                  &inlink->time_base),
-                           av_ts2timestr(insamples->pts - silence->start, &inlink->time_base));
+                           get_metadata_val(insamples, "lavfi.silence_end"),
+                           get_metadata_val(insamples, "lavfi.silence_duration"));
+                }
                 silence->nb_null_samples = silence->start = 0;
             }
         }
     }
 
-    ff_filter_samples(inlink->dst->outputs[0], insamples);
+    return ff_filter_frame(inlink->dst->outputs[0], insamples);
 }
 
 static int query_formats(AVFilterContext *ctx)
@@ -148,23 +162,31 @@ static int query_formats(AVFilterContext *ctx)
     return 0;
 }
 
+static const AVFilterPad silencedetect_inputs[] = {
+    {
+        .name             = "default",
+        .type             = AVMEDIA_TYPE_AUDIO,
+        .get_audio_buffer = ff_null_get_audio_buffer,
+        .filter_frame     = filter_frame,
+    },
+    { NULL }
+};
+
+static const AVFilterPad silencedetect_outputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_AUDIO,
+    },
+    { NULL }
+};
+
 AVFilter avfilter_af_silencedetect = {
     .name          = "silencedetect",
     .description   = NULL_IF_CONFIG_SMALL("Detect silence."),
     .priv_size     = sizeof(SilenceDetectContext),
     .init          = init,
     .query_formats = query_formats,
-
-    .inputs = (const AVFilterPad[]) {
-        { .name             = "default",
-          .type             = AVMEDIA_TYPE_AUDIO,
-          .get_audio_buffer = ff_null_get_audio_buffer,
-          .filter_samples   = filter_samples, },
-        { .name = NULL }
-    },
-    .outputs = (const AVFilterPad[]) {
-        { .name = "default",
-          .type = AVMEDIA_TYPE_AUDIO, },
-        { .name = NULL }
-    },
+    .inputs        = silencedetect_inputs,
+    .outputs       = silencedetect_outputs,
+    .priv_class    = &silencedetect_class,
 };