Browse code

avfilter: add metadata filters

Signed-off-by: Paul B Mahol <onemda@gmail.com>

Paul B Mahol authored on 2016/02/06 19:19:45
Showing 6 changed files
... ...
@@ -66,6 +66,7 @@ version <next>:
66 66
 - nnedi deinterlacer
67 67
 - streamselect video and astreamselect audio filter
68 68
 - swaprect filter
69
+- metadata video and ametadata audio filter
69 70
 
70 71
 
71 72
 version 2.8:
... ...
@@ -8445,6 +8445,100 @@ format=rgb24,mergeplanes=0x000102:yuv444p
8445 8445
 @end example
8446 8446
 @end itemize
8447 8447
 
8448
+@section metadata, ametadata
8449
+
8450
+Manipulate frame metadata.
8451
+
8452
+This filter accepts the following options:
8453
+
8454
+@table @option
8455
+@item mode
8456
+Set mode of operation of the filter.
8457
+
8458
+Can be one of the following:
8459
+
8460
+@table @samp
8461
+@item select
8462
+If both @code{value} and @code{key} is set, select frames
8463
+which have such metadata. If only @code{key} is set, select
8464
+every frame that has such key in metadata.
8465
+
8466
+@item add
8467
+Add new metadata @code{key} and @code{value}. If key is already available
8468
+do nothing.
8469
+
8470
+@item modify
8471
+Modify value of already present key.
8472
+
8473
+@item delete
8474
+If @code{value} is set, delete only keys that have such value.
8475
+Otherwise, delete key.
8476
+
8477
+@item print
8478
+Print key and its value if metadata was found. If @code{key} is not set print all
8479
+metadata values available in frame.
8480
+@end table
8481
+
8482
+@item key
8483
+Set key used with all modes. Must be set for all modes except @code{print}.
8484
+
8485
+@item value
8486
+Set metadata value which will be used. This option is mandatory for
8487
+@code{modify} and @code{add} mode.
8488
+
8489
+@item length
8490
+Set length of how many characters of two metadata values need to match to be
8491
+considered same. Default is all available characters.
8492
+
8493
+@item function
8494
+Which function to use when comparing metadata value and @code{value}.
8495
+
8496
+Can be one of following:
8497
+
8498
+@table @samp
8499
+@item string
8500
+Values are interpreted as strings, returns true if @code{value} is same as metadata value up
8501
+to N chars as set in @code{length} option.
8502
+
8503
+@item less
8504
+Values are interpreted as floats, returns true if @code{value} is less than metadata value.
8505
+
8506
+@item equal
8507
+Values are interpreted as floats, returns true if @code{value} is equal with metadata value.
8508
+
8509
+@item greater
8510
+Values are interpreted as floats, returns true if @code{value} is greater than metadata value.
8511
+
8512
+@item expr
8513
+Values are interpreted as floats, returns true if expression from option @code{expr}
8514
+evaluates to true.
8515
+@end table
8516
+
8517
+@item expr
8518
+Set expression which is used when @code{function} is set to @code{expr}.
8519
+The expression is evaluated through the eval API and can contain the following
8520
+constants:
8521
+
8522
+@table @option
8523
+@item VALUE1
8524
+Float representation of @code{value} from metadata key.
8525
+
8526
+@item VALUE2
8527
+Float representation of @code{value} as supplied by user in @code{value} option.
8528
+@end table
8529
+@end table
8530
+
8531
+@subsection Examples
8532
+
8533
+@itemize
8534
+@item
8535
+Print all metadata values for frames with key @code{lavfi.singnalstats.YDIF} with values
8536
+between 0 and 1.
8537
+@example
8538
+@end example
8539
+signalstats,metadata=print:key=lavfi.signalstats.YDIF:function=expr:expr='between(VALUE1,0,1)'
8540
+@end itemize
8541
+
8448 8542
 @section mpdecimate
8449 8543
 
8450 8544
 Drop frames that do not differ greatly from the previous frame in
... ...
@@ -39,6 +39,7 @@ OBJS-$(CONFIG_AINTERLEAVE_FILTER)            += f_interleave.o
39 39
 OBJS-$(CONFIG_ALIMITER_FILTER)               += af_alimiter.o
