GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavfilter/vf_detelecine.c Lines: 0 174 0.0 %
Date: 2021-06-22 13:37:07 Branches: 0 86 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 *formats = NULL;
128
    int ret;
129
130
    ret = ff_formats_pixdesc_filter(&formats, 0,
131
                                    AV_PIX_FMT_FLAG_BITSTREAM |
132
                                    AV_PIX_FMT_FLAG_PAL |
133
                                    AV_PIX_FMT_FLAG_HWACCEL);
134
    if (ret < 0)
135
        return ret;
136
    return ff_set_common_formats(ctx, formats);
137
}
138
139
static int config_input(AVFilterLink *inlink)
140
{
141
    DetelecineContext *s = inlink->dst->priv;
142
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
143
    int ret;
144
145
    s->temp = ff_get_video_buffer(inlink, inlink->w, inlink->h);
146
    if (!s->temp)
147
        return AVERROR(ENOMEM);
148
149
    s->frame[0] = ff_get_video_buffer(inlink, inlink->w, inlink->h);
150
    if (!s->frame[0])
151
        return AVERROR(ENOMEM);
152
153
    s->frame[1] = ff_get_video_buffer(inlink, inlink->w, inlink->h);
154
    if (!s->frame[1])
155
        return AVERROR(ENOMEM);
156
157
    if ((ret = av_image_fill_linesizes(s->stride, inlink->format, inlink->w)) < 0)
158
        return ret;
159
160
    s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
161
    s->planeheight[0] = s->planeheight[3] = inlink->h;
162
163
    s->nb_planes = av_pix_fmt_count_planes(inlink->format);
164
165
    return 0;
166
}
167
168
static int config_output(AVFilterLink *outlink)
169
{
170
    AVFilterContext *ctx = outlink->src;
171
    DetelecineContext *s = ctx->priv;
172
    const AVFilterLink *inlink = ctx->inputs[0];
173
    AVRational fps = inlink->frame_rate;
174
175
    if (!fps.num || !fps.den) {
176
        av_log(ctx, AV_LOG_ERROR, "The input needs a constant frame rate; "
177
               "current rate of %d/%d is invalid\n", fps.num, fps.den);
178
        return AVERROR(EINVAL);
179
    }
180
    fps = av_mul_q(fps, av_inv_q(s->pts));
181
    av_log(ctx, AV_LOG_VERBOSE, "FPS: %d/%d -> %d/%d\n",
182
           inlink->frame_rate.num, inlink->frame_rate.den, fps.num, fps.den);
183
184
    outlink->frame_rate = fps;
185
    outlink->time_base = av_mul_q(inlink->time_base, s->pts);
186
    av_log(ctx, AV_LOG_VERBOSE, "TB: %d/%d -> %d/%d\n",
187
           inlink->time_base.num, inlink->time_base.den, outlink->time_base.num, outlink->time_base.den);
188
189
    s->ts_unit = av_inv_q(av_mul_q(fps, outlink->time_base));
190
191
    return 0;
192
}
193
194
static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref)
195
{
196
    AVFilterContext *ctx = inlink->dst;
197
    AVFilterLink *outlink = ctx->outputs[0];
198
    DetelecineContext *s = ctx->priv;
199
    int i, len = 0, ret = 0, out = 0;
200
201
    if (s->start_time == AV_NOPTS_VALUE)
202
        s->start_time = inpicref->pts;
203
204
    if (s->nskip_fields >= 2) {
205
        s->nskip_fields -= 2;
206
        av_frame_free(&inpicref);
207
        return 0;
208
    } else if (s->nskip_fields >= 1) {
209
        for (i = 0; i < s->nb_planes; i++) {
210
            av_image_copy_plane(s->temp->data[i], s->temp->linesize[i],
211
                                inpicref->data[i], inpicref->linesize[i],
212
                                s->stride[i],
213
                                s->planeheight[i]);
214
        }
215
        s->occupied = 1;
216
        s->nskip_fields--;
217
        av_frame_free(&inpicref);
218
        return 0;
219
    }
220
221
    if (s->nskip_fields == 0) {
222
        len = s->init_len;
223
        s->init_len = 0;
224
        while(!len && s->pattern[s->pattern_pos]) {
225
            len = s->pattern[s->pattern_pos] - '0';
226
            s->pattern_pos++;
227
        }
228
229
        if (!s->pattern[s->pattern_pos])
230
            s->pattern_pos = 0;
231
232
        if(!len) { // do not output any field as the entire pattern is zero
233
            av_frame_free(&inpicref);
234
            return 0;
235
        }
236
237
        if (len == 1 && s->occupied) {
238
            s->occupied = 0;
239
            // output THIS image as-is
240
            for (i = 0; i < s->nb_planes; i++)
241
                av_image_copy_plane(s->frame[out]->data[i], s->frame[out]->linesize[i],
242
                                    s->temp->data[i], s->temp->linesize[i],
243
                                    s->stride[i],
244
                                    s->planeheight[i]);
245
            len = 0;
246
            while(!len && s->pattern[s->pattern_pos]) {
247
                len = s->pattern[s->pattern_pos] - '0';
248
                s->pattern_pos++;
249
            }
250
251
            if (!s->pattern[s->pattern_pos])
252
                s->pattern_pos = 0;
253
254
            s->occupied = 0;
255
            ++out;
256
        }
257
258
        if (s->occupied) {
259
            for (i = 0; i < s->nb_planes; i++) {
260
                // fill in the EARLIER field from the new pic
261
                av_image_copy_plane(s->frame[out]->data[i] + s->frame[out]->linesize[i] * s->first_field,
262
                                    s->frame[out]->linesize[i] * 2,
263
                                    inpicref->data[i] + inpicref->linesize[i] * s->first_field,
264
                                    inpicref->linesize[i] * 2,
265
                                    s->stride[i],
266
                                    (s->planeheight[i] - s->first_field + 1) / 2);
267
                // fill in the LATER field from the buffered pic
268
                av_image_copy_plane(s->frame[out]->data[i] + s->frame[out]->linesize[i] * !s->first_field,
269
                                    s->frame[out]->linesize[i] * 2,
270
                                    s->temp->data[i] + s->temp->linesize[i] * !s->first_field,
271
                                    s->temp->linesize[i] * 2,
272
                                    s->stride[i],
273
                                    (s->planeheight[i] - !s->first_field + 1) / 2);
274
            }
275
276
            s->occupied = 0;
277
            if (len <= 2) {
278
                for (i = 0; i < s->nb_planes; i++) {
279
                    av_image_copy_plane(s->temp->data[i], s->temp->linesize[i],
280
                                        inpicref->data[i], inpicref->linesize[i],
281
                                        s->stride[i],
282
                                        s->planeheight[i]);
283
                }
284
                s->occupied = 1;
285
            }
286
            ++out;
287
            len = (len >= 3) ? len - 3 : 0;
288
        } else {
289
            if (len >= 2) {
290
                // output THIS image as-is
291
                for (i = 0; i < s->nb_planes; i++)
292
                    av_image_copy_plane(s->frame[out]->data[i], s->frame[out]->linesize[i],
293
                                        inpicref->data[i], inpicref->linesize[i],
294
                                        s->stride[i],
295
                                        s->planeheight[i]);
296
                len -= 2;
297
                ++out;
298
            } else if (len == 1) {
299
                // output THIS image as-is
300
                for (i = 0; i < s->nb_planes; i++)
301
                    av_image_copy_plane(s->frame[out]->data[i], s->frame[out]->linesize[i],
302
                                        inpicref->data[i], inpicref->linesize[i],
303
                                        s->stride[i],
304
                                        s->planeheight[i]);
305
306
                for (i = 0; i < s->nb_planes; i++) {
307
                    av_image_copy_plane(s->temp->data[i], s->temp->linesize[i],
308
                                        inpicref->data[i], inpicref->linesize[i],
309
                                        s->stride[i],
310
                                        s->planeheight[i]);
311
                }
312
                s->occupied = 1;
313
314
                len--;
315
                ++out;
316
            }
317
        }
318
319
        if (len == 1 && s->occupied)
320
        {
321
            len--;
322
            s->occupied = 0;
323
        }
324
    }
325
    s->nskip_fields = len;
326
327
    for (i = 0; i < out; ++i) {
328
        AVFrame *frame = av_frame_clone(s->frame[i]);
329
330
        if (!frame) {
331
            av_frame_free(&inpicref);
332
            return AVERROR(ENOMEM);
333
        }
334
335
        av_frame_copy_props(frame, inpicref);
336
        frame->pts = ((s->start_time == AV_NOPTS_VALUE) ? 0 : s->start_time) +
337
                     av_rescale(outlink->frame_count_in, s->ts_unit.num,
338
                                s->ts_unit.den);
339
        ret = ff_filter_frame(outlink, frame);
340
    }
341
342
    av_frame_free(&inpicref);
343
344
    return ret;
345
}
346
347
static av_cold void uninit(AVFilterContext *ctx)
348
{
349
    DetelecineContext *s = ctx->priv;
350
351
    av_frame_free(&s->temp);
352
    av_frame_free(&s->frame[0]);
353
    av_frame_free(&s->frame[1]);
354
}
355
356
static const AVFilterPad detelecine_inputs[] = {
357
    {
358
        .name          = "default",
359
        .type          = AVMEDIA_TYPE_VIDEO,
360
        .filter_frame  = filter_frame,
361
        .config_props  = config_input,
362
    },
363
    { NULL }
364
};
365
366
static const AVFilterPad detelecine_outputs[] = {
367
    {
368
        .name          = "default",
369
        .type          = AVMEDIA_TYPE_VIDEO,
370
        .config_props  = config_output,
371
    },
372
    { NULL }
373
};
374
375
const AVFilter ff_vf_detelecine = {
376
    .name          = "detelecine",
377
    .description   = NULL_IF_CONFIG_SMALL("Apply an inverse telecine pattern."),
378
    .priv_size     = sizeof(DetelecineContext),
379
    .priv_class    = &detelecine_class,
380
    .init          = init,
381
    .uninit        = uninit,
382
    .query_formats = query_formats,
383
    .inputs        = detelecine_inputs,
384
    .outputs       = detelecine_outputs,
385
};