• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 #include "libavutil/imgutils.h"
22 #include "libavutil/opt.h"
23 #include "libavutil/pixdesc.h"
24 #include "avfilter.h"
25 #include "internal.h"
26 
27 typedef struct WeaveContext {
28     const AVClass *class;
29     int first_field;
30     int double_weave;
31     int nb_planes;
32     int planeheight[4];
33     int outheight[4];
34     int linesize[4];
35 
36     AVFrame *prev;
37 } WeaveContext;
38 
39 #define OFFSET(x) offsetof(WeaveContext, x)
40 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
41 
42 static const AVOption weave_options[] = {
43     { "first_field", "set first field", OFFSET(first_field), AV_OPT_TYPE_INT,   {.i64=0}, 0, 1, FLAGS, "field"},
44         { "top",     "set top field first",               0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"},
45         { "t",       "set top field first",               0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"},
46         { "bottom",  "set bottom field first",            0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"},
47         { "b",       "set bottom field first",            0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"},
48     { NULL }
49 };
50 
51 AVFILTER_DEFINE_CLASS_EXT(weave, "(double)weave", weave_options);
52 
query_formats(AVFilterContext * ctx)53 static int query_formats(AVFilterContext *ctx)
54 {
55     int reject_flags = AV_PIX_FMT_FLAG_PAL | AV_PIX_FMT_FLAG_HWACCEL;
56 
57     return ff_set_common_formats(ctx, ff_formats_pixdesc_filter(0, reject_flags));
58 }
59 
config_props_output(AVFilterLink * outlink)60 static int config_props_output(AVFilterLink *outlink)
61 {
62     AVFilterContext *ctx = outlink->src;
63     WeaveContext *s = ctx->priv;
64     AVFilterLink *inlink = ctx->inputs[0];
65     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
66     int ret;
67 
68     if (!s->double_weave) {
69         outlink->time_base.num = inlink->time_base.num * 2;
70         outlink->time_base.den = inlink->time_base.den;
71         outlink->frame_rate.num = inlink->frame_rate.num;
72         outlink->frame_rate.den = inlink->frame_rate.den * 2;
73     }
74     outlink->w = inlink->w;
75     outlink->h = inlink->h * 2;
76 
77     if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0)
78         return ret;
79 
80     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
81     s->planeheight[0] = s->planeheight[3] = inlink->h;
82 
83     s->outheight[1] = s->outheight[2] = AV_CEIL_RSHIFT(2*inlink->h, desc->log2_chroma_h);
84     s->outheight[0] = s->outheight[3] = 2*inlink->h;
85 
86     s->nb_planes = av_pix_fmt_count_planes(inlink->format);
87 
88     return 0;
89 }
90 
91 typedef struct ThreadData {
92     AVFrame *in, *out;
93 } ThreadData;
94 
weave_slice(AVFilterContext * ctx,void * arg,int jobnr,int nb_jobs)95 static int weave_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
96 {
97     AVFilterLink *inlink = ctx->inputs[0];
98     WeaveContext *s = ctx->priv;
99     ThreadData *td = arg;
100     AVFrame *in = td->in;
101     AVFrame *out = td->out;
102 
103     const int weave = (s->double_weave && !(inlink->frame_count_out & 1));
104     const int field1 = weave ? s->first_field : (!s->first_field);
105     const int field2 = weave ? (!s->first_field) : s->first_field;
106 
107     for (int i = 0; i < s->nb_planes; i++) {
108         const int height = s->planeheight[i];
109         const int start = (height * jobnr) / nb_jobs;
110         const int end = (height * (jobnr+1)) / nb_jobs;
111         const int compensation = 2*end > s->outheight[i];
112 
113         av_image_copy_plane(out->data[i] + out->linesize[i] * field1 +
114                             out->linesize[i] * start * 2,
115                             out->linesize[i] * 2,
116                             in->data[i] + start * in->linesize[i],
117                             in->linesize[i],
118                             s->linesize[i], end - start - compensation * field1);
119         av_image_copy_plane(out->data[i] + out->linesize[i] * field2 +
120                             out->linesize[i] * start * 2,
121                             out->linesize[i] * 2,
122                             s->prev->data[i] + start * s->prev->linesize[i],
123                             s->prev->linesize[i],
124                             s->linesize[i], end - start - compensation * field2);
125     }
126 
127     return 0;
128 }
129 
filter_frame(AVFilterLink * inlink,AVFrame * in)130 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
131 {
132     AVFilterContext *ctx = inlink->dst;
133     WeaveContext *s = ctx->priv;
134     AVFilterLink *outlink = ctx->outputs[0];
135     ThreadData td;
136     AVFrame *out;
137 
138     if (!s->prev) {
139         s->prev = in;
140         return 0;
141     }
142 
143     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
144     if (!out) {
145         av_frame_free(&in);
146         av_frame_free(&s->prev);
147         return AVERROR(ENOMEM);
148     }
149     av_frame_copy_props(out, in);
150 
151     td.out = out, td.in = in;
152     ff_filter_execute(ctx, weave_slice, &td, NULL,
153                       FFMIN(s->planeheight[1], ff_filter_get_nb_threads(ctx)));
154 
155     out->pts = s->double_weave ? s->prev->pts : in->pts / 2;
156     out->interlaced_frame = 1;
157     out->top_field_first = !s->first_field;
158 
159     if (!s->double_weave)
160         av_frame_free(&in);
161     av_frame_free(&s->prev);
162     if (s->double_weave)
163         s->prev = in;
164     return ff_filter_frame(outlink, out);
165 }
166 
uninit(AVFilterContext * ctx)167 static av_cold void uninit(AVFilterContext *ctx)
168 {
169     WeaveContext *s = ctx->priv;
170 
171     av_frame_free(&s->prev);
172 }
173 
174 static const AVFilterPad weave_inputs[] = {
175     {
176         .name             = "default",
177         .type             = AVMEDIA_TYPE_VIDEO,
178         .filter_frame     = filter_frame,
179     },
180 };
181 
182 static const AVFilterPad weave_outputs[] = {
183     {
184         .name          = "default",
185         .type          = AVMEDIA_TYPE_VIDEO,
186         .config_props  = config_props_output,
187     },
188 };
189 
190 const AVFilter ff_vf_weave = {
191     .name          = "weave",
192     .description   = NULL_IF_CONFIG_SMALL("Weave input video fields into frames."),
193     .priv_size     = sizeof(WeaveContext),
194     .priv_class    = &weave_class,
195     .uninit        = uninit,
196     FILTER_INPUTS(weave_inputs),
197     FILTER_OUTPUTS(weave_outputs),
198     FILTER_QUERY_FUNC(query_formats),
199     .flags         = AVFILTER_FLAG_SLICE_THREADS,
200 };
201 
init(AVFilterContext * ctx)202 static av_cold int init(AVFilterContext *ctx)
203 {
204     WeaveContext *s = ctx->priv;
205 
206     if (!strcmp(ctx->filter->name, "doubleweave"))
207         s->double_weave = 1;
208 
209     return 0;
210 }
211 
212 const AVFilter ff_vf_doubleweave = {
213     .name          = "doubleweave",
214     .description   = NULL_IF_CONFIG_SMALL("Weave input video fields into double number of frames."),
215     .priv_class    = &weave_class,
216     .priv_size     = sizeof(WeaveContext),
217     .init          = init,
218     .uninit        = uninit,
219     FILTER_INPUTS(weave_inputs),
220     FILTER_OUTPUTS(weave_outputs),
221     FILTER_QUERY_FUNC(query_formats),
222     .flags         = AVFILTER_FLAG_SLICE_THREADS,
223 };
224