blob: 0d5f88df77665028821e4c6ec0920071ca7aaa05
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 { |
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 | return 0; |
210 | } else if (s->nskip_fields >= 1) { |
211 | for (i = 0; i < s->nb_planes; i++) { |
212 | av_image_copy_plane(s->temp->data[i], s->temp->linesize[i], |
213 | inpicref->data[i], inpicref->linesize[i], |
214 | s->stride[i], |
215 | s->planeheight[i]); |
216 | } |
217 | s->occupied = 1; |
218 | s->nskip_fields--; |
219 | return 0; |
220 | } |
221 | |
222 | if (s->nskip_fields == 0) { |
223 | len = s->init_len; |
224 | s->init_len = 0; |
225 | while(!len && s->pattern[s->pattern_pos]) { |
226 | len = s->pattern[s->pattern_pos] - '0'; |
227 | s->pattern_pos++; |
228 | } |
229 | |
230 | if (!s->pattern[s->pattern_pos]) |
231 | s->pattern_pos = 0; |
232 | |
233 | if(!len) { // do not output any field as the entire pattern is zero |
234 | av_frame_free(&inpicref); |
235 | return 0; |
236 | } |
237 | |
238 | if (len == 1 && s->occupied) { |
239 | s->occupied = 0; |
240 | // output THIS image as-is |
241 | for (i = 0; i < s->nb_planes; i++) |
242 | av_image_copy_plane(s->frame[out]->data[i], s->frame[out]->linesize[i], |
243 | s->temp->data[i], s->temp->linesize[i], |
244 | s->stride[i], |
245 | s->planeheight[i]); |
246 | len = 0; |
247 | while(!len && s->pattern[s->pattern_pos]) { |
248 | len = s->pattern[s->pattern_pos] - '0'; |
249 | s->pattern_pos++; |
250 | } |
251 | |
252 | if (!s->pattern[s->pattern_pos]) |
253 | s->pattern_pos = 0; |
254 | |
255 | s->occupied = 0; |
256 | ++out; |
257 | } |
258 | |
259 | if (s->occupied) { |
260 | for (i = 0; i < s->nb_planes; i++) { |
261 | // fill in the EARLIER field from the new pic |
262 | av_image_copy_plane(s->frame[out]->data[i] + s->frame[out]->linesize[i] * s->first_field, |
263 | s->frame[out]->linesize[i] * 2, |
264 | inpicref->data[i] + inpicref->linesize[i] * s->first_field, |
265 | inpicref->linesize[i] * 2, |
266 | s->stride[i], |
267 | (s->planeheight[i] - s->first_field + 1) / 2); |
268 | // fill in the LATER field from the buffered pic |
269 | av_image_copy_plane(s->frame[out]->data[i] + s->frame[out]->linesize[i] * !s->first_field, |
270 | s->frame[out]->linesize[i] * 2, |
271 | s->temp->data[i] + s->temp->linesize[i] * !s->first_field, |
272 | s->temp->linesize[i] * 2, |
273 | s->stride[i], |
274 | (s->planeheight[i] - !s->first_field + 1) / 2); |
275 | } |
276 | |
277 | s->occupied = 0; |
278 | if (len <= 2) { |
279 | for (i = 0; i < s->nb_planes; i++) { |
280 | av_image_copy_plane(s->temp->data[i], s->temp->linesize[i], |
281 | inpicref->data[i], inpicref->linesize[i], |
282 | s->stride[i], |
283 | s->planeheight[i]); |
284 | } |
285 | s->occupied = 1; |
286 | } |
287 | ++out; |
288 | len = (len >= 3) ? len - 3 : 0; |
289 | } else { |
290 | if (len >= 2) { |
291 | // output THIS image as-is |
292 | for (i = 0; i < s->nb_planes; i++) |
293 | av_image_copy_plane(s->frame[out]->data[i], s->frame[out]->linesize[i], |
294 | inpicref->data[i], inpicref->linesize[i], |
295 | s->stride[i], |
296 | s->planeheight[i]); |
297 | len -= 2; |
298 | ++out; |
299 | } else if (len == 1) { |
300 | // output THIS image as-is |
301 | for (i = 0; i < s->nb_planes; i++) |
302 | av_image_copy_plane(s->frame[out]->data[i], s->frame[out]->linesize[i], |
303 | inpicref->data[i], inpicref->linesize[i], |
304 | s->stride[i], |
305 | s->planeheight[i]); |
306 | |
307 | for (i = 0; i < s->nb_planes; i++) { |
308 | av_image_copy_plane(s->temp->data[i], s->temp->linesize[i], |
309 | inpicref->data[i], inpicref->linesize[i], |
310 | s->stride[i], |
311 | s->planeheight[i]); |
312 | } |
313 | s->occupied = 1; |
314 | |
315 | len--; |
316 | ++out; |
317 | } |
318 | } |
319 | |
320 | if (len == 1 && s->occupied) |
321 | { |
322 | len--; |
323 | s->occupied = 0; |
324 | } |
325 | } |
326 | s->nskip_fields = len; |
327 | |
328 | for (i = 0; i < out; ++i) { |
329 | AVFrame *frame = av_frame_clone(s->frame[i]); |
330 | |
331 | if (!frame) { |
332 | av_frame_free(&inpicref); |
333 | return AVERROR(ENOMEM); |
334 | } |
335 | |
336 | av_frame_copy_props(frame, inpicref); |
337 | frame->pts = ((s->start_time == AV_NOPTS_VALUE) ? 0 : s->start_time) + |
338 | av_rescale(outlink->frame_count_in, s->ts_unit.num, |
339 | s->ts_unit.den); |
340 | ret = ff_filter_frame(outlink, frame); |
341 | } |
342 | |
343 | av_frame_free(&inpicref); |
344 | |
345 | return ret; |
346 | } |
347 | |
348 | static av_cold void uninit(AVFilterContext *ctx) |
349 | { |
350 | DetelecineContext *s = ctx->priv; |
351 | |
352 | av_frame_free(&s->temp); |
353 | av_frame_free(&s->frame[0]); |
354 | av_frame_free(&s->frame[1]); |
355 | } |
356 | |
357 | static const AVFilterPad detelecine_inputs[] = { |
358 | { |
359 | .name = "default", |
360 | .type = AVMEDIA_TYPE_VIDEO, |
361 | .filter_frame = filter_frame, |
362 | .config_props = config_input, |
363 | }, |
364 | { NULL } |
365 | }; |
366 | |
367 | static const AVFilterPad detelecine_outputs[] = { |
368 | { |
369 | .name = "default", |
370 | .type = AVMEDIA_TYPE_VIDEO, |
371 | .config_props = config_output, |
372 | }, |
373 | { NULL } |
374 | }; |
375 | |
376 | AVFilter ff_vf_detelecine = { |
377 | .name = "detelecine", |
378 | .description = NULL_IF_CONFIG_SMALL("Apply an inverse telecine pattern."), |
379 | .priv_size = sizeof(DetelecineContext), |
380 | .priv_class = &detelecine_class, |
381 | .init = init, |
382 | .uninit = uninit, |
383 | .query_formats = query_formats, |
384 | .inputs = detelecine_inputs, |
385 | .outputs = detelecine_outputs, |
386 | }; |
387 |