FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 
58 };
59 
60 static const char *const var_names[] = {
61  "VALUE1",
62  "VALUE2",
63  NULL
64 };
65 
66 enum var_name {
70 };
71 
72 typedef struct MetadataContext {
73  const AVClass *class;
74 
75  int mode;
76  char *key;
77  char *value;
78  int function;
79 
80  char *expr_str;
83 
85  char *file_str;
86 
88  const char *value1, const char *value2);
89  void (*print)(AVFilterContext *ctx, const char *msg, ...) av_printf_format(2, 3);
91 
92 #define OFFSET(x) offsetof(MetadataContext, x)
93 #define DEFINE_OPTIONS(filt_name, FLAGS) \
94 static const AVOption filt_name##_options[] = { \
95  { "mode", "set a mode of operation", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, METADATA_NB-1, FLAGS, "mode" }, \
96  { "select", "select frame", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_SELECT }, 0, 0, FLAGS, "mode" }, \
97  { "add", "add new metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_ADD }, 0, 0, FLAGS, "mode" }, \
98  { "modify", "modify metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_MODIFY }, 0, 0, FLAGS, "mode" }, \
99  { "delete", "delete metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_DELETE }, 0, 0, FLAGS, "mode" }, \
100  { "print", "print metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_PRINT }, 0, 0, FLAGS, "mode" }, \
101  { "key", "set metadata key", OFFSET(key), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \
102  { "value", "set metadata value", OFFSET(value), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \
103  { "function", "function for comparing values", OFFSET(function), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, METADATAF_NB-1, FLAGS, "function" }, \
104  { "same_str", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_SAME_STR }, 0, 3, FLAGS, "function" }, \
105  { "starts_with", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_STARTS_WITH }, 0, 0, FLAGS, "function" }, \
106  { "less", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_LESS }, 0, 3, FLAGS, "function" }, \
107  { "equal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EQUAL }, 0, 3, FLAGS, "function" }, \
108  { "greater", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_GREATER }, 0, 3, FLAGS, "function" }, \
109  { "expr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EXPR }, 0, 3, FLAGS, "function" }, \
110  { "expr", "set expression for expr function", OFFSET(expr_str), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \
111  { "file", "set file where to print metadata information", OFFSET(file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, \
112  { NULL } \
113 }
114 
115 static int same_str(MetadataContext *s, const char *value1, const char *value2)
116 {
117  return !strcmp(value1, value2);
118 }
119 
120 static int starts_with(MetadataContext *s, const char *value1, const char *value2)
121 {
122  return !strncmp(value1, value2, strlen(value2));
123 }
124 
125 static int equal(MetadataContext *s, const char *value1, const char *value2)
126 {
127  float f1, f2;
128 
129  if (sscanf(value1, "%f", &f1) + sscanf(value2, "%f", &f2) != 2)
130  return 0;
131 
132  return fabsf(f1 - f2) < FLT_EPSILON;
133 }
134 
135 static int less(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 (f1 - f2) < FLT_EPSILON;
143 }
144 
145 static int greater(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 (f2 - f1) < FLT_EPSILON;
153 }
154 
155 static int parse_expr(MetadataContext *s, const char *value1, const char *value2)
156 {
157  double f1, f2;
158 
159  if (sscanf(value1, "%lf", &f1) + sscanf(value2, "%lf", &f2) != 2)
160  return 0;
161 
162  s->var_values[VAR_VALUE1] = f1;
163  s->var_values[VAR_VALUE2] = f2;
164 
165  return av_expr_eval(s->expr, s->var_values, NULL);
166 }
167 
168 static void print_log(AVFilterContext *ctx, const char *msg, ...)
169 {
170  va_list argument_list;
171 
172  va_start(argument_list, msg);
173  if (msg)
174  av_vlog(ctx, AV_LOG_INFO, msg, argument_list);
175  va_end(argument_list);
176 }
177 
178 static void print_file(AVFilterContext *ctx, const char *msg, ...)
179 {
180  MetadataContext *s = ctx->priv;
181  va_list argument_list;
182 
183  va_start(argument_list, msg);
184  if (msg) {
185  char buf[128];
186  vsnprintf(buf, sizeof(buf), msg, argument_list);
187  avio_write(s->avio_context, buf, av_strnlen(buf, sizeof(buf)));
188  }
189  va_end(argument_list);
190 }
191 
193 {
194  MetadataContext *s = ctx->priv;
195  int ret;
196 
197  if (!s->key && s->mode != METADATA_PRINT && s->mode != METADATA_DELETE) {
198  av_log(ctx, AV_LOG_WARNING, "Metadata key must be set\n");
199  return AVERROR(EINVAL);
200  }
201 
202  if ((s->mode == METADATA_MODIFY ||
203  s->mode == METADATA_ADD) && !s->value) {
204  av_log(ctx, AV_LOG_WARNING, "Missing metadata value\n");
205  return AVERROR(EINVAL);
206  }
207 
208  switch (s->function) {
209  case METADATAF_SAME_STR:
210  s->compare = same_str;
211  break;
213  s->compare = starts_with;
214  break;
215  case METADATAF_LESS:
216  s->compare = less;
217  break;
218  case METADATAF_EQUAL:
219  s->compare = equal;
220  break;
221  case METADATAF_GREATER:
222  s->compare = greater;
223  break;
224  case METADATAF_EXPR:
225  s->compare = parse_expr;
226  break;
227  default:
228  av_assert0(0);
229  };
230 
231  if (s->function == METADATAF_EXPR) {
232  if (!s->expr_str) {
233  av_log(ctx, AV_LOG_WARNING, "expr option not set\n");
234  return AVERROR(EINVAL);
235  }
236  if ((ret = av_expr_parse(&s->expr, s->expr_str,
237  var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
238  av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", s->expr_str);
239  return ret;
240  }
241  }
242 
243  if (s->mode == METADATA_PRINT && s->file_str) {
244  s->print = print_file;
245  } else {
246  s->print = print_log;
247  }
248 
249  s->avio_context = NULL;
250  if (s->file_str) {
251  if (!strcmp("-", s->file_str)) {
252  ret = avio_open(&s->avio_context, "pipe:1", AVIO_FLAG_WRITE);
253  } else {
255  }
256 
257  if (ret < 0) {
258  char buf[128];
259  av_strerror(ret, buf, sizeof(buf));
260  av_log(ctx, AV_LOG_ERROR, "Could not open %s: %s\n",
261  s->file_str, buf);
262  return ret;
263  }
264  }
265 
266  return 0;
267 }
268 
270 {
271  MetadataContext *s = ctx->priv;
272 
273  if (s->avio_context) {
275  }
276 }
277 
278 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
279 {
280  AVFilterContext *ctx = inlink->dst;
281  AVFilterLink *outlink = ctx->outputs[0];
282  MetadataContext *s = ctx->priv;
283  AVDictionary **metadata = &frame->metadata;
285 
286  if (!*metadata)
287  return ff_filter_frame(outlink, frame);
288 
289  e = av_dict_get(*metadata, !s->key ? "" : s->key, NULL,
290  !s->key ? AV_DICT_IGNORE_SUFFIX: 0);
291 
292  switch (s->mode) {
293  case METADATA_SELECT:
294  if (!s->value && e && e->value) {
295  return ff_filter_frame(outlink, frame);
296  } else if (s->value && e && e->value &&
297  s->compare(s, e->value, s->value)) {
298  return ff_filter_frame(outlink, frame);
299  }
300  break;
301  case METADATA_ADD:
302  if (e && e->value) {
303  ;
304  } else {
305  av_dict_set(metadata, s->key, s->value, 0);
306  }
307  return ff_filter_frame(outlink, frame);
308  break;
309  case METADATA_MODIFY:
310  if (e && e->value) {
311  av_dict_set(metadata, s->key, s->value, 0);
312  }
313  return ff_filter_frame(outlink, frame);
314  break;
315  case METADATA_PRINT:
316  if (!s->key && e) {
317  s->print(ctx, "frame:%-4"PRId64" pts:%-7s pts_time:%s\n",
318  inlink->frame_count_out, av_ts2str(frame->pts), av_ts2timestr(frame->pts, &inlink->time_base));
319  s->print(ctx, "%s=%s\n", e->key, e->value);
320  while ((e = av_dict_get(*metadata, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL) {
321  s->print(ctx, "%s=%s\n", e->key, e->value);
322  }
323  } else if (e && e->value && (!s->value || (e->value && s->compare(s, e->value, s->value)))) {
324  s->print(ctx, "frame:%-4"PRId64" pts:%-7s pts_time:%s\n",
325  inlink->frame_count_out, av_ts2str(frame->pts), av_ts2timestr(frame->pts, &inlink->time_base));
326  s->print(ctx, "%s=%s\n", s->key, e->value);
327  }
328  return ff_filter_frame(outlink, frame);
329  break;
330  case METADATA_DELETE:
331  if (!s->key) {
332  av_dict_free(metadata);
333  } else if (e && e->value && (!s->value || s->compare(s, e->value, s->value))) {
334  av_dict_set(metadata, s->key, NULL, 0);
335  }
336  return ff_filter_frame(outlink, frame);
337  break;
338  default:
339  av_assert0(0);
340  };
341 
342  av_frame_free(&frame);
343 
344  return 0;
345 }
346 
347 #if CONFIG_AMETADATA_FILTER
348 
350 AVFILTER_DEFINE_CLASS(ametadata);
351 
352 static const AVFilterPad ainputs[] = {
353  {
354  .name = "default",
355  .type = AVMEDIA_TYPE_AUDIO,
356  .filter_frame = filter_frame,
357  },
358  { NULL }
359 };
360 
361 static const AVFilterPad aoutputs[] = {
362  {
363  .name = "default",
364  .type = AVMEDIA_TYPE_AUDIO,
365  },
366  { NULL }
367 };
368 
370  .name = "ametadata",
371  .description = NULL_IF_CONFIG_SMALL("Manipulate audio frame metadata."),
372  .priv_size = sizeof(MetadataContext),
373  .priv_class = &ametadata_class,
374  .init = init,
375  .uninit = uninit,
376  .inputs = ainputs,
377  .outputs = aoutputs,
379 };
380 #endif /* CONFIG_AMETADATA_FILTER */
381 
382 #if CONFIG_METADATA_FILTER
383 
385 AVFILTER_DEFINE_CLASS(metadata);
386 
387 static const AVFilterPad inputs[] = {
388  {
389  .name = "default",
390  .type = AVMEDIA_TYPE_VIDEO,
391  .filter_frame = filter_frame,
392  },
393  { NULL }
394 };
395 
396 static const AVFilterPad outputs[] = {
397  {
398  .name = "default",
399  .type = AVMEDIA_TYPE_VIDEO,
400  },
401  { NULL }
402 };
403 
405  .name = "metadata",
406  .description = NULL_IF_CONFIG_SMALL("Manipulate video frame metadata."),
407  .priv_size = sizeof(MetadataContext),
408  .priv_class = &metadata_class,
409  .init = init,
410  .uninit = uninit,
411  .inputs = inputs,
412  .outputs = outputs,
414 };
415 #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:1154
#define NULL
Definition: coverity.c:32
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:120
This structure describes decoded (raw) audio or video data.
Definition: frame.h:226
static int parse_expr(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:155
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:60
static av_cold int init(AVFilterContext *ctx)
Definition: f_metadata.c:192
#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:655
int(* compare)(struct MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:87
#define DEFINE_OPTIONS(filt_name, FLAGS)
Definition: f_metadata.c:93
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:679
#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:1080
#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:319
Definition: eval.c:157
static AVFrame * frame
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:84
AVDictionary * metadata
metadata.
Definition: frame.h:513
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:80
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:115
double var_values[VAR_VARS_NB]
Definition: f_metadata.c:82
char * file_str
Definition: f_metadata.c:85
#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:178
#define AV_OPT_FLAG_FILTERING_PARAM
a generic parameter which can be set by the user for filtering
Definition: opt.h:291
static void print_log(AVFilterContext *ctx, const char *msg,...)
Definition: f_metadata.c:168
#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: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 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:269
AVFormatContext * ctx
Definition: movenc.c:48
#define s(width, name)
Definition: cbs_vp9.c:257
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
Definition: f_metadata.c:278
static const AVFilterPad inputs[]
Definition: af_acontrast.c:193
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:145
#define AV_OPT_FLAG_VIDEO_PARAM
Definition: opt.h:279
MetadataFunction
Definition: f_metadata.c:50
void * buf
Definition: avisynth_c.h:690
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:125
AVFilterLink ** outputs
array of pointers to output links
Definition: avfilter.h:350
#define flags(name, subs,...)
Definition: cbs_av1.c:596
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
int
static int less(MetadataContext *s, const char *value1, const char *value2)
Definition: f_metadata.c:135
char * key
Definition: dict.h:86
AVExpr * expr
Definition: f_metadata.c:81
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:734
#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
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:1215
void(* print)(AVFilterContext *ctx, const char *msg,...) av_printf_format(2
Definition: f_metadata.c:89
simple arithmetic expression evaluator