Signed-off-by: Paul B Mahol <onemda@gmail.com>
Paul B Mahol authored on 2013/04/06 22:45:52... | ... |
@@ -458,6 +458,45 @@ slope |
458 | 458 |
Determine how steep is the filter's shelf transition. |
459 | 459 |
@end table |
460 | 460 |
|
461 |
+@section telecine |
|
462 |
+ |
|
463 |
+Apply telecine process to the video. |
|
464 |
+ |
|
465 |
+This filter accepts the following options: |
|
466 |
+ |
|
467 |
+@table @option |
|
468 |
+@item first_field |
|
469 |
+@table @samp |
|
470 |
+@item top, t |
|
471 |
+top field first |
|
472 |
+@item bottom, b |
|
473 |
+bottom field first |
|
474 |
+The default value is @code{top}. |
|
475 |
+@end table |
|
476 |
+ |
|
477 |
+@item pattern |
|
478 |
+A string of numbers representing the pulldown pattern you wish to apply. |
|
479 |
+The default value is @code{23}. |
|
480 |
+@end table |
|
481 |
+ |
|
482 |
+@example |
|
483 |
+Some typical patterns: |
|
484 |
+ |
|
485 |
+NTSC output (30i): |
|
486 |
+27.5p: 32222 |
|
487 |
+24p: 23 (classic) |
|
488 |
+24p: 2332 (preferred) |
|
489 |
+20p: 33 |
|
490 |
+18p: 334 |
|
491 |
+16p: 3444 |
|
492 |
+ |
|
493 |
+PAL output (25i): |
|
494 |
+27.5p: 12222 |
|
495 |
+24p: 222222222223 ("Euro pulldown") |
|
496 |
+16.67p: 33 |
|
497 |
+16p: 33333334 |
|
498 |
+@end example |
|
499 |
+ |
|
461 | 500 |
@section treble |
462 | 501 |
|
463 | 502 |
Boost or cut treble (upper) frequencies of the audio using a two-pole |
... | ... |
@@ -164,6 +164,7 @@ OBJS-$(CONFIG_STEREO3D_FILTER) += vf_stereo3d.o |
164 | 164 |
OBJS-$(CONFIG_SUBTITLES_FILTER) += vf_subtitles.o |
165 | 165 |
OBJS-$(CONFIG_SUPER2XSAI_FILTER) += vf_super2xsai.o |
166 | 166 |
OBJS-$(CONFIG_SWAPUV_FILTER) += vf_swapuv.o |
167 |
+OBJS-$(CONFIG_TELECINE_FILTER) += vf_telecine.o |
|
167 | 168 |
OBJS-$(CONFIG_THUMBNAIL_FILTER) += vf_thumbnail.o |
168 | 169 |
OBJS-$(CONFIG_TILE_FILTER) += vf_tile.o |
169 | 170 |
OBJS-$(CONFIG_TINTERLACE_FILTER) += vf_tinterlace.o |
... | ... |
@@ -159,6 +159,7 @@ void avfilter_register_all(void) |
159 | 159 |
REGISTER_FILTER(SUBTITLES, subtitles, vf); |
160 | 160 |
REGISTER_FILTER(SUPER2XSAI, super2xsai, vf); |
161 | 161 |
REGISTER_FILTER(SWAPUV, swapuv, vf); |
162 |
+ REGISTER_FILTER(TELECINE, telecine, vf); |
|
162 | 163 |
REGISTER_FILTER(THUMBNAIL, thumbnail, vf); |
163 | 164 |
REGISTER_FILTER(TILE, tile, vf); |
164 | 165 |
REGISTER_FILTER(TINTERLACE, tinterlace, vf); |
... | ... |
@@ -759,6 +759,7 @@ int avfilter_init_filter(AVFilterContext *filter, const char *args, void *opaque |
759 | 759 |
!strcmp(filter->filter->name, "split" ) || |
760 | 760 |
!strcmp(filter->filter->name, "stereo3d" ) || |
761 | 761 |
!strcmp(filter->filter->name, "subtitles") || |
762 |
+ !strcmp(filter->filter->name, "telecine" ) || |
|
762 | 763 |
!strcmp(filter->filter->name, "testsrc" ) || |
763 | 764 |
!strcmp(filter->filter->name, "thumbnail") || |
764 | 765 |
!strcmp(filter->filter->name, "tile") || |
... | ... |
@@ -29,7 +29,7 @@ |
29 | 29 |
#include "libavutil/avutil.h" |
30 | 30 |
|
31 | 31 |
#define LIBAVFILTER_VERSION_MAJOR 3 |
32 |
-#define LIBAVFILTER_VERSION_MINOR 51 |
|
32 |
+#define LIBAVFILTER_VERSION_MINOR 52 |
|
33 | 33 |
#define LIBAVFILTER_VERSION_MICRO 100 |
34 | 34 |
|
35 | 35 |
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ |
36 | 36 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,283 @@ |
0 |
+/* |
|
1 |
+ * Copyright (c) 2012 Rudolf Polzer |
|
2 |
+ * Copyright (c) 2013 Paul B Mahol |
|
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 telecine filter, heavily based from mpv-player:TOOLS/vf_dlopen/telecine.c by |
|
23 |
+ * Rudolf Polzer. |
|
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 |
+ unsigned int pattern_pos; |
|
40 |
+ |
|
41 |
+ AVRational pts; |
|
42 |
+ double ts_unit; |
|
43 |
+ int out_cnt; |
|
44 |
+ int occupied; |
|
45 |
+ int64_t frame_count; |
|
46 |
+ |
|
47 |
+ int nb_planes; |
|
48 |
+ int planeheight[4]; |
|
49 |
+ int stride[4]; |
|
50 |
+ |
|
51 |
+ AVFrame *frame[5]; |
|
52 |
+ AVFrame *temp; |
|
53 |
+} TelecineContext; |
|
54 |
+ |
|
55 |
+#define OFFSET(x) offsetof(TelecineContext, x) |
|
56 |
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM |
|
57 |
+ |
|
58 |
+static const AVOption telecine_options[] = { |
|
59 |
+ {"first_field", "select first field", OFFSET(first_field), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "field"}, |
|
60 |
+ {"top", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"}, |
|
61 |
+ {"t", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"}, |
|
62 |
+ {"bottom", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"}, |
|
63 |
+ {"b", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"}, |
|
64 |
+ {"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}, |
|
65 |
+ {NULL} |
|
66 |
+}; |
|
67 |
+ |
|
68 |
+AVFILTER_DEFINE_CLASS(telecine); |
|
69 |
+ |
|
70 |
+static av_cold int init(AVFilterContext *ctx, const char *args) |
|
71 |
+{ |
|
72 |
+ TelecineContext *tc = ctx->priv; |
|
73 |
+ const char *p; |
|
74 |
+ int max = 0; |
|
75 |
+ |
|
76 |
+ if (!strlen(tc->pattern)) { |
|
77 |
+ av_log(ctx, AV_LOG_ERROR, "No pattern provided.\n"); |
|
78 |
+ return AVERROR_INVALIDDATA; |
|
79 |
+ } |
|
80 |
+ |
|
81 |
+ for (p = tc->pattern; *p; p++) { |
|
82 |
+ if (!av_isdigit(*p)) { |
|
83 |
+ av_log(ctx, AV_LOG_ERROR, "Provided pattern includes non-numeric characters.\n"); |
|
84 |
+ return AVERROR_INVALIDDATA; |
|
85 |
+ } |
|
86 |
+ |
|
87 |
+ max = FFMAX(*p - '0', max); |
|
88 |
+ tc->pts.num += 2; |
|
89 |
+ tc->pts.den += *p - '0'; |
|
90 |
+ } |
|
91 |
+ |
|
92 |
+ tc->out_cnt = (max + 1) / 2; |
|
93 |
+ av_log(ctx, AV_LOG_INFO, "Telecine pattern %s yields up to %d frames per frame, pts advance factor: %d/%d\n", |
|
94 |
+ tc->pattern, tc->out_cnt, tc->pts.num, tc->pts.den); |
|
95 |
+ |
|
96 |
+ return 0; |
|
97 |
+} |
|
98 |
+ |
|
99 |
+static int query_formats(AVFilterContext *ctx) |
|
100 |
+{ |
|
101 |
+ AVFilterFormats *pix_fmts = NULL; |
|
102 |
+ int fmt; |
|
103 |
+ |
|
104 |
+ for (fmt = 0; fmt < AV_PIX_FMT_NB; fmt++) { |
|
105 |
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); |
|
106 |
+ if (!(desc->flags & PIX_FMT_HWACCEL)) |
|
107 |
+ ff_add_format(&pix_fmts, fmt); |
|
108 |
+ } |
|
109 |
+ |
|
110 |
+ ff_set_common_formats(ctx, pix_fmts); |
|
111 |
+ return 0; |
|
112 |
+} |
|
113 |
+ |
|
114 |
+static int config_input(AVFilterLink *inlink) |
|
115 |
+{ |
|
116 |
+ TelecineContext *tc = inlink->dst->priv; |
|
117 |
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); |
|
118 |
+ int i, ret; |
|
119 |
+ |
|
120 |
+ tc->temp = ff_get_video_buffer(inlink, inlink->w, inlink->h); |
|
121 |
+ if (!tc->temp) |
|
122 |
+ return AVERROR(ENOMEM); |
|
123 |
+ for (i = 0; i < tc->out_cnt; i++) { |
|
124 |
+ tc->frame[i] = ff_get_video_buffer(inlink, inlink->w, inlink->h); |
|
125 |
+ if (!tc->frame[i]) |
|
126 |
+ return AVERROR(ENOMEM); |
|
127 |
+ } |
|
128 |
+ |
|
129 |
+ if ((ret = av_image_fill_linesizes(tc->stride, inlink->format, inlink->w)) < 0) |
|
130 |
+ return ret; |
|
131 |
+ |
|
132 |
+ tc->planeheight[1] = tc->planeheight[2] = inlink->h >> desc->log2_chroma_h; |
|
133 |
+ tc->planeheight[0] = tc->planeheight[3] = inlink->h; |
|
134 |
+ |
|
135 |
+ tc->nb_planes = av_pix_fmt_count_planes(inlink->format); |
|
136 |
+ |
|
137 |
+ return 0; |
|
138 |
+} |
|
139 |
+ |
|
140 |
+static int config_output(AVFilterLink *outlink) |
|
141 |
+{ |
|
142 |
+ AVFilterContext *ctx = outlink->src; |
|
143 |
+ TelecineContext *tc = ctx->priv; |
|
144 |
+ const AVFilterLink *inlink = ctx->inputs[0]; |
|
145 |
+ AVRational fps = inlink->frame_rate; |
|
146 |
+ |
|
147 |
+ if (!fps.num || !fps.den) { |
|
148 |
+ av_log(ctx, AV_LOG_ERROR, "The input needs a constant frame rate; " |
|
149 |
+ "current rate of %d/%d is invalid\n", fps.num, fps.den); |
|
150 |
+ return AVERROR(EINVAL); |
|
151 |
+ } |
|
152 |
+ fps = av_mul_q(fps, av_inv_q(tc->pts)); |
|
153 |
+ av_log(ctx, AV_LOG_VERBOSE, "FPS: %d/%d -> %d/%d\n", |
|
154 |
+ inlink->frame_rate.num, inlink->frame_rate.den, fps.num, fps.den); |
|
155 |
+ |
|
156 |
+ outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP; |
|
157 |
+ outlink->frame_rate = fps; |
|
158 |
+ outlink->time_base = av_mul_q(inlink->time_base, tc->pts); |
|
159 |
+ |
|
160 |
+ tc->ts_unit = av_q2d(av_inv_q(av_mul_q(fps, outlink->time_base))); |
|
161 |
+ |
|
162 |
+ return 0; |
|
163 |
+} |
|
164 |
+ |
|
165 |
+static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) |
|
166 |
+{ |
|
167 |
+ AVFilterContext *ctx = inlink->dst; |
|
168 |
+ AVFilterLink *outlink = ctx->outputs[0]; |
|
169 |
+ TelecineContext *tc = ctx->priv; |
|
170 |
+ int i, len, ret = 0, nout = 0; |
|
171 |
+ |
|
172 |
+ len = tc->pattern[tc->pattern_pos] - '0'; |
|
173 |
+ |
|
174 |
+ tc->pattern_pos++; |
|
175 |
+ if (!tc->pattern[tc->pattern_pos]) |
|
176 |
+ tc->pattern_pos = 0; |
|
177 |
+ |
|
178 |
+ if (!len) { // do not output any field from this frame |
|
179 |
+ av_frame_free(&inpicref); |
|
180 |
+ return 0; |
|
181 |
+ } |
|
182 |
+ |
|
183 |
+ if (tc->occupied) { |
|
184 |
+ for (i = 0; i < tc->nb_planes; i++) { |
|
185 |
+ // fill in the EARLIER field from the buffered pic |
|
186 |
+ av_image_copy_plane(tc->frame[nout]->data[i] + tc->frame[nout]->linesize[i] * tc->first_field, |
|
187 |
+ tc->frame[nout]->linesize[i] * 2, |
|
188 |
+ tc->temp->data[i] + tc->temp->linesize[i] * tc->first_field, |
|
189 |
+ tc->temp->linesize[i] * 2, |
|
190 |
+ tc->stride[i], |
|
191 |
+ (tc->planeheight[i] - tc->first_field + 1) / 2); |
|
192 |
+ // fill in the LATER field from the new pic |
|
193 |
+ av_image_copy_plane(tc->frame[nout]->data[i] + tc->frame[nout]->linesize[i] * !tc->first_field, |
|
194 |
+ tc->frame[nout]->linesize[i] * 2, |
|
195 |
+ inpicref->data[i] + inpicref->linesize[i] * !tc->first_field, |
|
196 |
+ inpicref->linesize[i] * 2, |
|
197 |
+ tc->stride[i], |
|
198 |
+ (tc->planeheight[i] - !tc->first_field + 1) / 2); |
|
199 |
+ } |
|
200 |
+ nout++; |
|
201 |
+ len--; |
|
202 |
+ tc->occupied = 0; |
|
203 |
+ } |
|
204 |
+ |
|
205 |
+ while (len >= 2) { |
|
206 |
+ // output THIS image as-is |
|
207 |
+ for (i = 0; i < tc->nb_planes; i++) |
|
208 |
+ av_image_copy_plane(tc->frame[nout]->data[i], tc->frame[nout]->linesize[i], |
|
209 |
+ inpicref->data[i], inpicref->linesize[i], |
|
210 |
+ tc->stride[i], |
|
211 |
+ tc->planeheight[i]); |
|
212 |
+ nout++; |
|
213 |
+ len -= 2; |
|
214 |
+ } |
|
215 |
+ |
|
216 |
+ if (len >= 1) { |
|
217 |
+ // copy THIS image to the buffer, we need it later |
|
218 |
+ for (i = 0; i < tc->nb_planes; i++) |
|
219 |
+ av_image_copy_plane(tc->temp->data[i], tc->temp->linesize[i], |
|
220 |
+ inpicref->data[i], inpicref->linesize[i], |
|
221 |
+ tc->stride[i], |
|
222 |
+ tc->planeheight[i]); |
|
223 |
+ tc->occupied = 1; |
|
224 |
+ } |
|
225 |
+ |
|
226 |
+ for (i = 0; i < nout; i++) { |
|
227 |
+ AVFrame *frame = av_frame_clone(tc->frame[i]); |
|
228 |
+ |
|
229 |
+ if (!frame) { |
|
230 |
+ av_frame_free(&inpicref); |
|
231 |
+ return AVERROR(ENOMEM); |
|
232 |
+ } |
|
233 |
+ |
|
234 |
+ av_frame_copy_props(frame, inpicref); |
|
235 |
+ frame->pts = tc->frame_count++ * tc->ts_unit; |
|
236 |
+ ret = ff_filter_frame(outlink, frame); |
|
237 |
+ } |
|
238 |
+ av_frame_free(&inpicref); |
|
239 |
+ |
|
240 |
+ return ret; |
|
241 |
+} |
|
242 |
+ |
|
243 |
+static av_cold void uninit(AVFilterContext *ctx) |
|
244 |
+{ |
|
245 |
+ TelecineContext *tc = ctx->priv; |
|
246 |
+ int i; |
|
247 |
+ |
|
248 |
+ av_frame_free(&tc->temp); |
|
249 |
+ for (i = 0; i < tc->out_cnt; i++) |
|
250 |
+ av_frame_free(&tc->frame[i]); |
|
251 |
+} |
|
252 |
+ |
|
253 |
+static const AVFilterPad telecine_inputs[] = { |
|
254 |
+ { |
|
255 |
+ .name = "default", |
|
256 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
257 |
+ .filter_frame = filter_frame, |
|
258 |
+ .config_props = config_input, |
|
259 |
+ }, |
|
260 |
+ { NULL } |
|
261 |
+}; |
|
262 |
+ |
|
263 |
+static const AVFilterPad telecine_outputs[] = { |
|
264 |
+ { |
|
265 |
+ .name = "default", |
|
266 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
267 |
+ .config_props = config_output, |
|
268 |
+ }, |
|
269 |
+ { NULL } |
|
270 |
+}; |
|
271 |
+ |
|
272 |
+AVFilter avfilter_vf_telecine = { |
|
273 |
+ .name = "telecine", |
|
274 |
+ .description = NULL_IF_CONFIG_SMALL("Apply a telecine pattern."), |
|
275 |
+ .priv_size = sizeof(TelecineContext), |
|
276 |
+ .priv_class = &telecine_class, |
|
277 |
+ .init = init, |
|
278 |
+ .uninit = uninit, |
|
279 |
+ .query_formats = query_formats, |
|
280 |
+ .inputs = telecine_inputs, |
|
281 |
+ .outputs = telecine_outputs, |
|
282 |
+}; |