yading@11
|
1 /*
|
yading@11
|
2 * Copyright (c) 2012 Rudolf Polzer
|
yading@11
|
3 * Copyright (c) 2013 Paul B Mahol
|
yading@11
|
4 *
|
yading@11
|
5 * This file is part of FFmpeg.
|
yading@11
|
6 *
|
yading@11
|
7 * FFmpeg is free software; you can redistribute it and/or
|
yading@11
|
8 * modify it under the terms of the GNU Lesser General Public
|
yading@11
|
9 * License as published by the Free Software Foundation; either
|
yading@11
|
10 * version 2.1 of the License, or (at your option) any later version.
|
yading@11
|
11 *
|
yading@11
|
12 * FFmpeg is distributed in the hope that it will be useful,
|
yading@11
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
yading@11
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
yading@11
|
15 * Lesser General Public License for more details.
|
yading@11
|
16 *
|
yading@11
|
17 * You should have received a copy of the GNU Lesser General Public
|
yading@11
|
18 * License along with FFmpeg; if not, write to the Free Software
|
yading@11
|
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
yading@11
|
20 */
|
yading@11
|
21
|
yading@11
|
22 /**
|
yading@11
|
23 * @file telecine filter, heavily based from mpv-player:TOOLS/vf_dlopen/telecine.c by
|
yading@11
|
24 * Rudolf Polzer.
|
yading@11
|
25 */
|
yading@11
|
26
|
yading@11
|
27 #include "libavutil/avstring.h"
|
yading@11
|
28 #include "libavutil/imgutils.h"
|
yading@11
|
29 #include "libavutil/opt.h"
|
yading@11
|
30 #include "libavutil/pixdesc.h"
|
yading@11
|
31 #include "avfilter.h"
|
yading@11
|
32 #include "formats.h"
|
yading@11
|
33 #include "internal.h"
|
yading@11
|
34 #include "video.h"
|
yading@11
|
35
|
yading@11
|
36 typedef struct {
|
yading@11
|
37 const AVClass *class;
|
yading@11
|
38 int first_field;
|
yading@11
|
39 char *pattern;
|
yading@11
|
40 unsigned int pattern_pos;
|
yading@11
|
41
|
yading@11
|
42 AVRational pts;
|
yading@11
|
43 double ts_unit;
|
yading@11
|
44 int out_cnt;
|
yading@11
|
45 int occupied;
|
yading@11
|
46 int64_t frame_count;
|
yading@11
|
47
|
yading@11
|
48 int nb_planes;
|
yading@11
|
49 int planeheight[4];
|
yading@11
|
50 int stride[4];
|
yading@11
|
51
|
yading@11
|
52 AVFrame *frame[5];
|
yading@11
|
53 AVFrame *temp;
|
yading@11
|
54 } TelecineContext;
|
yading@11
|
55
|
yading@11
|
56 #define OFFSET(x) offsetof(TelecineContext, x)
|
yading@11
|
57 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
|
yading@11
|
58
|
yading@11
|
59 static const AVOption telecine_options[] = {
|
yading@11
|
60 {"first_field", "select first field", OFFSET(first_field), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "field"},
|
yading@11
|
61 {"top", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"},
|
yading@11
|
62 {"t", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"},
|
yading@11
|
63 {"bottom", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"},
|
yading@11
|
64 {"b", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"},
|
yading@11
|
65 {"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},
|
yading@11
|
66 {NULL}
|
yading@11
|
67 };
|
yading@11
|
68
|
yading@11
|
69 AVFILTER_DEFINE_CLASS(telecine);
|
yading@11
|
70
|
yading@11
|
71 static av_cold int init(AVFilterContext *ctx)
|
yading@11
|
72 {
|
yading@11
|
73 TelecineContext *tc = ctx->priv;
|
yading@11
|
74 const char *p;
|
yading@11
|
75 int max = 0;
|
yading@11
|
76
|
yading@11
|
77 if (!strlen(tc->pattern)) {
|
yading@11
|
78 av_log(ctx, AV_LOG_ERROR, "No pattern provided.\n");
|
yading@11
|
79 return AVERROR_INVALIDDATA;
|
yading@11
|
80 }
|
yading@11
|
81
|
yading@11
|
82 for (p = tc->pattern; *p; p++) {
|
yading@11
|
83 if (!av_isdigit(*p)) {
|
yading@11
|
84 av_log(ctx, AV_LOG_ERROR, "Provided pattern includes non-numeric characters.\n");
|
yading@11
|
85 return AVERROR_INVALIDDATA;
|
yading@11
|
86 }
|
yading@11
|
87
|
yading@11
|
88 max = FFMAX(*p - '0', max);
|
yading@11
|
89 tc->pts.num += 2;
|
yading@11
|
90 tc->pts.den += *p - '0';
|
yading@11
|
91 }
|
yading@11
|
92
|
yading@11
|
93 tc->out_cnt = (max + 1) / 2;
|
yading@11
|
94 av_log(ctx, AV_LOG_INFO, "Telecine pattern %s yields up to %d frames per frame, pts advance factor: %d/%d\n",
|
yading@11
|
95 tc->pattern, tc->out_cnt, tc->pts.num, tc->pts.den);
|
yading@11
|
96
|
yading@11
|
97 return 0;
|
yading@11
|
98 }
|
yading@11
|
99
|
yading@11
|
100 static int query_formats(AVFilterContext *ctx)
|
yading@11
|
101 {
|
yading@11
|
102 AVFilterFormats *pix_fmts = NULL;
|
yading@11
|
103 int fmt;
|
yading@11
|
104
|
yading@11
|
105 for (fmt = 0; fmt < AV_PIX_FMT_NB; fmt++) {
|
yading@11
|
106 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
|
yading@11
|
107 if (!(desc->flags & PIX_FMT_HWACCEL))
|
yading@11
|
108 ff_add_format(&pix_fmts, fmt);
|
yading@11
|
109 }
|
yading@11
|
110
|
yading@11
|
111 ff_set_common_formats(ctx, pix_fmts);
|
yading@11
|
112 return 0;
|
yading@11
|
113 }
|
yading@11
|
114
|
yading@11
|
115 static int config_input(AVFilterLink *inlink)
|
yading@11
|
116 {
|
yading@11
|
117 TelecineContext *tc = inlink->dst->priv;
|
yading@11
|
118 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
|
yading@11
|
119 int i, ret;
|
yading@11
|
120
|
yading@11
|
121 tc->temp = ff_get_video_buffer(inlink, inlink->w, inlink->h);
|
yading@11
|
122 if (!tc->temp)
|
yading@11
|
123 return AVERROR(ENOMEM);
|
yading@11
|
124 for (i = 0; i < tc->out_cnt; i++) {
|
yading@11
|
125 tc->frame[i] = ff_get_video_buffer(inlink, inlink->w, inlink->h);
|
yading@11
|
126 if (!tc->frame[i])
|
yading@11
|
127 return AVERROR(ENOMEM);
|
yading@11
|
128 }
|
yading@11
|
129
|
yading@11
|
130 if ((ret = av_image_fill_linesizes(tc->stride, inlink->format, inlink->w)) < 0)
|
yading@11
|
131 return ret;
|
yading@11
|
132
|
yading@11
|
133 tc->planeheight[1] = tc->planeheight[2] = inlink->h >> desc->log2_chroma_h;
|
yading@11
|
134 tc->planeheight[0] = tc->planeheight[3] = inlink->h;
|
yading@11
|
135
|
yading@11
|
136 tc->nb_planes = av_pix_fmt_count_planes(inlink->format);
|
yading@11
|
137
|
yading@11
|
138 return 0;
|
yading@11
|
139 }
|
yading@11
|
140
|
yading@11
|
141 static int config_output(AVFilterLink *outlink)
|
yading@11
|
142 {
|
yading@11
|
143 AVFilterContext *ctx = outlink->src;
|
yading@11
|
144 TelecineContext *tc = ctx->priv;
|
yading@11
|
145 const AVFilterLink *inlink = ctx->inputs[0];
|
yading@11
|
146 AVRational fps = inlink->frame_rate;
|
yading@11
|
147
|
yading@11
|
148 if (!fps.num || !fps.den) {
|
yading@11
|
149 av_log(ctx, AV_LOG_ERROR, "The input needs a constant frame rate; "
|
yading@11
|
150 "current rate of %d/%d is invalid\n", fps.num, fps.den);
|
yading@11
|
151 return AVERROR(EINVAL);
|
yading@11
|
152 }
|
yading@11
|
153 fps = av_mul_q(fps, av_inv_q(tc->pts));
|
yading@11
|
154 av_log(ctx, AV_LOG_VERBOSE, "FPS: %d/%d -> %d/%d\n",
|
yading@11
|
155 inlink->frame_rate.num, inlink->frame_rate.den, fps.num, fps.den);
|
yading@11
|
156
|
yading@11
|
157 outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
|
yading@11
|
158 outlink->frame_rate = fps;
|
yading@11
|
159 outlink->time_base = av_mul_q(inlink->time_base, tc->pts);
|
yading@11
|
160
|
yading@11
|
161 tc->ts_unit = av_q2d(av_inv_q(av_mul_q(fps, outlink->time_base)));
|
yading@11
|
162
|
yading@11
|
163 return 0;
|
yading@11
|
164 }
|
yading@11
|
165
|
yading@11
|
166 static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref)
|
yading@11
|
167 {
|
yading@11
|
168 AVFilterContext *ctx = inlink->dst;
|
yading@11
|
169 AVFilterLink *outlink = ctx->outputs[0];
|
yading@11
|
170 TelecineContext *tc = ctx->priv;
|
yading@11
|
171 int i, len, ret = 0, nout = 0;
|
yading@11
|
172
|
yading@11
|
173 len = tc->pattern[tc->pattern_pos] - '0';
|
yading@11
|
174
|
yading@11
|
175 tc->pattern_pos++;
|
yading@11
|
176 if (!tc->pattern[tc->pattern_pos])
|
yading@11
|
177 tc->pattern_pos = 0;
|
yading@11
|
178
|
yading@11
|
179 if (!len) { // do not output any field from this frame
|
yading@11
|
180 av_frame_free(&inpicref);
|
yading@11
|
181 return 0;
|
yading@11
|
182 }
|
yading@11
|
183
|
yading@11
|
184 if (tc->occupied) {
|
yading@11
|
185 for (i = 0; i < tc->nb_planes; i++) {
|
yading@11
|
186 // fill in the EARLIER field from the buffered pic
|
yading@11
|
187 av_image_copy_plane(tc->frame[nout]->data[i] + tc->frame[nout]->linesize[i] * tc->first_field,
|
yading@11
|
188 tc->frame[nout]->linesize[i] * 2,
|
yading@11
|
189 tc->temp->data[i] + tc->temp->linesize[i] * tc->first_field,
|
yading@11
|
190 tc->temp->linesize[i] * 2,
|
yading@11
|
191 tc->stride[i],
|
yading@11
|
192 (tc->planeheight[i] - tc->first_field + 1) / 2);
|
yading@11
|
193 // fill in the LATER field from the new pic
|
yading@11
|
194 av_image_copy_plane(tc->frame[nout]->data[i] + tc->frame[nout]->linesize[i] * !tc->first_field,
|
yading@11
|
195 tc->frame[nout]->linesize[i] * 2,
|
yading@11
|
196 inpicref->data[i] + inpicref->linesize[i] * !tc->first_field,
|
yading@11
|
197 inpicref->linesize[i] * 2,
|
yading@11
|
198 tc->stride[i],
|
yading@11
|
199 (tc->planeheight[i] - !tc->first_field + 1) / 2);
|
yading@11
|
200 }
|
yading@11
|
201 nout++;
|
yading@11
|
202 len--;
|
yading@11
|
203 tc->occupied = 0;
|
yading@11
|
204 }
|
yading@11
|
205
|
yading@11
|
206 while (len >= 2) {
|
yading@11
|
207 // output THIS image as-is
|
yading@11
|
208 for (i = 0; i < tc->nb_planes; i++)
|
yading@11
|
209 av_image_copy_plane(tc->frame[nout]->data[i], tc->frame[nout]->linesize[i],
|
yading@11
|
210 inpicref->data[i], inpicref->linesize[i],
|
yading@11
|
211 tc->stride[i],
|
yading@11
|
212 tc->planeheight[i]);
|
yading@11
|
213 nout++;
|
yading@11
|
214 len -= 2;
|
yading@11
|
215 }
|
yading@11
|
216
|
yading@11
|
217 if (len >= 1) {
|
yading@11
|
218 // copy THIS image to the buffer, we need it later
|
yading@11
|
219 for (i = 0; i < tc->nb_planes; i++)
|
yading@11
|
220 av_image_copy_plane(tc->temp->data[i], tc->temp->linesize[i],
|
yading@11
|
221 inpicref->data[i], inpicref->linesize[i],
|
yading@11
|
222 tc->stride[i],
|
yading@11
|
223 tc->planeheight[i]);
|
yading@11
|
224 tc->occupied = 1;
|
yading@11
|
225 }
|
yading@11
|
226
|
yading@11
|
227 for (i = 0; i < nout; i++) {
|
yading@11
|
228 AVFrame *frame = av_frame_clone(tc->frame[i]);
|
yading@11
|
229
|
yading@11
|
230 if (!frame) {
|
yading@11
|
231 av_frame_free(&inpicref);
|
yading@11
|
232 return AVERROR(ENOMEM);
|
yading@11
|
233 }
|
yading@11
|
234
|
yading@11
|
235 av_frame_copy_props(frame, inpicref);
|
yading@11
|
236 frame->pts = tc->frame_count++ * tc->ts_unit;
|
yading@11
|
237 ret = ff_filter_frame(outlink, frame);
|
yading@11
|
238 }
|
yading@11
|
239 av_frame_free(&inpicref);
|
yading@11
|
240
|
yading@11
|
241 return ret;
|
yading@11
|
242 }
|
yading@11
|
243
|
yading@11
|
244 static av_cold void uninit(AVFilterContext *ctx)
|
yading@11
|
245 {
|
yading@11
|
246 TelecineContext *tc = ctx->priv;
|
yading@11
|
247 int i;
|
yading@11
|
248
|
yading@11
|
249 av_frame_free(&tc->temp);
|
yading@11
|
250 for (i = 0; i < tc->out_cnt; i++)
|
yading@11
|
251 av_frame_free(&tc->frame[i]);
|
yading@11
|
252 }
|
yading@11
|
253
|
yading@11
|
254 static const AVFilterPad telecine_inputs[] = {
|
yading@11
|
255 {
|
yading@11
|
256 .name = "default",
|
yading@11
|
257 .type = AVMEDIA_TYPE_VIDEO,
|
yading@11
|
258 .filter_frame = filter_frame,
|
yading@11
|
259 .config_props = config_input,
|
yading@11
|
260 },
|
yading@11
|
261 { NULL }
|
yading@11
|
262 };
|
yading@11
|
263
|
yading@11
|
264 static const AVFilterPad telecine_outputs[] = {
|
yading@11
|
265 {
|
yading@11
|
266 .name = "default",
|
yading@11
|
267 .type = AVMEDIA_TYPE_VIDEO,
|
yading@11
|
268 .config_props = config_output,
|
yading@11
|
269 },
|
yading@11
|
270 { NULL }
|
yading@11
|
271 };
|
yading@11
|
272
|
yading@11
|
273 AVFilter avfilter_vf_telecine = {
|
yading@11
|
274 .name = "telecine",
|
yading@11
|
275 .description = NULL_IF_CONFIG_SMALL("Apply a telecine pattern."),
|
yading@11
|
276 .priv_size = sizeof(TelecineContext),
|
yading@11
|
277 .priv_class = &telecine_class,
|
yading@11
|
278 .init = init,
|
yading@11
|
279 .uninit = uninit,
|
yading@11
|
280 .query_formats = query_formats,
|
yading@11
|
281 .inputs = telecine_inputs,
|
yading@11
|
282 .outputs = telecine_outputs,
|
yading@11
|
283 };
|