Browse code

avfilter: add fieldhint filter

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

Paul B Mahol authored on 2016/02/15 03:02:42
Showing 6 changed files
... ...
@@ -3,6 +3,7 @@ releases are sorted from youngest to oldest.
3 3
 
4 4
 version <next>:
5 5
 - DXVA2-accelerated HEVC Main10 decoding
6
+- fieldhint filter
6 7
 
7 8
 
8 9
 version 3.0:
... ...
@@ -6637,6 +6637,50 @@ Specify whether to extract the top (if the value is @code{0} or
6637 6637
 @code{bottom}).
6638 6638
 @end table
6639 6639
 
6640
+@section fieldhint
6641
+
6642
+Create new frames by copying the top and bottom fields from surrounding frames
6643
+supplied as numbers by the hint file.
6644
+
6645
+@table @option
6646
+@item hint
6647
+Set file containing hints: absolute/relative frame numbers.
6648
+
6649
+There must be one line for each frame in a clip. Each line must contain two
6650
+numbers separated by the comma, optionally followed by @code{-} or @code{+}.
6651
+Numbers supplied on each line of file can not be out of [N-1,N+1] where N
6652
+is current frame number for @code{absolute} mode or out of [-1, 1] range
6653
+for @code{relative} mode. First number tells from which frame to pick up top
6654
+field and second number tells from which frame to pick up bottom field.
6655
+
6656
+If optionally followed by @code{+} output frame will be marked as interlaced,
6657
+else if followed by @code{-} output frame will be marked as progressive, else
6658
+it will be marked same as input frame.
6659
+If line starts with @code{#} or @code{;} that line is skipped.
6660
+
6661
+@item mode
6662
+Can be item @code{absolute} or @code{relative}. Default is @code{absolute}.
6663
+@end table
6664
+
6665
+Example of first several lines of @code{hint} file for @code{relative} mode:
6666
+@example
6667
+0,0 - # first frame
6668
+1,0 - # second frame, use third's frame top field and second's frame bottom field
6669
+1,0 - # third frame, use fourth's frame top field and third's frame bottom field
6670
+1,0 -
6671
+0,0 -
6672
+0,0 -
6673
+1,0 -
6674
+1,0 -
6675
+1,0 -
6676
+0,0 -
6677
+0,0 -
6678
+1,0 -
6679
+1,0 -
6680
+1,0 -
6681
+0,0 -
6682
+@end example
6683
+
6640 6684
 @section fieldmatch
6641 6685
 
6642 6686
 Field matching filter for inverse telecine. It is meant to reconstruct the
... ...
@@ -153,6 +153,7 @@ OBJS-$(CONFIG_EXTRACTPLANES_FILTER)          += vf_extractplanes.o
153 153
 OBJS-$(CONFIG_FADE_FILTER)                   += vf_fade.o
154 154
 OBJS-$(CONFIG_FFTFILT_FILTER)                += vf_fftfilt.o
155 155
 OBJS-$(CONFIG_FIELD_FILTER)                  += vf_field.o
156
+OBJS-$(CONFIG_FIELDHINT_FILTER)              += vf_fieldhint.o
156 157
 OBJS-$(CONFIG_FIELDMATCH_FILTER)             += vf_fieldmatch.o
157 158
 OBJS-$(CONFIG_FIELDORDER_FILTER)             += vf_fieldorder.o
158 159
 OBJS-$(CONFIG_FIND_RECT_FILTER)              += vf_find_rect.o lavfutils.o
... ...
@@ -174,6 +174,7 @@ void avfilter_register_all(void)
174 174
     REGISTER_FILTER(FADE,           fade,           vf);
175 175
     REGISTER_FILTER(FFTFILT,        fftfilt,        vf);
176 176
     REGISTER_FILTER(FIELD,          field,          vf);
177
+    REGISTER_FILTER(FIELDHINT,      fieldhint,      vf);
177 178
     REGISTER_FILTER(FIELDMATCH,     fieldmatch,     vf);
178 179
     REGISTER_FILTER(FIELDORDER,     fieldorder,     vf);
179 180
     REGISTER_FILTER(FIND_RECT,      find_rect,      vf);
... ...
@@ -30,7 +30,7 @@
30 30
 #include "libavutil/version.h"
31 31
 
32 32
 #define LIBAVFILTER_VERSION_MAJOR   6
33
-#define LIBAVFILTER_VERSION_MINOR  31
33
+#define LIBAVFILTER_VERSION_MINOR  32
34 34
 #define LIBAVFILTER_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
37 37
new file mode 100644
... ...
@@ -0,0 +1,280 @@
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
+#include "libavutil/imgutils.h"
21
+#include "libavutil/internal.h"
22
+#include "libavutil/opt.h"
23
+#include "libavutil/pixdesc.h"
24
+#include "avfilter.h"
25
+#include "internal.h"
26
+#include "video.h"
27
+
28
+typedef struct FieldHintContext {
29
+    const AVClass *class;
30
+
31
+    char *hint_file_str;
32
+    FILE *hint;
33
+    int mode;
34
+
35
+    AVFrame *frame[3];
36
+
37
+    int64_t line;
38
+    int nb_planes;
39
+    int eof;
40
+    int planewidth[4];
41
+    int planeheight[4];
42
+} FieldHintContext;
43
+
44
+#define OFFSET(x) offsetof(FieldHintContext, x)
45
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
46
+
47
+static const AVOption fieldhint_options[] = {
48
+    { "hint", "set hint file", OFFSET(hint_file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
49
+    { "mode", "set hint mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "mode" },
50
+    {   "absolute", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" },
51
+    {   "relative", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" },
52
+    { NULL }
53
+};
54
+
55
+AVFILTER_DEFINE_CLASS(fieldhint);
56
+
57
+static av_cold int init(AVFilterContext *ctx)
58
+{
59
+    FieldHintContext *s = ctx->priv;
60
+    int ret;
61
+
62
+    if (!s->hint_file_str) {
63
+        av_log(ctx, AV_LOG_ERROR, "Hint file must be set.\n");
64
+        return AVERROR(EINVAL);
65
+    }
66
+    s->hint = fopen(s->hint_file_str, "r");
67
+    if (!s->hint) {
68
+        ret = AVERROR(errno);
69
+        av_log(ctx, AV_LOG_ERROR, "%s: %s\n", s->hint_file_str, av_err2str(ret));
70
+        return ret;
71
+    }
72
+
73
+    return 0;
74
+}
75
+
76
+static int query_formats(AVFilterContext *ctx)
77
+{
78
+    AVFilterFormats *pix_fmts = NULL;
79
+    int fmt, ret;
80
+
81
+    for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) {
82
+        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
83
+        if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL ||
84
+              desc->flags & AV_PIX_FMT_FLAG_PAL     ||
85
+              desc->flags & AV_PIX_FMT_FLAG_BITSTREAM) &&
86
+            (ret = ff_add_format(&pix_fmts, fmt)) < 0)
87
+            return ret;
88
+    }
89
+
90
+    return ff_set_common_formats(ctx, pix_fmts);
91
+}
92
+
93
+static int config_input(AVFilterLink *inlink)
94
+{
95
+    FieldHintContext *s = inlink->dst->priv;
96
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
97
+    int ret;
98
+
99
+    if ((ret = av_image_fill_linesizes(s->planewidth, inlink->format, inlink->w)) < 0)
100
+        return ret;
101
+
102
+    s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
103
+    s->planeheight[0] = s->planeheight[3] = inlink->h;
104
+
105
+    s->nb_planes = av_pix_fmt_count_planes(inlink->format);
106
+
107
+    return 0;
108
+}
109
+
110
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
111
+{
112
+    AVFilterContext *ctx = inlink->dst;
113
+    AVFilterLink *outlink = ctx->outputs[0];
114
+    FieldHintContext *s = ctx->priv;
115
+    AVFrame *out, *top, *bottom;
116
+    char buf[1024] = { 0 };
117
+    int64_t tf, bf;
118
+    char hint = '=';
119
+    int p;
120
+
121
+    av_frame_free(&s->frame[0]);
122
+    s->frame[0] = s->frame[1];
123
+    s->frame[1] = s->frame[2];
124
+    s->frame[2] = in;
125
+    if (!s->frame[1])
126
+        return 0;
127
+    else if (!s->frame[0]) {
128
+        s->frame[0] = av_frame_clone(s->frame[1]);
129
+        if (!s->frame[0])
130
+            return AVERROR(ENOMEM);
131
+    }
132
+
133
+    while (1) {
134
+        if (fgets(buf, sizeof(buf)-1, s->hint)) {
135
+            s->line++;
136
+            if (buf[0] == '#' || buf[0] == ';') {
137
+                continue;
138
+            } else if (sscanf(buf, "%"PRId64",%"PRId64" %c", &tf, &bf, &hint) == 3) {
139
+                ;
140
+            } else if (sscanf(buf, "%"PRId64",%"PRId64"", &tf, &bf) == 2) {
141
+                ;
142
+            } else {
143
+                av_log(ctx, AV_LOG_ERROR, "Invalid entry at line %"PRId64".\n", s->line);
144
+                return AVERROR_INVALIDDATA;
145
+            }
146
+            switch (s->mode) {
147
+            case 0:
148
+                if (tf > outlink->frame_count + 1 || tf < FFMAX(0, outlink->frame_count - 1) ||
149
+                    bf > outlink->frame_count + 1 || bf < FFMAX(0, outlink->frame_count - 1)) {
150
+                    av_log(ctx, AV_LOG_ERROR, "Out of range frames %"PRId64" and/or %"PRId64" on line %"PRId64" for %"PRId64". input frame.\n", tf, bf, s->line, inlink->frame_count);
151
+                    return AVERROR_INVALIDDATA;
152
+                }
153
+                break;
154
+            case 1:
155
+                if (tf > 1 || tf < -1 ||
156
+                    bf > 1 || bf < -1) {
157
+                    av_log(ctx, AV_LOG_ERROR, "Out of range %"PRId64" and/or %"PRId64" on line %"PRId64" for %"PRId64". input frame.\n", tf, bf, s->line, inlink->frame_count);
158
+                    return AVERROR_INVALIDDATA;
159
+                }
160
+            };
161
+            break;
162
+        } else {
163
+            av_log(ctx, AV_LOG_ERROR, "Missing entry for %"PRId64". input frame.\n", inlink->frame_count);
164
+            return AVERROR_INVALIDDATA;
165
+        }
166
+    }
167
+
168
+    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
169
+    if (!out)
170
+        return AVERROR(ENOMEM);
171
+    av_frame_copy_props(out, s->frame[1]);
172
+
173
+    switch (s->mode) {
174
+    case 0:
175
+        top    = s->frame[1 + tf - outlink->frame_count];
176
+        bottom = s->frame[1 + bf - outlink->frame_count];
177
+        break;
178
+    case 1:
179
+        top    = s->frame[1 + tf];
180
+        bottom = s->frame[1 + bf];
181
+        break;
182
+    }
183
+
184
+    switch (hint) {
185
+    case '+':
186
+        out->interlaced_frame = 1;
187
+        break;
188
+    case '-':
189
+        out->interlaced_frame = 0;
190
+        break;
191
+    case '=':
192
+        break;
193
+    default:
194
+        av_log(ctx, AV_LOG_ERROR, "Invalid hint: %c.\n", hint);
195
+        return AVERROR(EINVAL);
196
+    }
197
+
198
+    for (p = 0; p < s->nb_planes; p++) {
199
+        av_image_copy_plane(out->data[p],
200
+                            out->linesize[p] * 2,
201
+                            top->data[p],
202
+                            top->linesize[p] * 2,
203
+                            s->planewidth[p],
204
+                            (s->planeheight[p] + 1) / 2);
205
+        av_image_copy_plane(out->data[p] + out->linesize[p],
206
+                            out->linesize[p] * 2,
207
+                            bottom->data[p] + bottom->linesize[p],
208
+                            bottom->linesize[p] * 2,
209
+                            s->planewidth[p],
210
+                            (s->planeheight[p] + 1) / 2);
211
+    }
212
+
213
+    return ff_filter_frame(outlink, out);
214
+}
215
+
216
+static int request_frame(AVFilterLink *outlink)
217
+{
218
+    AVFilterContext *ctx = outlink->src;
219
+    FieldHintContext *s = ctx->priv;
220
+    int ret;
221
+
222
+    if (s->eof)
223
+        return AVERROR_EOF;
224
+
225
+    ret = ff_request_frame(ctx->inputs[0]);
226
+    if (ret == AVERROR_EOF && s->frame[2]) {
227
+        AVFrame *next = av_frame_clone(s->frame[2]);
228
+        if (!next)
229
+            return AVERROR(ENOMEM);
230
+        ret = filter_frame(ctx->inputs[0], next);
231
+        s->eof = 1;
232
+    }
233
+
234
+    return ret;
235
+}
236
+
237
+static av_cold void uninit(AVFilterContext *ctx)
238
+{
239
+    FieldHintContext *s = ctx->priv;
240
+
241
+    if (s->hint)
242
+        fclose(s->hint);
243
+    s->hint = NULL;
244
+
245
+    av_frame_free(&s->frame[0]);
246
+    av_frame_free(&s->frame[1]);
247
+    av_frame_free(&s->frame[2]);
248
+}
249
+
250
+static const AVFilterPad inputs[] = {
251
+    {
252
+        .name         = "default",
253
+        .type         = AVMEDIA_TYPE_VIDEO,
254
+        .config_props = config_input,
255
+        .filter_frame = filter_frame,
256
+    },
257
+    { NULL }
258
+};
259
+
260
+static const AVFilterPad outputs[] = {
261
+    {
262
+        .name          = "default",
263
+        .type          = AVMEDIA_TYPE_VIDEO,
264
+        .request_frame = request_frame,
265
+    },
266
+    { NULL }
267
+};
268
+
269
+AVFilter ff_vf_fieldhint = {
270
+    .name          = "fieldhint",
271
+    .description   = NULL_IF_CONFIG_SMALL("Field matching using hints."),
272
+    .priv_size     = sizeof(FieldHintContext),
273
+    .priv_class    = &fieldhint_class,
274
+    .init          = init,
275
+    .uninit        = uninit,
276
+    .query_formats = query_formats,
277
+    .inputs        = inputs,
278
+    .outputs       = outputs,
279
+};