40 40
 OBJS-$(CONFIG_ALLPASS_FILTER)                += af_biquads.o
41 41
 OBJS-$(CONFIG_AMERGE_FILTER)                 += af_amerge.o
42
+OBJS-$(CONFIG_AMETADATA_FILTER)              += f_metadata.o
42 43
 OBJS-$(CONFIG_AMIX_FILTER)                   += af_amix.o
43 44
 OBJS-$(CONFIG_ANULL_FILTER)                  += af_anull.o
44 45
 OBJS-$(CONFIG_APAD_FILTER)                   += af_apad.o
... ...
@@ -186,6 +187,7 @@ OBJS-$(CONFIG_LUTYUV_FILTER)                 += vf_lut.o
186 186
 OBJS-$(CONFIG_MASKEDMERGE_FILTER)            += vf_maskedmerge.o framesync.o
187 187
 OBJS-$(CONFIG_MCDEINT_FILTER)                += vf_mcdeint.o
188 188
 OBJS-$(CONFIG_MERGEPLANES_FILTER)            += vf_mergeplanes.o framesync.o
189
+OBJS-$(CONFIG_METADATA_FILTER)               += f_metadata.o
189 190
 OBJS-$(CONFIG_MPDECIMATE_FILTER)             += vf_mpdecimate.o
190 191
 OBJS-$(CONFIG_NEGATE_FILTER)                 += vf_lut.o
191 192
 OBJS-$(CONFIG_NNEDI_FILTER)                  += vf_nnedi.o
... ...
@@ -59,6 +59,7 @@ void avfilter_register_all(void)
59 59
     REGISTER_FILTER(ALIMITER,       alimiter,       af);
60 60
     REGISTER_FILTER(ALLPASS,        allpass,        af);
61 61
     REGISTER_FILTER(AMERGE,         amerge,         af);
62
+    REGISTER_FILTER(AMETADATA,      ametadata,      af);
62 63
     REGISTER_FILTER(AMIX,           amix,           af);
63 64
     REGISTER_FILTER(ANEQUALIZER,    anequalizer,    af);
64 65
     REGISTER_FILTER(ANULL,          anull,          af);
... ...
@@ -207,6 +208,7 @@ void avfilter_register_all(void)
207 207
     REGISTER_FILTER(MASKEDMERGE,    maskedmerge,    vf);
208 208
     REGISTER_FILTER(MCDEINT,        mcdeint,        vf);
209 209
     REGISTER_FILTER(MERGEPLANES,    mergeplanes,    vf);
210
+    REGISTER_FILTER(METADATA,       metadata,       vf);
210 211
     REGISTER_FILTER(MPDECIMATE,     mpdecimate,     vf);
211 212
     REGISTER_FILTER(NEGATE,         negate,         vf);
212 213
     REGISTER_FILTER(NNEDI,          nnedi,          vf);
