GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavfilter/vf_detelecine.c Lines: 0 176 0.0 %
Date: 2020-08-13 15:06:06 Branches: 0 94 0.0 %

Line Branch Exec Source
1
/*
2
 * Copyright (c) 2015 Himangi Saraogi <himangi774@gmail.com>
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 detelecine filter.
23
 */
24
25
26
#include "libavutil/avstring.h"
27
#include "libavutil/imgutils.h"
28
#include "libavutil/opt.h"
29
#include "libavutil/pixdesc.h"
30
#include "avfilter.h"
31
#include "formats.h"
32
#include "internal.h"
33
#include "video.h"
34
35
typedef struct DetelecineContext {
36
    const AVClass *class;
37
    int first_field;
38
    char *pattern;
39
    int start_frame;
40
    int init_len;
41
    unsigned int pattern_pos;
42
    unsigned int nskip_fields;
43
    int64_t start_time;
44
45
    AVRational pts;
46
    AVRational ts_unit;
47
    int occupied;
48
49
    int nb_planes;
50
    int planeheight[4];
51
    int stride[4];
52
53
    AVFrame *frame[2];
54
    AVFrame *temp;
55
} DetelecineContext;
56
57
#define OFFSET(x) offsetof(DetelecineContext, x)
58
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
59
60
static const AVOption detelecine_options[] = {
61
    {"first_field", "select first field", OFFSET(first_field), AV_OPT_TYPE_INT,   {.i64=0}, 0, 1, FLAGS, "field"},
62
        {"top",    "select top field first",                0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"},
63
        {"t",      "select top field first",                0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"},
64
        {"bottom", "select bottom field first",             0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"},
65
        {"b",      "select bottom field first",             0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"},
66
    {"pattern", "pattern that describe for how many fields a frame is to be displayed", OFFSET(pattern), AV_OPT_TYPE_STRING, {.str="23"}, 0, 0, FLAGS},
67
    {"start_frame", "position of first frame with respect to the pattern if stream is cut", OFFSET(start_frame), AV_OPT_TYPE_INT, {.i64=0}, 0, 13, FLAGS},
68
    {NULL}
69
};
70
71
AVFILTER_DEFINE_CLASS(detelecine);
72
73
static av_cold int init(AVFilterContext *ctx)
74
{
75
    DetelecineContext *s = ctx->priv;
76
    const char *p;
77
    int max = 0;
78
    int sum = 0;
79
80
    if (!strlen(s->pattern)) {
81
        av_log(ctx, AV_LOG_ERROR, "No pattern provided.\n");
82
        return AVERROR_INVALIDDATA;
83
    }
84
85
    for (p = s->pattern; *p; p++) {
86
        if (!av_isdigit(*p)) {
87
            av_log(ctx, AV_LOG_ERROR, "Provided pattern includes non-numeric characters.\n");
88
            return AVERROR_INVALIDDATA;
89
        }
90
91
        sum += *p - '0';
92
        max = FFMAX(*p - '0', max);
93
        s->pts.num += *p - '0';
94
        s->pts.den += 2;
95
    }
96
97
    if (s->start_frame >= sum) {
98
        av_log(ctx, AV_LOG_ERROR, "Provided start_frame is too big.\n");
99
        return AVERROR_INVALIDDATA;
100
    }
101
102
    s->nskip_fields = 0;
103
    s->pattern_pos = 0;
104
    s->start_time = AV_NOPTS_VALUE;
105
    s->init_len = 0;
106
107
    if (s->start_frame != 0) {
108
        int nfields = 0;
109
        for (p = s->pattern; *p; p++) {
110
            nfields += *p - '0';
111
            s->pattern_pos++;
112
            if (nfields >= 2*s->start_frame) {
113
                s->init_len = nfields - 2*s->start_frame;
114
                break;
115
            }
116
        }
117
    }
118
119
    av_log(ctx, AV_LOG_INFO, "Detelecine pattern %s removes up to %d frames per frame, pts advance factor: %d/%d\n",
120
           s->pattern, (max + 1) / 2, s->pts.num, s->pts.den);
121
122
    return 0;
123
}
124
125
static int query_formats(AVFilterContext *ctx)
126
{
127
    AVFilterFormats *pix_fmts = NULL;
128
    int fmt, ret;
129
130
    for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) {
131
        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
132
        if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL ||
133
              desc->flags & AV_PIX_FMT_FLAG_PAL     ||
134
              desc->flags & AV_PIX_FMT_FLAG_BITSTREAM) &&
135
             (ret = ff_add_format(&pix_fmts, fmt)) < 0)
136
            return ret;
137
    }
138
139
    return ff_set_common_formats(ctx, pix_fmts);
140
}
141
142
static int config_input(AVFilterLink *inlink)
143
{
144
    DetelecineContext *s = inlink->dst->priv;
145
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
146
    int ret;
147
148
    s->temp = ff_get_video_buffer(inlink, inlink->w, inlink->h);
149
    if (!s->temp)
150
        return AVERROR(ENOMEM);
151
152
    s->frame[0] = ff_get_video_buffer(inlink, inlink->w, inlink->h);
153
    if (!s->frame[0])
154
        return AVERROR(ENOMEM);
155
156
    s->frame[1] = ff_get_video_buffer(inlink, inlink->w, inlink->h);
157
    if (!s->frame[1])
158
        return AVERROR(ENOMEM);
159
160
    if ((ret = av_image_fill_linesizes(s->stride, inlink->format, inlink->w)) < 0)
161
        return ret;
162
163
    s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
164
    s->planeheight[0] = s->planeheight[3] = inlink->h;
165
166
    s->nb_planes = av_pix_fmt_count_planes(inlink->format);
167
168
    return 0;
169
}
170
171
static int config_output(AVFilterLink *outlink)
172
{
173
    AVFilterContext *ctx = outlink->src;
174
    DetelecineContext *s = ctx->priv;
175
    const AVFilterLink *inlink = ctx->inputs[0];
176
    AVRational fps = inlink->frame_rate;
177
178
    if (!fps.num || !fps.den) {
179
        av_log(ctx, AV_LOG_ERROR, "The input needs a constant frame rate; "
180
               "current rate of %d/%d is invalid\n", fps.num, fps.den);
181
        return AVERROR(EINVAL);
182
    }
183
    fps = av_mul_q(fps, av_inv_q(s->pts));
184
    av_log(ctx, AV_LOG_VERBOSE, "FPS: %d/%d -> %d/%d\n",
185
           inlink->frame_rate.num, inlink->frame_rate.den, fps.num, fps.den);
186
187
    outlink->frame_rate = fps;
188
    outlink->time_base = av_mul_q(inlink->time_base, s->pts);
189
    av_log(ctx, AV_LOG_VERBOSE, "TB: %d/%d -> %d/%d\n",
190
           inlink->time_base.num, inlink->time_base.den, outlink->time_base.num, outlink->time_base.den);
191
192
    s->ts_unit = av_inv_q(av_mul_q(fps, outlink->time_base));
193
194
    return 0;
195
}
196
197
static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref)
198
{
199
    AVFilterContext *ctx = inlink->dst;
200
    AVFilterLink *outlink = ctx->outputs[0];
201
    DetelecineContext *s = ctx->priv;
202
    int i, len = 0, ret = 0, out = 0;
203
204
    if (s->start_time == AV_NOPTS_VALUE)
205
        s->start_time = inpicref->pts;
206
207
    if (s->nskip_fields >= 2) {
208
        s->nskip_fields -= 2;
209
        av_frame_free(&inpicref);
210
        return 0;
211
    } else if (s->nskip_fields >= 1) {
212
        for (i = 0; i < s->nb_planes; i++) {
213
            av_image_copy_plane(s->temp->data[i], s->temp->linesize[i],
214
                                inpicref->data[i], inpicref->linesize[i],
215
                                s->stride[i],
216
                                s->planeheight[i]);
217
        }
218
        s->occupied = 1;
219
        s->nskip_fields--;
220
        av_frame_free(&inpicref);
221
        return 0;
222
    }
223
224
    if (s->nskip_fields == 0) {
225
        len = s->init_len;
226
        s->init_len = 0;
227
        while(!len && s->pattern[s->pattern_pos]) {
228
            len = s->pattern[s->pattern_pos] - '0';
229
            s->pattern_pos++;
230
        }
231
232
        if (!s->pattern[s->pattern_pos])
233
            s->pattern_pos = 0;
234
235
        if(!len) { // do not output any field as the entire pattern is zero
236
            av_frame_free(&inpicref);
237
            return 0;
238
        }
239
240
        if (len == 1 && s->occupied) {
241
            s->occupied = 0;
242
            // output THIS image as-is
243
            for (i = 0; i < s->nb_planes; i++)
244
                av_image_copy_plane(s->frame[out]->data[i], s->frame[out]->linesize[i],
245
                                    s->temp->data[i], s->temp->linesize[i],
246
                                    s->stride[i],
247
                                    s->planeheight[i]);
248
            len = 0;
249
            while(!len && s->pattern[s->pattern_pos]) {
250
                len = s->pattern[s->pattern_pos] - '0';
251
                s->pattern_pos++;
252
            }
253
254
            if (!s->pattern[s->pattern_pos])
255
                s->pattern_pos = 0;
256
257
            s->occupied = 0;
258
            ++out;
259
        }
260
261
        if (s->occupied) {
262
            for (i = 0; i < s->nb_planes; i++) {
263
                // fill in the EARLIER field from the new pic
264
                av_image_copy_plane(s->frame[out]->data[i] + s->frame[out]->linesize[i] * s->first_field,
265
                                    s->frame[out]->linesize[i] * 2,
266
                                    inpicref->data[i] + inpicref->linesize[i] * s->first_field,
267
                                    inpicref->linesize[i] * 2,
268
                                    s->stride[i],
269
                                    (s->planeheight[i] - s->first_field + 1) / 2);
270
                // fill in the LATER field from the buffered pic
271
                av_image_copy_plane(s->frame[out]->data[i] + s->frame[out]->linesize[i] * !s->first_field,
272
                                    s->frame[out]->linesize[i] * 2,
273
                                    s->temp->data[i] + s->temp->linesize[i] * !s->first_field,
274
                                    s->temp->linesize[i] * 2,
275
                                    s->stride[i],
276
                                    (s->planeheight[i] - !s->first_field + 1) / 2);
277
            }
278
279
            s->occupied = 0;
280
            if (len <= 2) {
281
                for (i = 0; i < s->nb_planes; i++) {
282
                    av_image_copy_plane(s->temp->data[i], s->temp->linesize[i],
283
                                        inpicref->data[i], inpicref->linesize[i],
284
                                        s->stride[i],
285
                                        s->planeheight[i]);
286
                }
287
                s->occupied = 1;
288
            }
289
            ++out;
290
            len = (len >= 3) ? len - 3 : 0;
291
        } else {
292
            if (len >= 2) {
293
                // output THIS image as-is
294
                for (i = 0; i < s->nb_planes; i++)
295
                    av_image_copy_plane(s->frame[out]->data[i], s->frame[out]->linesize[i],
296
                                        inpicref->data[i], inpicref->linesize[i],
297
                                        s->stride[i],
298
                                        s->planeheight[i]);
299
                len -= 2;
300
                ++out;
301
            } else if (len == 1) {
302
                // output THIS image as-is
303
                for (i = 0; i < s->nb_planes; i++)
304
                    av_image_copy_plane(s->frame[out]->data[i], s->frame[out]->linesize[i],
305
                                        inpicref->data[i], inpicref->linesize[i],
306
                                        s->stride[i],
307
                                        s->planeheight[i]);
308
309
                for (i = 0; i < s->nb_planes; i++) {
310
                    av_image_copy_plane(s->temp->data[i], s->temp->linesize[i],
311
                                        inpicref->data[i], inpicref->linesize[i],
312
                                        s->stride[i],
313
                                        s->planeheight[i]);
314
                }
315
                s->occupied = 1;
316
317
                len--;
318
                ++out;
319
            }
320
        }
321
322
        if (len == 1 && s->occupied)
323
        {
324
            len--;
325
            s->occupied = 0;
326
        }
327
    }
328
    s->nskip_fields = len;
329
330
    for (i = 0; i < out; ++i) {
331
        AVFrame *frame = av_frame_clone(s->frame[i]);
332
333
        if (!frame) {
334
            av_frame_free(&inpicref);
335
            return AVERROR(ENOMEM);
336
        }
337
338
        av_frame_copy_props(frame, inpicref);
339
        frame->pts = ((s->start_time == AV_NOPTS_VALUE) ? 0 : s->start_time) +
340
                     av_rescale(outlink->frame_count_in, s->ts_unit.num,
341
                                s->ts_unit.den);
342
        ret = ff_filter_frame(outlink, frame);
343
    }
344
345
    av_frame_free(&inpicref);
346
347
    return ret;
348
}
349
350
static av_cold void uninit(AVFilterContext *ctx)
351
{
352
    DetelecineContext *s = ctx->priv;
353
354
    av_frame_free(&s->temp);
355
    av_frame_free(&s->frame[0]);
356
    av_frame_free(&s->frame[1]);
357
}
358
359
static const AVFilterPad detelecine_inputs[] = {
360
    {
361
        .name          = "default",
362
        .type          = AVMEDIA_TYPE_VIDEO,
363
        .filter_frame  = filter_frame,
364
        .config_props  = config_input,
365
    },
366
    { NULL }
367
};
368
369
static const AVFilterPad detelecine_outputs[] = {
370
    {
371
        .name          = "default",
372
        .type          = AVMEDIA_TYPE_VIDEO,
373
        .config_props  = config_output,
374
    },
375
    { NULL }
376
};
377
378
AVFilter ff_vf_detelecine = {
379
    .name          = "detelecine",
380
    .description   = NULL_IF_CONFIG_SMALL("Apply an inverse telecine pattern."),
381
    .priv_size     = sizeof(DetelecineContext),
382
    .priv_class    = &detelecine_class,
383
    .init          = init,
384
    .uninit        = uninit,
385
    .query_formats = query_formats,
386
    .inputs        = detelecine_inputs,
387
    .outputs       = detelecine_outputs,
388
};