FFmpeg  4.4
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);
91 
92  int direct; // reduces buffering when printing to user-supplied URL
94 
95 #define OFFSET(x) offsetof(MetadataContext, x)
96 #define DEFINE_OPTIONS(filt_name, FLAGS) \
97 static const AVOption filt_name##_options[] = { \
98  { "mode", "set a mode of operation", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, METADATA_NB-1, FLAGS, "mode" }, \
99  { "select", "select frame", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_SELECT }, 0, 0, FLAGS, "mode" }, \
100  { "add", "add new metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_ADD }, 0, 0, FLAGS, "mode" }, \
101  { "modify", "modify metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_MODIFY }, 0, 0, FLAGS, "mode" }, \
102  { "delete", "delete metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_DELETE }, 0, 0, FLAGS, "mode" }, \
103  { "print", "print metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_PRINT }, 0, 0, FLAGS, "mode" }, \
104  { "key", "set metadata key", OFFSET(key), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \
105  { "value", "set metadata value", OFFSET(value), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \
106  { "function", "function for comparing values", OFFSET(function), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, METADATAF_NB-1, FLAGS, "function" }, \
107  { "same_str", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_SAME_STR }, 0, 3, FLAGS, "function" }, \
108  { "starts_with", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_STARTS_WITH }, 0, 0, FLAGS, "function" }, \
109  { "less", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_LESS }, 0, 3, FLAGS, "function" }, \
110  { "equal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EQUAL }, 0, 3, FLAGS, "function" }, \
111  { "greater", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_GREATER }, 0, 3, FLAGS, "function" }, \
112  { "expr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EXPR }, 0, 3, FLAGS, "function" }, \
113  { "ends_with", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_ENDS_WITH }, 0, 0, FLAGS, "function" }, \
114  { "expr", "set expression for expr function", OFFSET(expr_str), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \
115  { "file", "set file where to print metadata information", OFFSET(file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, \
116  { "direct", "reduce buffering when printing to user-set file or pipe", OFFSET(direct), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, \
117  { NULL } \
118 }
119 
120 static int same_str(MetadataContext *s, const char *value1, const char *value2)
121 {
122  return !strcmp(value1, value2);
123 }
124 
125 static int starts_with(MetadataContext *s, const char *value1, const char *value2)
126 {
127  return !strncmp(value1, value2, strlen(value2));
128 }
129 
130 static int ends_with(MetadataContext *s, const char *value1, const char *value2)
131 {
132  const int len1 = strlen(value1);
133  const int len2 = strlen(value2);
134 
135  return !strncmp(value1 + FFMAX(len1 - len2, 0), value2, len2);
136 }
137 
138 static int equal(MetadataContext *s, const char *value1, const char *value2)
139 {
140  float f1, f2;
141 
142  if (sscanf(value1, "%f", &f1) + sscanf(value2, "%f", &f2) != 2)
143  return 0;
144 
145  return fabsf(f1 - f2) < FLT_EPSILON;
146 }
147 
148 static int less(MetadataContext *s, const char *value1, const char *value2)
149 {
150  float f1, f2;
151 
152  if (sscanf(value1, "%f", &f1) + sscanf(value2, "%f", &f2) != 2)
153  return 0;
154 
155  return (f1 - f2) < FLT_EPSILON;
156 }
157 
158 static int greater(MetadataContext *s, const char *value1, const char *value2)
159 {
160  float f1, f2;
161 
162  if (sscanf(value1, "%f", &f1) + sscanf(value2, "%f", &f2) != 2)
163  return 0;
164 
165  return (f2 - f1) < FLT_EPSILON;
166 }
167 
168 static int parse_expr(MetadataContext *s, const char *value1, const char *value2)
169 {
170  double f1, f2;
171 
172  if (sscanf(value1, "%lf", &f1) + sscanf(value2, "%lf", &f2) != 2)
173  return 0;
174 
175  s->var_values[VAR_VALUE1] = f1;
176  s->var_values[VAR_VALUE2] = f2;
177 
178  return av_expr_eval(s->expr, s->var_values, NULL);
179 }
180 
181 static void print_log(AVFilterContext *ctx, const char *msg, ...)
182 {
183  va_list argument_list;
184 
185  va_start(argument_list, msg);
186  if (msg)
187  av_vlog(ctx, AV_LOG_INFO, msg, argument_list);
188  va_end(argument_list);
189 }
190 
191 static void print_file(AVFilterContext *ctx, const char *msg, ...)
192 {
193  MetadataContext *s = ctx->priv;
194  va_list argument_list;
195 
196  va_start(argument_list, msg);
197  if (msg) {
198  char buf[128];
199  vsnprintf(buf, sizeof(buf), msg, argument_list);
200  avio_write(s->avio_context, buf, av_strnlen(buf, sizeof(buf)));
201  }
202  va_end(argument_list);
203 }
204 
206 {
207  MetadataContext *s = ctx->priv;
208  int ret;
209 
210  if (!s->key && s->mode != METADATA_PRINT && s->mode != METADATA_DELETE) {
211  av_log(ctx, AV_LOG_WARNING, "Metadata key must be set\n");
212  return AVERROR(EINVAL);
213  }
214 
215  if ((s->mode == METADATA_MODIFY ||
216  s->mode == METADATA_ADD) && !s->value) {
217  av_log(ctx, AV_LOG_WARNING, "Missing metadata value\n");
218  return AVERROR(EINVAL);
219  }
220 
221  switch (s->function) {
222  case METADATAF_SAME_STR:
223  s->compare = same_str;
224  break;
226  s->compare = starts_with;
227  break;
228  case METADATAF_ENDS_WITH:
229  s->compare = ends_with;
230  break;
231  case METADATAF_LESS:
232  s->compare = less;
233  break;
234  case METADATAF_EQUAL:
235  s->compare = equal;
236  break;
237  case METADATAF_GREATER:
238  s->compare = greater;
239  break;
240  case METADATAF_EXPR:
241  s->compare = parse_expr;
242  break;
243  default:
244  av_assert0(0);
245  };
246 
247  if (s->function == METADATAF_EXPR) {
248  if (!s->expr_str) {
249  av_log(ctx, AV_LOG_WARNING, "expr option not set\n");
250  return AVERROR(EINVAL);
251  }
252  if ((ret = av_expr_parse(&s->expr, s->expr_str,
253  var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
254  av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", s->expr_str);
255  return ret;
256  }
257  }
258 
259  if (s->mode == METADATA_PRINT && s->file_str) {
260  s->print = print_file;
261  } else {
262  s->print = print_log;
263  }
264 
265  s->avio_context = NULL;
266  if (s->file_str) {
267  if (!strcmp("-", s->file_str)) {
268  ret = avio_open(&s->avio_context, "pipe:1", AVIO_FLAG_WRITE);
269  } else {
270  ret = avio_open(&s->avio_context, s->file_str, AVIO_FLAG_WRITE);
271  }
272 
273  if (ret < 0) {
274  char buf[128];
275  av_strerror(ret, buf, sizeof(buf));
276  av_log(ctx, AV_LOG_ERROR, "Could not open %s: %s\n",
277  s->file_str, buf);
278  return ret;
279  }
280 
281  if (s->direct)
282  s->avio_context->direct = AVIO_FLAG_DIRECT;
283  }
284 
285  return 0;
286 }
287 
289 {
290  MetadataContext *s = ctx->priv;
291 
292  av_expr_free(s->expr);
293  s->expr = NULL;
294  if (s->avio_context) {
295  avio_closep(&s->avio_context);
296  }
297 }
298 
299 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
300 {
301  AVFilterContext *ctx = inlink->dst;
302  AVFilterLink *outlink = ctx->outputs[0];
303  MetadataContext *s = ctx->priv;
304  AVDictionary **metadata = &frame->metadata;
306 
307  if (!*metadata && s->mode != METADATA_ADD)
308  return ff_filter_frame(outlink, frame);
309 
310  e = av_dict_get(*metadata, !s->key ? "" : s->key, NULL,
311  !s->key ? AV_DICT_IGNORE_SUFFIX: 0);
312 
313  switch (s->mode) {
314  case METADATA_SELECT:
315  if (!s->value && e && e->value) {
316  return ff_filter_frame(outlink, frame);
317  } else if (s->value && e && e->value &&
318  s->compare(s, e->value, s->value)) {
319  return ff_filter_frame(outlink, frame);
320  }
321  break;
322  case METADATA_ADD:
323  if (e && e->value) {
324  ;
325  } else {
326  av_dict_set(metadata, s->key, s->value, 0);
327  }
328  return ff_filter_frame(outlink, frame);
329  case METADATA_MODIFY:
330  if (e && e->value) {
331  av_dict_set(metadata, s->key, s->value, 0);
332  }
333  return ff_filter_frame(outlink, frame);
334  case METADATA_PRINT:
335  if (!s->key && e) {
336  s->print(ctx, "frame:%-4"PRId64" pts:%-7s pts_time:%s\n",
338  s->print(ctx, "%s=%s\n", e->key, e->value);
339  while ((e = av_dict_get(*metadata, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL) {
340  s->print(ctx, "%s=%s\n", e->key, e->value);
341  }
342  } else if (e && e->value && (!s->value || (e->value && s->compare(s, e->value, s->value)))) {
343  s->print(ctx, "frame:%-4"PRId64" pts:%-7s pts_time:%s\n",
345  s->print(ctx, "%s=%s\n", s->key, e->value);
346  }
347  return ff_filter_frame(outlink, frame);
348  case METADATA_DELETE:
349  if (!s->key) {
350  av_dict_free(metadata);
351  } else if (e && e->value && (!s->value || s->compare(s, e->value, s->value))) {
352  av_dict_set(metadata, s->key, NULL, 0);
353  }
354  return ff_filter_frame(outlink, frame);
355  default:
356  av_assert0(0);
357  };
358 
360 
361  return 0;
362 }
363 
364 #if CONFIG_AMETADATA_FILTER
365 
367 AVFILTER_DEFINE_CLASS(ametadata);
368 
369 static const AVFilterPad ainputs[] = {
370  {
371  .name = "default",
372  .type = AVMEDIA_TYPE_AUDIO,
373  .filter_frame = filter_frame,
374  },
375  { NULL }
376 };
377 
378 static const AVFilterPad aoutputs[] = {
379  {
380  .name = "default",
381  .type = AVMEDIA_TYPE_AUDIO,
382  },
383  { NULL }
384 };
385 
387  .name = "ametadata",
388  .description = NULL_IF_CONFIG_SMALL("Manipulate audio frame metadata."),
389  .priv_size = sizeof(MetadataContext),
390  .priv_class = &ametadata_class,
391  .init = init,
392  .uninit = uninit,
393  .inputs = ainputs,
394  .outputs = aoutputs,
396 };
397 #endif /* CONFIG_AMETADATA_FILTER */
398 
399 #if CONFIG_METADATA_FILTER
400 
402 AVFILTER_DEFINE_CLASS(metadata);
403 
404 static const AVFilterPad inputs[] = {
405  {
406  .name = "default",
407  .type = AVMEDIA_TYPE_VIDEO,
408  .filter_frame = filter_frame,
409  },
410  { NULL }
411 };
412 
413 static const AVFilterPad outputs[] = {
414  {
415  .name = "default",
416  .type = AVMEDIA_TYPE_VIDEO,
417  },
418  { NULL }
419 };
420 
422  .name = "metadata",
423  .description = NULL_IF_CONFIG_SMALL("Manipulate video frame metadata."),
424  .priv_size = sizeof(MetadataContext),
425  .priv_class = &metadata_class,
426  .init = init,
427  .uninit = uninit,
428  .inputs = inputs,
429  .outputs = outputs,
431 };
432 #endif /* CONFIG_METADATA_FILTER */
static const AVFilterPad inputs[]
Definition: af_acontrast.c:193
static const AVFilterPad outputs[]
Definition: af_acontrast.c:203
AVFilter ff_af_ametadata
AVFilter ff_vf_metadata
#define av_printf_format(fmtpos, attrpos)
Definition: attributes.h:161
#define av_cold
Definition: attributes.h:88
simple assert() macros that are a bit more flexible than ISO C assert().
#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:1094
Main libavfilter public API header.
Buffered I/O operations.
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:1137
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:675
#define AVIO_FLAG_DIRECT
Use direct mode.
Definition: avio.h:701
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition: aviobuf.c:225
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:1192
#define flags(name, subs,...)
Definition: cbs_av1.c:561
#define s(width, name)
Definition: cbs_vp9.c:257
#define FFMAX(a, b)
Definition: common.h:103
#define NULL
Definition: coverity.c:32
static __device__ float fabsf(float a)
Definition: cuda_runtime.h:181
static AVFrame * frame
void av_expr_free(AVExpr *e)
Free a parsed expression previously created with av_expr_parse().
Definition: eval.c:336
double av_expr_eval(AVExpr *e, const double *const_values, void *opaque)
Evaluate a previously parsed expression.
Definition: eval.c:766
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:685
simple arithmetic expression evaluator
static int equal(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:138
static void print_file(AVFilterContext *ctx, const char *msg,...)
Definition: f_metadata.c:191
@ VAR_VALUE1
Definition: f_metadata.c:68
@ VAR_VALUE2
Definition: f_metadata.c:69
@ VAR_VARS_NB
Definition: f_metadata.c:70
static int less(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:148
static int greater(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:158
static int parse_expr(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:168
static int starts_with(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:125
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
Definition: f_metadata.c:299
static int same_str(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:120
MetadataMode
Definition: f_metadata.c:41
@ METADATA_ADD
Definition: f_metadata.c:43
@ METADATA_PRINT
Definition: f_metadata.c:46
@ METADATA_SELECT
Definition: f_metadata.c:42
@ METADATA_MODIFY
Definition: f_metadata.c:44
@ METADATA_NB
Definition: f_metadata.c:47
@ METADATA_DELETE
Definition: f_metadata.c:45
MetadataFunction
Definition: f_metadata.c:50
@ METADATAF_LESS
Definition: f_metadata.c:53
@ METADATAF_ENDS_WITH
Definition: f_metadata.c:57
@ METADATAF_EQUAL
Definition: f_metadata.c:54
@ METADATAF_SAME_STR
Definition: f_metadata.c:51
@ METADATAF_EXPR
Definition: f_metadata.c:56
@ METADATAF_STARTS_WITH
Definition: f_metadata.c:52
@ METADATAF_NB
Definition: f_metadata.c:58
@ METADATAF_GREATER
Definition: f_metadata.c:55
static int ends_with(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:130
static const char *const var_names[]
Definition: f_metadata.c:61
static av_cold int init(AVFilterContext *ctx)
Definition: f_metadata.c:205
static av_cold void uninit(AVFilterContext *ctx)
Definition: f_metadata.c:288
#define DEFINE_OPTIONS(filt_name, FLAGS)
Definition: f_metadata.c:96
static void print_log(AVFilterContext *ctx, const char *msg,...)
Definition: f_metadata.c:181
int
#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:126
void av_dict_free(AVDictionary **pm)
Free all the memory allocated for an AVDictionary struct and all keys and values.
Definition: dict.c:203
#define AV_DICT_IGNORE_SUFFIX
Return first entry in a dictionary whose first part corresponds to the search key,...
Definition: dict.h:70
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
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
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
#define AVERROR(e)
Definition: error.h:43
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:203
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:200
#define AV_LOG_INFO
Standard information.
Definition: log.h:205
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:194
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:424
@ AVMEDIA_TYPE_AUDIO
Definition: avutil.h:202
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
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:141
#define AVFILTER_DEFINE_CLASS(fname)
Definition: internal.h:288
common internal API header
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:117
AVOptions.
#define AV_OPT_FLAG_FILTERING_PARAM
a generic parameter which can be set by the user for filtering
Definition: opt.h:294
#define AV_OPT_FLAG_AUDIO_PARAM
Definition: opt.h:280
#define AV_OPT_FLAG_VIDEO_PARAM
Definition: opt.h:281
typedef void(RENAME(mix_any_func_type))
var_name
Definition: setts_bsf.c:50
#define vsnprintf
Definition: snprintf.h:36
Describe the class of an AVClass context structure.
Definition: log.h:67
char * key
Definition: dict.h:82
char * value
Definition: dict.h:83
Definition: eval.c:157
An instance of a filter.
Definition: avfilter.h:341
A filter pad used for either input or output.
Definition: internal.h:54
const char * name
Pad name.
Definition: internal.h:60
Filter definition.
Definition: avfilter.h:145
const char * name
Filter name.
Definition: avfilter.h:149
This structure describes decoded (raw) audio or video data.
Definition: frame.h:318
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
Definition: frame.h:411
AVDictionary * metadata
metadata.
Definition: frame.h:604
Bytestream IO Context.
Definition: avio.h:161
void(* print)(AVFilterContext *ctx, const char *msg,...) av_printf_format(2
Definition: f_metadata.c:90
void(*) in direct)
Definition: f_metadata.c:90
double var_values[VAR_VARS_NB]
Definition: f_metadata.c:83
char * file_str
Definition: f_metadata.c:86
int(* compare)(struct MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:88
char * expr_str
Definition: f_metadata.c:81
AVExpr * expr
Definition: f_metadata.c:82
AVIOContext * avio_context
Definition: f_metadata.c:85
#define av_log(a,...)
AVFormatContext * ctx
Definition: movenc.c:48
timestamp utils, mostly useful for debugging/logging purposes
#define av_ts2str(ts)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition: timestamp.h:54
#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