213 214
new file mode 100644
... ...
@@ -0,0 +1,341 @@
0
+/*
1
+ * Copyright (c) 2016 Paul B Mahol
2
+ *
3
+ * This file is part of FFmpeg.
4
+ *
5
+ * FFmpeg is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * FFmpeg is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with FFmpeg; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+/**
21
+ * @file
22
+ * filter for manipulating frame metadata
23
+ */
24
+
25
+#include "libavutil/avassert.h"
26
+#include "libavutil/avstring.h"
27
+#include "libavutil/eval.h"
28
+#include "libavutil/fifo.h"
29
+#include "libavutil/internal.h"
30
+#include "libavutil/opt.h"
31
+#include "libavutil/pixelutils.h"
32
+#include "avfilter.h"
33
+#include "audio.h"
34
+#include "formats.h"
35
+#include "internal.h"
36
+#include "video.h"
37
+
38
+enum MetadataMode {
39
+    METADATA_SELECT,
40
+    METADATA_ADD,
41
+    METADATA_MODIFY,
42
+    METADATA_DELETE,
43
+    METADATA_PRINT,
44
+    METADATA_NB
45
+};
46
+
47
+enum MetadataFunction {
48
+    METADATAF_STRING,
49
+    METADATAF_LESS,
50
+    METADATAF_EQUAL,
51
+    METADATAF_GREATER,
52
+    METADATAF_EXPR,
53
+    METADATAF_NB
54
+};
55
+
56
+static const char *const var_names[] = {
57
+    "VALUE1",
58
+    "VALUE2",
59
+    NULL
60
+};
61
+
62
+enum var_name {
63
+    VAR_VALUE1,
64
+    VAR_VALUE2,
65
+    VAR_VARS_NB
66
+};
67
+
68
+typedef struct MetadataContext {
69
+    const AVClass *class;
70
+
71
+    int mode;
72
+    char *key;
73
+    char *value;
74
+    int length;
75
+    int function;
76
+
77
+    char *expr_str;
78
+    AVExpr *expr;
79
+    double var_values[VAR_VARS_NB];
80
+
81
+    int (*compare)(struct MetadataContext *s,
82
+                   const char *value1, const char *value2, size_t length);
83
+} MetadataContext;
84
+
85
+#define OFFSET(x) offsetof(MetadataContext, x)
86
+#define DEFINE_OPTIONS(filt_name, FLAGS)                            \
87
+static const AVOption filt_name##_options[] = {                     \
88
+    { "mode", "set a mode of operation", OFFSET(mode),   AV_OPT_TYPE_INT,    {.i64 = 0 }, 0, METADATA_NB-1, FLAGS, "mode" }, \
89
+    {   "select", "select frame",        0,              AV_OPT_TYPE_CONST,  {.i64 = METADATA_SELECT }, 0, 0, FLAGS, "mode" }, \
90
+    {   "add",    "add new metadata",    0,              AV_OPT_TYPE_CONST,  {.i64 = METADATA_ADD },    0, 0, FLAGS, "mode" }, \
91
+    {   "modify", "modify metadata",     0,              AV_OPT_TYPE_CONST,  {.i64 = METADATA_MODIFY }, 0, 0, FLAGS, "mode" }, \
92
+    {   "delete", "delete metadata",     0,              AV_OPT_TYPE_CONST,  {.i64 = METADATA_DELETE }, 0, 0, FLAGS, "mode" }, \
93
+    {   "print",  "print metadata",      0,              AV_OPT_TYPE_CONST,  {.i64 = METADATA_PRINT },  0, 0, FLAGS, "mode" }, \
94
+    { "key",   "set metadata key",       OFFSET(key),    AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \
95
+    { "value", "set metadata value",     OFFSET(value),  AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \
96
+    { "function", "function for comparing values", OFFSET(function), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, METADATAF_NB-1, FLAGS, "function" }, \
97
+    {   "string",  NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_STRING  }, 0, 3, FLAGS, "function" }, \
98
+    {   "less",    NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_LESS    }, 0, 3, FLAGS, "function" }, \
99
+    {   "equal",   NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EQUAL   }, 0, 3, FLAGS, "function" }, \
100
+    {   "greater", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_GREATER }, 0, 3, FLAGS, "function" }, \
101
+    {   "expr",    NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EXPR    }, 0, 3, FLAGS, "function" }, \
102
+    { "expr", "set expression for expr function", OFFSET(expr_str), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \
103
+    { "length", "compare up to N chars for string function", OFFSET(length), AV_OPT_TYPE_INT,    {.i64 = INT_MAX }, 1, INT_MAX, FLAGS }, \
104
+    { NULL }                                                            \
105
+}
106
+
107
+static int string(MetadataContext *s, const char *value1, const char *value2, size_t length)
108
+{
109
+    return !strncmp(value1, value2, length);
110
+}
111
+
112
+static int equal(MetadataContext *s, const char *value1, const char *value2, size_t length)
113
+{
114
+    float f1, f2;
115
+
116
+    if (sscanf(value1, "%f", &f1) + sscanf(value2, "%f", &f2) != 2)
117
+        return 0;
118
+
119
+    return f1 == f2;
120
+}
121
+
122
+static int less(MetadataContext *s, const char *value1, const char *value2, size_t length)
123
+{
124
+    float f1, f2;
125
+
126
+    if (sscanf(value1, "%f", &f1) + sscanf(value2, "%f", &f2) != 2)
127
+        return 0;
128
+
129
+    return f1 > f2;
130
+}
131
+
132
+static int greater(MetadataContext *s, const char *value1, const char *value2, size_t length)
133
+{
134
+    float f1, f2;
135
+
136
+    if (sscanf(value1, "%f", &f1) + sscanf(value2, "%f", &f2) != 2)
137
+        return 0;
138
+
139
+    return f1 < f2;
140
+}
141
+
142
+static int parse_expr(MetadataContext *s, const char *value1, const char *value2, size_t length)
143
+{
144
+    double f1, f2;
145
+
146
+    if (sscanf(value1, "%lf", &f1) + sscanf(value2, "%lf", &f2) != 2)
147
+        return 0;
148
+
149
+    s->var_values[VAR_VALUE1] = f1;
150
+    s->var_values[VAR_VALUE1] = f2;
151
+
152
+    return av_expr_eval(s->expr, s->var_values, NULL);
153
+}
154
+
155
+static av_cold int init(AVFilterContext *ctx)
156
+{
157
+    MetadataContext *s = ctx->priv;
158
+    int ret;
159
+
160
+    if (!s->key && s->mode != METADATA_PRINT) {
161
+        av_log(ctx, AV_LOG_WARNING, "Metadata key must be set\n");
162
+        return AVERROR(EINVAL);
163
+    }
164
+
165
+    if ((s->mode == METADATA_MODIFY ||
166
+        s->mode == METADATA_ADD) && !s->value) {
167
+        av_log(ctx, AV_LOG_WARNING, "Missing metadata value\n");
168
+        return AVERROR(EINVAL);
169
+    }
170
+
171
+    switch (s->function) {
172
+    case METADATAF_STRING:
173
+        s->compare = string;
174
+        break;
175
+    case METADATAF_LESS:
176
+        s->compare = less;
177
+        break;
178
+    case METADATAF_EQUAL:
179
+        s->compare = equal;
180
+        break;
181
+    case METADATAF_GREATER:
182
+        s->compare = greater;
183
+        break;
184
+    case METADATAF_EXPR:
185
+        s->compare = parse_expr;
186
+        break;
187
+    default:
188
+        av_assert0(0);
189
+    };
190
+
191
+    if (s->function == METADATAF_EXPR) {
192
+        if (!s->expr_str) {
193
+            av_log(ctx, AV_LOG_WARNING, "expr option not set\n");
194
+            return AVERROR(EINVAL);
195
+        }
196
+        if ((ret = av_expr_parse(&s->expr, s->expr_str,
197
+                                 var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
198
+            av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", s->expr_str);
199
+            return ret;
200
+        }
201
+    }
202
+
203
+    return 0;
204
+}
205
+
206
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
207
+{
208
+    AVFilterContext *ctx = inlink->dst;
209
+    AVFilterLink *outlink = ctx->outputs[0];
210
+    MetadataContext *s = ctx->priv;
211
+    AVDictionary *metadata = av_frame_get_metadata(frame);
212
+    AVDictionaryEntry *e;
213
+
214
+    if (!metadata)
215
+        return ff_filter_frame(outlink, frame);
216
+
217
+    e = av_dict_get(metadata, !s->key ? "" : s->key, NULL,
218
+                    !s->key ? AV_DICT_IGNORE_SUFFIX: 0);
219
+
220
+    switch (s->mode) {
221
+    case METADATA_SELECT:
222
+        if (!s->value && e && e->value) {
223
+            return ff_filter_frame(outlink, frame);
224
+        } else if (s->value && e && e->value &&
225
+                   s->compare(s, s->value, e->value, s->length)) {
226
+            return ff_filter_frame(outlink, frame);
227
+        }
228
+        break;
229
+    case METADATA_ADD:
230
+        if (e && e->value) {
231
+            ;
232
+        } else {
233
+            av_dict_set(&metadata, s->key, s->value, 0);
234
+        }
235
+        return ff_filter_frame(outlink, frame);
236
+        break;
237
+    case METADATA_MODIFY:
238
+        if (e && e->value) {
239
+            av_dict_set(&metadata, s->key, s->value, 0);
240
+        }
241
+        return ff_filter_frame(outlink, frame);
242
+        break;
243
+    case METADATA_PRINT:
244
+        if (!s->key && e) {
245
+            av_log(ctx, AV_LOG_INFO, "frame %"PRId64" pts %"PRId64"\n", inlink->frame_count, frame->pts);
246
+            av_log(ctx, AV_LOG_INFO, "%s=%s\n", e->key, e->value);
247
+            while ((e = av_dict_get(metadata, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL) {
248
+                av_log(ctx, AV_LOG_INFO, "%s=%s\n", e->key, e->value);
249
+            }
250
+        } else if (e && e->value && (!s->value || (e->value && s->compare(s, s->value, e->value, s->length)))) {
251
+            av_log(ctx, AV_LOG_INFO, "frame %"PRId64" pts %"PRId64"\n", inlink->frame_count, frame->pts);
252
+            av_log(ctx, AV_LOG_INFO, "%s=%s\n", s->key, e->value);
253
+        }
254
+        return ff_filter_frame(outlink, frame);
255
+        break;
256
+    case METADATA_DELETE:
257
+        if (e && e->value && s->value && s->compare(s, s->value, e->value, s->length)) {
258
+            av_dict_set(&metadata, s->key, NULL, 0);
259
+        } else if (e && e->value) {
260
+            av_dict_set(&metadata, s->key, NULL, 0);
261
+        }
262
+        return ff_filter_frame(outlink, frame);
263
+        break;
264
+    default:
265
+        av_assert0(0);
266
+    };
267
+
268
+    av_frame_free(&frame);
269
+
270
+    return 0;
271
+}
272
+
273
+#if CONFIG_AMETADATA_FILTER
274
+
275
+DEFINE_OPTIONS(ametadata, AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM);
276
+AVFILTER_DEFINE_CLASS(ametadata);
277
+
278
+static const AVFilterPad ainputs[] = {
279
+    {
280
+        .name         = "default",
281
+        .type         = AVMEDIA_TYPE_AUDIO,
282
+        .filter_frame = filter_frame,
283
+    },
284
+    { NULL }
285
+};
286
+
287
+static const AVFilterPad aoutputs[] = {
288
+    {
289
+        .name = "default",
290
+        .type = AVMEDIA_TYPE_AUDIO,
291
+    },
292
+    { NULL }
293
+};
294
+
295
+AVFilter ff_af_ametadata = {
296
+    .name          = "ametadata",
297
+    .description   = NULL_IF_CONFIG_SMALL("Manipulate audio frame metadata."),
298
+    .priv_size     = sizeof(MetadataContext),
299
+    .priv_class    = &ametadata_class,
300
+    .init          = init,
301
+    .query_formats = ff_query_formats_all,
302
+    .inputs        = ainputs,
303
+    .outputs       = aoutputs,
304
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
305
+};
306
+#endif /* CONFIG_AMETADATA_FILTER */
307
+
308
+#if CONFIG_METADATA_FILTER
309
+
310
+DEFINE_OPTIONS(metadata, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM);
311
+AVFILTER_DEFINE_CLASS(metadata);
312
+
313
+static const AVFilterPad inputs[] = {
314
+    {
315
+        .name         = "default",
316
+        .type         = AVMEDIA_TYPE_VIDEO,
317
+        .filter_frame = filter_frame,
318
+    },
319
+    { NULL }
320
+};
321
+
322
+static const AVFilterPad outputs[] = {
323
+    {
324
+        .name = "default",
325
+        .type = AVMEDIA_TYPE_VIDEO,
326
+    },
327
+    { NULL }
328
+};
329
+
330
+AVFilter ff_vf_metadata = {
331
+    .name        = "metadata",
332
+    .description = NULL_IF_CONFIG_SMALL("Manipulate video frame metadata."),
333
+    .priv_size   = sizeof(MetadataContext),
334
+    .priv_class  = &metadata_class,
335
+    .init        = init,
336
+    .inputs      = inputs,
337
+    .outputs     = outputs,
338
+    .flags       = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
339
+};
340
+#endif /* CONFIG_METADATA_FILTER */
... ...
@@ -30,7 +30,7 @@
30 30
 #include "libavutil/version.h"
31 31
 
32 32
 #define LIBAVFILTER_VERSION_MAJOR   6
33
-#define LIBAVFILTER_VERSION_MINOR  30
33
+#define LIBAVFILTER_VERSION_MINOR  31
34 34
 #define LIBAVFILTER_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \