| Line | Branch | Exec | Source |
|---|---|---|---|
| 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 "config_components.h" | ||
| 27 | |||
| 28 | #include <float.h> | ||
| 29 | |||
| 30 | #include "libavutil/avassert.h" | ||
| 31 | #include "libavutil/avstring.h" | ||
| 32 | #include "libavutil/eval.h" | ||
| 33 | #include "libavutil/internal.h" | ||
| 34 | #include "libavutil/opt.h" | ||
| 35 | #include "libavutil/timestamp.h" | ||
| 36 | #include "libavformat/avio.h" | ||
| 37 | #include "avfilter.h" | ||
| 38 | #include "audio.h" | ||
| 39 | #include "filters.h" | ||
| 40 | #include "video.h" | ||
| 41 | |||
| 42 | enum MetadataMode { | ||
| 43 | METADATA_SELECT, | ||
| 44 | METADATA_ADD, | ||
| 45 | METADATA_MODIFY, | ||
| 46 | METADATA_DELETE, | ||
| 47 | METADATA_PRINT, | ||
| 48 | METADATA_NB | ||
| 49 | }; | ||
| 50 | |||
| 51 | enum MetadataFunction { | ||
| 52 | METADATAF_SAME_STR, | ||
| 53 | METADATAF_STARTS_WITH, | ||
| 54 | METADATAF_LESS, | ||
| 55 | METADATAF_EQUAL, | ||
| 56 | METADATAF_GREATER, | ||
| 57 | METADATAF_EXPR, | ||
| 58 | METADATAF_ENDS_WITH, | ||
| 59 | METADATAF_NB | ||
| 60 | }; | ||
| 61 | |||
| 62 | static const char *const var_names[] = { | ||
| 63 | "VALUE1", | ||
| 64 | "VALUE2", | ||
| 65 | "FRAMEVAL", | ||
| 66 | "USERVAL", | ||
| 67 | NULL | ||
| 68 | }; | ||
| 69 | |||
| 70 | enum var_name { | ||
| 71 | VAR_VALUE1, | ||
| 72 | VAR_VALUE2, | ||
| 73 | VAR_FRAMEVAL, | ||
| 74 | VAR_USERVAL, | ||
| 75 | VAR_VARS_NB | ||
| 76 | }; | ||
| 77 | |||
| 78 | typedef struct MetadataContext { | ||
| 79 | const AVClass *class; | ||
| 80 | |||
| 81 | int mode; | ||
| 82 | char *key; | ||
| 83 | char *value; | ||
| 84 | int function; | ||
| 85 | |||
| 86 | char *expr_str; | ||
| 87 | AVExpr *expr; | ||
| 88 | double var_values[VAR_VARS_NB]; | ||
| 89 | |||
| 90 | AVIOContext* avio_context; | ||
| 91 | char *file_str; | ||
| 92 | |||
| 93 | int (*compare)(struct MetadataContext *s, | ||
| 94 | const char *value1, const char *value2); | ||
| 95 | void (*print)(AVFilterContext *ctx, const char *msg, ...) av_printf_format(2, 3); | ||
| 96 | |||
| 97 | int direct; // reduces buffering when printing to user-supplied URL | ||
| 98 | } MetadataContext; | ||
| 99 | |||
| 100 | #define OFFSET(x) offsetof(MetadataContext, x) | ||
| 101 | #define DEFINE_OPTIONS(filt_name, FLAGS) \ | ||
| 102 | static const AVOption filt_name##_options[] = { \ | ||
| 103 | { "mode", "set a mode of operation", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, METADATA_NB-1, FLAGS, .unit = "mode" }, \ | ||
| 104 | { "select", "select frame", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_SELECT }, 0, 0, FLAGS, .unit = "mode" }, \ | ||
| 105 | { "add", "add new metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_ADD }, 0, 0, FLAGS, .unit = "mode" }, \ | ||
| 106 | { "modify", "modify metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_MODIFY }, 0, 0, FLAGS, .unit = "mode" }, \ | ||
| 107 | { "delete", "delete metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_DELETE }, 0, 0, FLAGS, .unit = "mode" }, \ | ||
| 108 | { "print", "print metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_PRINT }, 0, 0, FLAGS, .unit = "mode" }, \ | ||
| 109 | { "key", "set metadata key", OFFSET(key), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \ | ||
| 110 | { "value", "set metadata value", OFFSET(value), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \ | ||
| 111 | { "function", "function for comparing values", OFFSET(function), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, METADATAF_NB-1, FLAGS, .unit = "function" }, \ | ||
| 112 | { "same_str", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_SAME_STR }, 0, 3, FLAGS, .unit = "function" }, \ | ||
| 113 | { "starts_with", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_STARTS_WITH }, 0, 0, FLAGS, .unit = "function" }, \ | ||
| 114 | { "less", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_LESS }, 0, 3, FLAGS, .unit = "function" }, \ | ||
| 115 | { "equal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EQUAL }, 0, 3, FLAGS, .unit = "function" }, \ | ||
| 116 | { "greater", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_GREATER }, 0, 3, FLAGS, .unit = "function" }, \ | ||
| 117 | { "expr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EXPR }, 0, 3, FLAGS, .unit = "function" }, \ | ||
| 118 | { "ends_with", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_ENDS_WITH }, 0, 0, FLAGS, .unit = "function" }, \ | ||
| 119 | { "expr", "set expression for expr function", OFFSET(expr_str), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \ | ||
| 120 | { "file", "set file where to print metadata information", OFFSET(file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, \ | ||
| 121 | { "direct", "reduce buffering when printing to user-set file or pipe", OFFSET(direct), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, \ | ||
| 122 | { NULL } \ | ||
| 123 | } | ||
| 124 | |||
| 125 | ✗ | static int same_str(MetadataContext *s, const char *value1, const char *value2) | |
| 126 | { | ||
| 127 | ✗ | return !strcmp(value1, value2); | |
| 128 | } | ||
| 129 | |||
| 130 | ✗ | static int starts_with(MetadataContext *s, const char *value1, const char *value2) | |
| 131 | { | ||
| 132 | ✗ | return !strncmp(value1, value2, strlen(value2)); | |
| 133 | } | ||
| 134 | |||
| 135 | ✗ | static int ends_with(MetadataContext *s, const char *value1, const char *value2) | |
| 136 | { | ||
| 137 | ✗ | const int len1 = strlen(value1); | |
| 138 | ✗ | const int len2 = strlen(value2); | |
| 139 | |||
| 140 | ✗ | return !strncmp(value1 + FFMAX(len1 - len2, 0), value2, len2); | |
| 141 | } | ||
| 142 | |||
| 143 | ✗ | static int equal(MetadataContext *s, const char *value1, const char *value2) | |
| 144 | { | ||
| 145 | float f1, f2; | ||
| 146 | |||
| 147 | ✗ | if (sscanf(value1, "%f", &f1) + sscanf(value2, "%f", &f2) != 2) | |
| 148 | ✗ | return 0; | |
| 149 | |||
| 150 | ✗ | return fabsf(f1 - f2) < FLT_EPSILON; | |
| 151 | } | ||
| 152 | |||
| 153 | ✗ | static int less(MetadataContext *s, const char *value1, const char *value2) | |
| 154 | { | ||
| 155 | float f1, f2; | ||
| 156 | |||
| 157 | ✗ | if (sscanf(value1, "%f", &f1) + sscanf(value2, "%f", &f2) != 2) | |
| 158 | ✗ | return 0; | |
| 159 | |||
| 160 | ✗ | return (f1 - f2) < FLT_EPSILON; | |
| 161 | } | ||
| 162 | |||
| 163 | ✗ | static int greater(MetadataContext *s, const char *value1, const char *value2) | |
| 164 | { | ||
| 165 | float f1, f2; | ||
| 166 | |||
| 167 | ✗ | if (sscanf(value1, "%f", &f1) + sscanf(value2, "%f", &f2) != 2) | |
| 168 | ✗ | return 0; | |
| 169 | |||
| 170 | ✗ | return (f2 - f1) < FLT_EPSILON; | |
| 171 | } | ||
| 172 | |||
| 173 | ✗ | static int parse_expr(MetadataContext *s, const char *value1, const char *value2) | |
| 174 | { | ||
| 175 | double f1, f2; | ||
| 176 | |||
| 177 | ✗ | if (sscanf(value1, "%lf", &f1) + sscanf(value2, "%lf", &f2) != 2) | |
| 178 | ✗ | return 0; | |
| 179 | |||
| 180 | ✗ | s->var_values[VAR_VALUE1] = s->var_values[VAR_FRAMEVAL] = f1; | |
| 181 | ✗ | s->var_values[VAR_VALUE2] = s->var_values[VAR_USERVAL] = f2; | |
| 182 | |||
| 183 | ✗ | return av_expr_eval(s->expr, s->var_values, NULL); | |
| 184 | } | ||
| 185 | |||
| 186 | 140 | static void print_log(AVFilterContext *ctx, const char *msg, ...) | |
| 187 | { | ||
| 188 | va_list argument_list; | ||
| 189 | |||
| 190 | 140 | va_start(argument_list, msg); | |
| 191 |
1/2✓ Branch 0 taken 140 times.
✗ Branch 1 not taken.
|
140 | if (msg) |
| 192 | 140 | av_vlog(ctx, AV_LOG_INFO, msg, argument_list); | |
| 193 | 140 | va_end(argument_list); | |
| 194 | 140 | } | |
| 195 | |||
| 196 | 267 | static void print_file(AVFilterContext *ctx, const char *msg, ...) | |
| 197 | { | ||
| 198 | 267 | MetadataContext *s = ctx->priv; | |
| 199 | va_list argument_list; | ||
| 200 | |||
| 201 | 267 | va_start(argument_list, msg); | |
| 202 |
1/2✓ Branch 0 taken 267 times.
✗ Branch 1 not taken.
|
267 | if (msg) { |
| 203 | char buf[128]; | ||
| 204 | 267 | int ret = vsnprintf(buf, sizeof(buf), msg, argument_list); | |
| 205 | 267 | avio_write(s->avio_context, buf, ret); | |
| 206 | } | ||
| 207 | 267 | va_end(argument_list); | |
| 208 | 267 | } | |
| 209 | |||
| 210 | 28 | static av_cold int init(AVFilterContext *ctx) | |
| 211 | { | ||
| 212 | 28 | MetadataContext *s = ctx->priv; | |
| 213 | int ret; | ||
| 214 | |||
| 215 |
2/6✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
28 | if (!s->key && s->mode != METADATA_PRINT && s->mode != METADATA_DELETE) { |
| 216 | ✗ | av_log(ctx, AV_LOG_WARNING, "Metadata key must be set\n"); | |
| 217 | ✗ | return AVERROR(EINVAL); | |
| 218 | } | ||
| 219 | |||
| 220 |
1/2✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
|
28 | if ((s->mode == METADATA_MODIFY || |
| 221 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
28 | s->mode == METADATA_ADD) && !s->value) { |
| 222 | ✗ | av_log(ctx, AV_LOG_WARNING, "Missing metadata value\n"); | |
| 223 | ✗ | return AVERROR(EINVAL); | |
| 224 | } | ||
| 225 | |||
| 226 |
1/8✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
28 | switch (s->function) { |
| 227 | 28 | case METADATAF_SAME_STR: | |
| 228 | 28 | s->compare = same_str; | |
| 229 | 28 | break; | |
| 230 | ✗ | case METADATAF_STARTS_WITH: | |
| 231 | ✗ | s->compare = starts_with; | |
| 232 | ✗ | break; | |
| 233 | ✗ | case METADATAF_ENDS_WITH: | |
| 234 | ✗ | s->compare = ends_with; | |
| 235 | ✗ | break; | |
| 236 | ✗ | case METADATAF_LESS: | |
| 237 | ✗ | s->compare = less; | |
| 238 | ✗ | break; | |
| 239 | ✗ | case METADATAF_EQUAL: | |
| 240 | ✗ | s->compare = equal; | |
| 241 | ✗ | break; | |
| 242 | ✗ | case METADATAF_GREATER: | |
| 243 | ✗ | s->compare = greater; | |
| 244 | ✗ | break; | |
| 245 | ✗ | case METADATAF_EXPR: | |
| 246 | ✗ | s->compare = parse_expr; | |
| 247 | ✗ | break; | |
| 248 | ✗ | default: | |
| 249 | ✗ | av_assert0(0); | |
| 250 | }; | ||
| 251 | |||
| 252 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
|
28 | if (s->function == METADATAF_EXPR) { |
| 253 | ✗ | if (!s->expr_str) { | |
| 254 | ✗ | av_log(ctx, AV_LOG_WARNING, "expr option not set\n"); | |
| 255 | ✗ | return AVERROR(EINVAL); | |
| 256 | } | ||
| 257 | ✗ | if ((ret = av_expr_parse(&s->expr, s->expr_str, | |
| 258 | var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) { | ||
| 259 | ✗ | av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", s->expr_str); | |
| 260 | ✗ | return ret; | |
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 |
3/4✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 2 times.
|
28 | if (s->mode == METADATA_PRINT && s->file_str) { |
| 265 | 26 | s->print = print_file; | |
| 266 | } else { | ||
| 267 | 2 | s->print = print_log; | |
| 268 | } | ||
| 269 | |||
| 270 | 28 | s->avio_context = NULL; | |
| 271 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 2 times.
|
28 | if (s->file_str) { |
| 272 |
1/2✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
|
26 | if (!strcmp("-", s->file_str)) { |
| 273 | 26 | ret = avio_open(&s->avio_context, "pipe:1", AVIO_FLAG_WRITE); | |
| 274 | } else { | ||
| 275 | ✗ | ret = avio_open(&s->avio_context, s->file_str, AVIO_FLAG_WRITE); | |
| 276 | } | ||
| 277 | |||
| 278 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | if (ret < 0) { |
| 279 | ✗ | av_log(ctx, AV_LOG_ERROR, "Could not open %s: %s\n", | |
| 280 | ✗ | s->file_str, av_err2str(ret)); | |
| 281 | ✗ | return ret; | |
| 282 | } | ||
| 283 | |||
| 284 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | if (s->direct) |
| 285 | ✗ | s->avio_context->direct = AVIO_FLAG_DIRECT; | |
| 286 | } | ||
| 287 | |||
| 288 | 28 | return 0; | |
| 289 | } | ||
| 290 | |||
| 291 | 28 | static av_cold void uninit(AVFilterContext *ctx) | |
| 292 | { | ||
| 293 | 28 | MetadataContext *s = ctx->priv; | |
| 294 | |||
| 295 | 28 | av_expr_free(s->expr); | |
| 296 | 28 | s->expr = NULL; | |
| 297 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 2 times.
|
28 | if (s->avio_context) { |
| 298 | 26 | avio_closep(&s->avio_context); | |
| 299 | } | ||
| 300 | 28 | } | |
| 301 | |||
| 302 | 67 | static int filter_frame(AVFilterLink *inlink, AVFrame *frame) | |
| 303 | { | ||
| 304 | 67 | FilterLink *inl = ff_filter_link(inlink); | |
| 305 | 67 | AVFilterContext *ctx = inlink->dst; | |
| 306 | 67 | AVFilterLink *outlink = ctx->outputs[0]; | |
| 307 | 67 | MetadataContext *s = ctx->priv; | |
| 308 | 67 | AVDictionary **metadata = &frame->metadata; | |
| 309 | const AVDictionaryEntry *e; | ||
| 310 | |||
| 311 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 67 times.
|
67 | e = av_dict_get(*metadata, !s->key ? "" : s->key, NULL, |
| 312 |
1/2✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
|
67 | !s->key ? AV_DICT_IGNORE_SUFFIX: 0); |
| 313 | |||
| 314 |
1/6✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 67 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
67 | switch (s->mode) { |
| 315 | ✗ | case METADATA_SELECT: | |
| 316 | ✗ | if (!s->value && e && e->value) { | |
| 317 | ✗ | return ff_filter_frame(outlink, frame); | |
| 318 | ✗ | } else if (s->value && e && e->value && | |
| 319 | ✗ | s->compare(s, e->value, s->value)) { | |
| 320 | ✗ | return ff_filter_frame(outlink, frame); | |
| 321 | } | ||
| 322 | ✗ | break; | |
| 323 | ✗ | case METADATA_ADD: | |
| 324 | ✗ | if (e && e->value) { | |
| 325 | ; | ||
| 326 | } else { | ||
| 327 | ✗ | av_dict_set(metadata, s->key, s->value, 0); | |
| 328 | } | ||
| 329 | ✗ | return ff_filter_frame(outlink, frame); | |
| 330 | ✗ | case METADATA_MODIFY: | |
| 331 | ✗ | if (e && e->value) { | |
| 332 | ✗ | av_dict_set(metadata, s->key, s->value, 0); | |
| 333 | } | ||
| 334 | ✗ | return ff_filter_frame(outlink, frame); | |
| 335 | 67 | case METADATA_PRINT: | |
| 336 |
3/4✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✓ Branch 3 taken 4 times.
|
130 | if (!s->key && e) { |
| 337 | 63 | s->print(ctx, "frame:%-4"PRId64" pts:%-7s pts_time:%s\n", | |
| 338 | 63 | inl->frame_count_out, av_ts2str(frame->pts), av_ts2timestr(frame->pts, &inlink->time_base)); | |
| 339 | 63 | s->print(ctx, "%s=%s\n", e->key, e->value); | |
| 340 |
2/2✓ Branch 1 taken 281 times.
✓ Branch 2 taken 63 times.
|
344 | while ((e = av_dict_iterate(*metadata, e)) != NULL) { |
| 341 | 281 | s->print(ctx, "%s=%s\n", e->key, e->value); | |
| 342 | } | ||
| 343 |
1/10✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
4 | } else if (e && e->value && (!s->value || (e->value && s->compare(s, e->value, s->value)))) { |
| 344 | ✗ | s->print(ctx, "frame:%-4"PRId64" pts:%-7s pts_time:%s\n", | |
| 345 | ✗ | inl->frame_count_out, av_ts2str(frame->pts), av_ts2timestr(frame->pts, &inlink->time_base)); | |
| 346 | ✗ | s->print(ctx, "%s=%s\n", s->key, e->value); | |
| 347 | } | ||
| 348 | 67 | return ff_filter_frame(outlink, frame); | |
| 349 | ✗ | case METADATA_DELETE: | |
| 350 | ✗ | if (!s->key) { | |
| 351 | ✗ | av_dict_free(metadata); | |
| 352 | ✗ | } else if (e && e->value && (!s->value || s->compare(s, e->value, s->value))) { | |
| 353 | ✗ | av_dict_set(metadata, s->key, NULL, 0); | |
| 354 | } | ||
| 355 | ✗ | return ff_filter_frame(outlink, frame); | |
| 356 | ✗ | default: | |
| 357 | ✗ | av_assert0(0); | |
| 358 | }; | ||
| 359 | |||
| 360 | ✗ | av_frame_free(&frame); | |
| 361 | |||
| 362 | ✗ | return 0; | |
| 363 | } | ||
| 364 | |||
| 365 | #if CONFIG_AMETADATA_FILTER | ||
| 366 | |||
| 367 | DEFINE_OPTIONS(ametadata, AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM); | ||
| 368 | AVFILTER_DEFINE_CLASS(ametadata); | ||
| 369 | |||
| 370 | static const AVFilterPad ainputs[] = { | ||
| 371 | { | ||
| 372 | .name = "default", | ||
| 373 | .type = AVMEDIA_TYPE_AUDIO, | ||
| 374 | .filter_frame = filter_frame, | ||
| 375 | }, | ||
| 376 | }; | ||
| 377 | |||
| 378 | const FFFilter ff_af_ametadata = { | ||
| 379 | .p.name = "ametadata", | ||
| 380 | .p.description = NULL_IF_CONFIG_SMALL("Manipulate audio frame metadata."), | ||
| 381 | .p.priv_class = &ametadata_class, | ||
| 382 | .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | | ||
| 383 | AVFILTER_FLAG_METADATA_ONLY, | ||
| 384 | .priv_size = sizeof(MetadataContext), | ||
| 385 | .init = init, | ||
| 386 | .uninit = uninit, | ||
| 387 | FILTER_INPUTS(ainputs), | ||
| 388 | FILTER_OUTPUTS(ff_audio_default_filterpad), | ||
| 389 | }; | ||
| 390 | #endif /* CONFIG_AMETADATA_FILTER */ | ||
| 391 | |||
| 392 | #if CONFIG_METADATA_FILTER | ||
| 393 | |||
| 394 | DEFINE_OPTIONS(metadata, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM); | ||
| 395 | AVFILTER_DEFINE_CLASS(metadata); | ||
| 396 | |||
| 397 | static const AVFilterPad inputs[] = { | ||
| 398 | { | ||
| 399 | .name = "default", | ||
| 400 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 401 | .filter_frame = filter_frame, | ||
| 402 | }, | ||
| 403 | }; | ||
| 404 | |||
| 405 | const FFFilter ff_vf_metadata = { | ||
| 406 | .p.name = "metadata", | ||
| 407 | .p.description = NULL_IF_CONFIG_SMALL("Manipulate video frame metadata."), | ||
| 408 | .p.priv_class = &metadata_class, | ||
| 409 | .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | | ||
| 410 | AVFILTER_FLAG_METADATA_ONLY, | ||
| 411 | .priv_size = sizeof(MetadataContext), | ||
| 412 | .init = init, | ||
| 413 | .uninit = uninit, | ||
| 414 | FILTER_INPUTS(inputs), | ||
| 415 | FILTER_OUTPUTS(ff_video_default_filterpad), | ||
| 416 | }; | ||
| 417 | #endif /* CONFIG_METADATA_FILTER */ | ||
| 418 |