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