FFmpeg
f_metadata.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016 Paul B Mahol
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  * filter for manipulating frame metadata
24  */
25 
26 #include <float.h>
27 
28 #include "libavutil/avassert.h"
29 #include "libavutil/avstring.h"
30 #include "libavutil/eval.h"
31 #include "libavutil/internal.h"
32 #include "libavutil/opt.h"
33 #include "libavutil/timestamp.h"
34 #include "libavformat/avio.h"
35 #include "avfilter.h"
36 #include "audio.h"
37 #include "formats.h"
38 #include "internal.h"
39 #include "video.h"
40 
48 };
49 
59 };
60 
61 static const char *const var_names[] = {
62  "VALUE1",
63  "VALUE2",
64  NULL
65 };
66 
67 enum var_name {
71 };
72 
73 typedef struct MetadataContext {
74  const AVClass *class;
75 
76  int mode;
77  char *key;
78  char *value;
79  int function;
80 
81  char *expr_str;
84 
86  char *file_str;
87 
89  const char *value1, const char *value2);
90  void (*print)(AVFilterContext *ctx, const char *msg, ...) av_printf_format(2, 3);
92 
93 #define OFFSET(x) offsetof(MetadataContext, x)
94 #define DEFINE_OPTIONS(filt_name, FLAGS) \
95 static const AVOption filt_name##_options[] = { \
96  { "mode", "set a mode of operation", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, METADATA_NB-1, FLAGS, "mode" }, \
97  { "select", "select frame", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_SELECT }, 0, 0, FLAGS, "mode" }, \
98  { "add", "add new metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_ADD }, 0, 0, FLAGS, "mode" }, \
99  { "modify", "modify metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_MODIFY }, 0, 0, FLAGS, "mode" }, \
100  { "delete", "delete metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_DELETE }, 0, 0, FLAGS, "mode" }, \
101  { "print", "print metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_PRINT }, 0, 0, FLAGS, "mode" }, \
102  { "key", "set metadata key", OFFSET(key), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \
103  { "value", "set metadata value", OFFSET(value), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \
104  { "function", "function for comparing values", OFFSET(function), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, METADATAF_NB-1, FLAGS, "function" }, \
105  { "same_str", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_SAME_STR }, 0, 3, FLAGS, "function" }, \
106  { "starts_with", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_STARTS_WITH }, 0, 0, FLAGS, "function" }, \
107  { "less", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_LESS }, 0, 3, FLAGS, "function" }, \
108  { "equal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EQUAL }, 0, 3, FLAGS, "function" }, \
109  { "greater", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_GREATER }, 0, 3, FLAGS, "function" }, \
110  { "expr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EXPR }, 0, 3, FLAGS, "function" }, \
111  { "ends_with", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_ENDS_WITH }, 0, 0, FLAGS, "function" }, \
112  { "expr", "set expression for expr function", OFFSET(expr_str), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \
113  { "file", "set file where to print metadata information", OFFSET(file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, \
114  { NULL } \
115 }
116 
117 static int same_str(MetadataContext *s, const char *value1, const char *value2)
118 {
119  return !strcmp(value1, value2);
120 }
121 
122 static int starts_with(MetadataContext *s, const char *value1, const char *value2)
123 {
124  return !strncmp(value1, value2, strlen(value2));
125 }
126 
127 static int ends_with(MetadataContext *s, const char *value1, const char *value2)
128 {
129  const int len1 = strlen(value1);
130  const int len2 = strlen(value2);
131 
132  return !strncmp(value1 + FFMAX(len1 - len2, 0), value2, len2);
133 }
134 
135 static int equal(MetadataContext *s, const char *value1, const char *value2)
136 {
137  float f1, f2;
138 
139  if (sscanf(value1, "%f", &f1) + sscanf(value2, "%f", &f2) != 2)
140  return 0;
141 
142  return fabsf(f1 - f2) < FLT_EPSILON;
143 }
144 
145 static int less(MetadataContext *s, const char *value1, const char *value2)
146 {
147  float f1, f2;
148 
149  if (sscanf(value1, "%f", &f1) + sscanf(value2, "%f", &f2) != 2)
150  return 0;
151 
152  return (f1 - f2) < FLT_EPSILON;
153 }
154 
155 static int greater(MetadataContext *s, const char *value1, const char *value2)
156 {
157  float f1, f2;
158 
159  if (sscanf(value1, "%f", &f1) + sscanf(value2, "%f", &f2) != 2)
160  return 0;
161 
162  return (f2 - f1) < FLT_EPSILON;
163 }
164 
165 static int parse_expr(MetadataContext *s, const char *value1, const char *value2)
166 {
167  double f1, f2;
168 
169  if (sscanf(value1, "%lf", &f1) + sscanf(value2, "%lf", &f2) != 2)
170  return 0;
171 
172  s->var_values[VAR_VALUE1] = f1;
173  s->var_values[VAR_VALUE2] = f2;
174 
175  return av_expr_eval(s->expr, s->var_values, NULL);
176 }
177 
178 static void print_log(AVFilterContext *ctx, const char *msg, ...)
179 {
180  va_list argument_list;
181 
182  va_start(argument_list, msg);
183  if (msg)
184  av_vlog(ctx, AV_LOG_INFO, msg, argument_list);
185  va_end(argument_list);
186 }
187 
188 static void print_file(AVFilterContext *ctx, const char *msg, ...)
189 {
190  MetadataContext *s = ctx->priv;
191  va_list argument_list;
192 
193  va_start(argument_list, msg);
194  if (msg) {
195  char buf[128];
196  vsnprintf(buf, sizeof(buf), msg, argument_list);
197  avio_write(s->avio_context, buf, av_strnlen(buf, sizeof(buf)));
198  }
199  va_end(argument_list);
200 }
201 
203 {
204  MetadataContext *s = ctx->priv;
205  int ret;
206 
207  if (!s->key && s->mode != METADATA_PRINT && s->mode != METADATA_DELETE) {
208  av_log(ctx, AV_LOG_WARNING, "Metadata key must be set\n");
209  return AVERROR(EINVAL);
210  }
211 
212  if ((s->mode == METADATA_MODIFY ||
213  s->mode == METADATA_ADD) && !s->value) {
214  av_log(ctx, AV_LOG_WARNING, "Missing metadata value\n");
215  return AVERROR(EINVAL);
216  }
217 
218  switch (s->function) {
219  case METADATAF_SAME_STR:
220  s->compare = same_str;
221  break;
223  s->compare = starts_with;
224  break;
225  case METADATAF_ENDS_WITH:
226  s->compare = ends_with;
227  break;
228  case METADATAF_LESS:
229  s->compare = less;
230  break;
231  case METADATAF_EQUAL:
232  s->compare = equal;
233  break;
234  case METADATAF_GREATER:
235  s->compare = greater;
236  break;
237  case METADATAF_EXPR:
238  s->compare = parse_expr;
239  break;
240  default:
241  av_assert0(0);
242  };
243 
244  if (s->function == METADATAF_EXPR) {
245  if (!s->expr_str) {
246  av_log(ctx, AV_LOG_WARNING, "expr option not set\n");
247  return AVERROR(EINVAL);
248  }
249  if ((ret = av_expr_parse(&s->expr, s->expr_str,
250  var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
251  av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", s->expr_str);
252  return ret;
253  }
254  }
255 
256  if (s->mode == METADATA_PRINT && s->file_str) {
257  s->print = print_file;
258  } else {
259  s->print = print_log;
260  }
261 
262  s->avio_context = NULL;
263  if (s->file_str) {
264  if (!strcmp("-", s->file_str)) {
265  ret = avio_open(&s->avio_context, "pipe:1", AVIO_FLAG_WRITE);
266  } else {
268  }
269 
270  if (ret < 0) {
271  char buf[128];
272  av_strerror(ret, buf, sizeof(buf));
273  av_log(ctx, AV_LOG_ERROR, "Could not open %s: %s\n",
274  s->file_str, buf);
275  return ret;
276  }
277  }
278 
279  return 0;
280 }
281 
283 {
284  MetadataContext *s = ctx->priv;
285 
286  av_expr_free(s->expr);
287  s->expr = NULL;
288  if (s->avio_context) {
290  }
291 }
292 
294 {
295  AVFilterContext *ctx = inlink->dst;
296  AVFilterLink *outlink = ctx->outputs[0];
297  MetadataContext *s = ctx->priv;
298  AVDictionary **metadata = &frame->metadata;
300 
301  if (!*metadata)
302  return ff_filter_frame(outlink, frame);
303 
304  e = av_dict_get(*metadata, !s->key ? "" : s->key, NULL,
305  !s->key ? AV_DICT_IGNORE_SUFFIX: 0);
306 
307  switch (s->mode) {
308  case METADATA_SELECT:
309  if (!s->value && e && e->value) {
310  return ff_filter_frame(outlink, frame);
311  } else if (s->value && e && e->value &&
312  s->compare(s, e->value, s->value)) {
313  return ff_filter_frame(outlink, frame);
314  }
315  break;
316  case METADATA_ADD:
317  if (e && e->value) {
318  ;
319  } else {
320  av_dict_set(metadata, s->key, s->value, 0);
321  }
322  return ff_filter_frame(outlink, frame);
323  case METADATA_MODIFY:
324  if (e && e->value) {
325  av_dict_set(metadata, s->key, s->value, 0);
326  }
327  return ff_filter_frame(outlink, frame);
328  case METADATA_PRINT:
329  if (!s->key && e) {
330  s->print(ctx, "frame:%-4"PRId64" pts:%-7s pts_time:%s\n",
331  inlink->frame_count_out, av_ts2str(frame->pts), av_ts2timestr(frame->pts, &inlink->time_base));
332  s->print(ctx, "%s=%s\n", e->key, e->value);
333  while ((e = av_dict_get(*metadata, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL) {
334  s->print(ctx, "%s=%s\n", e->key, e->value);
335  }
336  } else if (e && e->value && (!s->value || (e->value && s->compare(s, e->value, s->value)))) {
337  s->print(ctx, "frame:%-4"PRId64" pts:%-7s pts_time:%s\n",
338  inlink->frame_count_out, av_ts2str(frame->pts), av_ts2timestr(frame->pts, &inlink->time_base));
339  s->print(ctx, "%s=%s\n", s->key, e->value);
340  }
341  return ff_filter_frame(outlink, frame);
342  case METADATA_DELETE:
343  if (!s->key) {
344  av_dict_free(metadata);
345  } else if (e && e->value && (!s->value || s->compare(s, e->value, s->value))) {
346  av_dict_set(metadata, s->key, NULL, 0);
347  }
348  return ff_filter_frame(outlink, frame);
349  default:
350  av_assert0(0);
351  };
352 
353  av_frame_free(&frame);
354 
355  return 0;
356 }
357 
358 #if CONFIG_AMETADATA_FILTER
359 
361 AVFILTER_DEFINE_CLASS(ametadata);
362 
363 static const AVFilterPad ainputs[] = {
364  {
365  .name = "default",
366  .type = AVMEDIA_TYPE_AUDIO,
367  .filter_frame = filter_frame,
368  },
369  { NULL }
370 };
371 
372 static const AVFilterPad aoutputs[] = {
373  {
374  .name = "default",
375  .type = AVMEDIA_TYPE_AUDIO,
376  },
377  { NULL }
378 };
379 
381  .name = "ametadata",
382  .description = NULL_IF_CONFIG_SMALL("Manipulate audio frame metadata."),
383  .priv_size = sizeof(MetadataContext),
384  .priv_class = &ametadata_class,
385  .init = init,
386  .uninit = uninit,
387  .inputs = ainputs,
388  .outputs = aoutputs,
390 };
391 #endif /* CONFIG_AMETADATA_FILTER */
392 
393 #if CONFIG_METADATA_FILTER
394 
396 AVFILTER_DEFINE_CLASS(metadata);
397 
398 static const AVFilterPad inputs[] = {
399  {
400  .name = "default",
401  .type = AVMEDIA_TYPE_VIDEO,
402  .filter_frame = filter_frame,
403  },
404  { NULL }
405 };
406 
407 static const AVFilterPad outputs[] = {
408  {
409  .name = "default",
410  .type = AVMEDIA_TYPE_VIDEO,
411  },
412  { NULL }
413 };
414 
416  .name = "metadata",
417  .description = NULL_IF_CONFIG_SMALL("Manipulate video frame metadata."),
418  .priv_size = sizeof(MetadataContext),
419  .priv_class = &metadata_class,
420  .init = init,
421  .uninit = uninit,
422  .inputs = inputs,
423  .outputs = outputs,
425 };
426 #endif /* CONFIG_METADATA_FILTER */
int avio_open(AVIOContext **s, const char *url, int flags)
Create and initialize a AVIOContext for accessing the resource indicated by url.
Definition: aviobuf.c:1187
#define NULL
Definition: coverity.c:32
void(* print)(AVFilterContext *ctx, const char *msg,...) av_printf_format(2
Definition: f_metadata.c:90
Bytestream IO Context.
Definition: avio.h:161
Buffered I/O operations.
static int starts_with(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:122
This structure describes decoded (raw) audio or video data.
Definition: frame.h:295
static int parse_expr(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:165
AVFilter ff_vf_metadata
size_t static size_t av_strnlen(const char *s, size_t len)
Get the count of continuous non zero chars starting from the beginning.
Definition: avstring.h:140
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:182
Main libavfilter public API header.
static const char *const var_names[]
Definition: f_metadata.c:61
static av_cold int init(AVFilterContext *ctx)
Definition: f_metadata.c:202
#define AV_OPT_FLAG_AUDIO_PARAM
Definition: opt.h:278
#define vsnprintf
Definition: snprintf.h:36
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:675
#define DEFINE_OPTIONS(filt_name, FLAGS)
Definition: f_metadata.c:94
int(* compare)(struct MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:88
int av_expr_parse(AVExpr **expr, const char *s, const char *const *const_names, const char *const *func1_names, double(*const *funcs1)(void *, double), const char *const *func2_names, double(*const *funcs2)(void *, double, double), int log_offset, void *log_ctx)
Parse an expression.
Definition: eval.c:683
#define AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
Some filters support a generic "enable" expression option that can be used to enable or disable a fil...
Definition: avfilter.h:125
const char * name
Pad name.
Definition: internal.h:60
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:37
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1093
#define av_cold
Definition: attributes.h:82
AVOptions.
timestamp utils, mostly useful for debugging/logging purposes
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
Definition: frame.h:388
Definition: eval.c:157
AVDictionaryEntry * av_dict_get(const AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags)
Get a dictionary entry with matching key.
Definition: dict.c:40
AVIOContext * avio_context
Definition: f_metadata.c:85
AVDictionary * metadata
metadata.
Definition: frame.h:581
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition: aviobuf.c:218
#define av_log(a,...)
char * expr_str
Definition: f_metadata.c:81
A filter pad used for either input or output.
Definition: internal.h:54
static int same_str(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:117
double var_values[VAR_VARS_NB]
Definition: f_metadata.c:83
char * file_str
Definition: f_metadata.c:86
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
static void print_file(AVFilterContext *ctx, const char *msg,...)
Definition: f_metadata.c:188
#define AV_OPT_FLAG_FILTERING_PARAM
a generic parameter which can be set by the user for filtering
Definition: opt.h:292
static void print_log(AVFilterContext *ctx, const char *msg,...)
Definition: f_metadata.c:178
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:202
#define av_ts2timestr(ts, tb)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition: timestamp.h:76
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:186
void * priv
private data for use by the filter
Definition: avfilter.h:353
void av_dict_free(AVDictionary **pm)
Free all the memory allocated for an AVDictionary struct and all keys and values. ...
Definition: dict.c:203
simple assert() macros that are a bit more flexible than ISO C assert().
#define FFMAX(a, b)
Definition: common.h:94
#define av_printf_format(fmtpos, attrpos)
Definition: attributes.h:155
var_name
Definition: aeval.c:46
common internal API header
typedef void(APIENTRY *FF_PFNGLACTIVETEXTUREPROC)(GLenum texture)
static av_cold void uninit(AVFilterContext *ctx)
Definition: f_metadata.c:282
AVFormatContext * ctx
Definition: movenc.c:48
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a it should directly call filter_frame on the corresponding output For a if there are queued frames already one of these frames should be pushed If the filter should request a frame on one of its repeatedly until at least one frame has been pushed Return or at least make progress towards producing a frame
#define s(width, name)
Definition: cbs_vp9.c:257
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
Definition: f_metadata.c:293
static const AVFilterPad outputs[]
Definition: af_acontrast.c:203
#define AV_LOG_INFO
Standard information.
Definition: log.h:187
static int greater(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:155
void av_expr_free(AVExpr *e)
Free a parsed expression previously created with av_expr_parse().
Definition: eval.c:336
#define AV_OPT_FLAG_VIDEO_PARAM
Definition: opt.h:279
MetadataFunction
Definition: f_metadata.c:50
void * buf
Definition: avisynth_c.h:766
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several inputs
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
Definition: dict.c:70
Describe the class of an AVClass context structure.
Definition: log.h:67
Filter definition.
Definition: avfilter.h:144
void av_vlog(void *avcl, int level, const char *fmt, va_list vl)
Send the specified message to the log if the level is less than or equal to the current av_log_level...
Definition: log.c:373
const char * name
Filter name.
Definition: avfilter.h:148
static int equal(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:135
AVFilterLink ** outputs
array of pointers to output links
Definition: avfilter.h:350
#define flags(name, subs,...)
Definition: cbs_av1.c:561
MetadataMode
Definition: f_metadata.c:41
int av_strerror(int errnum, char *errbuf, size_t errbuf_size)
Put a description of the AVERROR code errnum in errbuf.
Definition: error.c:105
The exact code depends on how similar the blocks are and how related they are to the and needs to apply these operations to the correct inlink or outlink if there are several Macros are available to factor that when no extra processing is inlink
int
static int less(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:145
char * key
Definition: dict.h:86
AVExpr * expr
Definition: f_metadata.c:82
char * value
Definition: dict.h:87
#define av_ts2str(ts)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition: timestamp.h:54
double av_expr_eval(AVExpr *e, const double *const_values, void *opaque)
Evaluate a previously parsed expression.
Definition: eval.c:738
#define AVFILTER_DEFINE_CLASS(fname)
Definition: internal.h:334
AVFilter ff_af_ametadata
An instance of a filter.
Definition: avfilter.h:338
#define AV_DICT_IGNORE_SUFFIX
Return first entry in a dictionary whose first part corresponds to the search key, ignoring the suffix of the found key string.
Definition: dict.h:70
internal API functions
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Frame references ownership and permissions
int avio_closep(AVIOContext **s)
Close the resource accessed by the AVIOContext *s, free it and set the pointer pointing to it to NULL...
Definition: aviobuf.c:1242
static int ends_with(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:127
simple arithmetic expression evaluator