Line |
Branch |
Exec |
Source |
1 |
|
|
/* |
2 |
|
|
* Copyright (c) 2003 Tobias Diedrich |
3 |
|
|
* |
4 |
|
|
* This file is part of FFmpeg. |
5 |
|
|
* |
6 |
|
|
* FFmpeg is free software; you can redistribute it and/or modify |
7 |
|
|
* it under the terms of the GNU General Public License as published by |
8 |
|
|
* the Free Software Foundation; either version 2 of the License, or |
9 |
|
|
* (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 |
14 |
|
|
* GNU General Public License for more details. |
15 |
|
|
* |
16 |
|
|
* You should have received a copy of the GNU General Public License along |
17 |
|
|
* with FFmpeg; if not, write to the Free Software Foundation, Inc., |
18 |
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 |
|
|
*/ |
20 |
|
|
|
21 |
|
|
#include "libavutil/imgutils.h" |
22 |
|
|
#include "avfilter.h" |
23 |
|
|
#include "filters.h" |
24 |
|
|
#include "video.h" |
25 |
|
|
|
26 |
|
|
typedef struct RepeatFieldsContext { |
27 |
|
|
const AVClass *class; |
28 |
|
|
int state; |
29 |
|
|
int nb_planes; |
30 |
|
|
int linesize[4]; |
31 |
|
|
int planeheight[4]; |
32 |
|
|
AVFrame *frame; |
33 |
|
|
} RepeatFieldsContext; |
34 |
|
|
|
35 |
|
✗ |
static av_cold void uninit(AVFilterContext *ctx) |
36 |
|
|
{ |
37 |
|
✗ |
RepeatFieldsContext *s = ctx->priv; |
38 |
|
|
|
39 |
|
✗ |
av_frame_free(&s->frame); |
40 |
|
✗ |
} |
41 |
|
|
|
42 |
|
|
static const enum AVPixelFormat pixel_fmts_eq[] = { |
43 |
|
|
AV_PIX_FMT_GRAY8, |
44 |
|
|
AV_PIX_FMT_YUV410P, |
45 |
|
|
AV_PIX_FMT_YUV411P, |
46 |
|
|
AV_PIX_FMT_YUV420P, |
47 |
|
|
AV_PIX_FMT_YUV422P, |
48 |
|
|
AV_PIX_FMT_YUV444P, |
49 |
|
|
AV_PIX_FMT_NONE |
50 |
|
|
}; |
51 |
|
|
|
52 |
|
✗ |
static int config_input(AVFilterLink *inlink) |
53 |
|
|
{ |
54 |
|
✗ |
RepeatFieldsContext *s = inlink->dst->priv; |
55 |
|
✗ |
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); |
56 |
|
|
int ret; |
57 |
|
|
|
58 |
|
✗ |
if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0) |
59 |
|
✗ |
return ret; |
60 |
|
|
|
61 |
|
✗ |
s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); |
62 |
|
✗ |
s->planeheight[0] = s->planeheight[3] = inlink->h; |
63 |
|
|
|
64 |
|
✗ |
s->nb_planes = av_pix_fmt_count_planes(inlink->format); |
65 |
|
|
|
66 |
|
✗ |
return 0; |
67 |
|
|
} |
68 |
|
|
|
69 |
|
✗ |
static void update_pts(AVFilterLink *link, AVFrame *f, int64_t pts, int fields) |
70 |
|
|
{ |
71 |
|
✗ |
FilterLink *l = ff_filter_link(link); |
72 |
|
|
|
73 |
|
✗ |
if (av_cmp_q(l->frame_rate, (AVRational){30000, 1001}) == 0 && |
74 |
|
✗ |
av_cmp_q(link->time_base, (AVRational){1001, 60000}) <= 0 |
75 |
|
✗ |
) { |
76 |
|
✗ |
f->pts = pts + av_rescale_q(fields, (AVRational){1001, 60000}, link->time_base); |
77 |
|
|
} else |
78 |
|
✗ |
f->pts = AV_NOPTS_VALUE; |
79 |
|
✗ |
} |
80 |
|
|
|
81 |
|
✗ |
static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
82 |
|
|
{ |
83 |
|
✗ |
AVFilterContext *ctx = inlink->dst; |
84 |
|
✗ |
AVFilterLink *outlink = inlink->dst->outputs[0]; |
85 |
|
✗ |
RepeatFieldsContext *s = ctx->priv; |
86 |
|
|
int ret, i; |
87 |
|
✗ |
int state = s->state; |
88 |
|
|
|
89 |
|
✗ |
if (!s->frame) { |
90 |
|
✗ |
s->frame = av_frame_clone(in); |
91 |
|
✗ |
if (!s->frame) { |
92 |
|
✗ |
av_frame_free(&in); |
93 |
|
✗ |
return AVERROR(ENOMEM); |
94 |
|
|
} |
95 |
|
✗ |
s->frame->pts = AV_NOPTS_VALUE; |
96 |
|
|
} |
97 |
|
|
|
98 |
|
✗ |
if ((state == 0 && !(in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)) || |
99 |
|
✗ |
(state == 1 && (in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST))) { |
100 |
|
✗ |
av_log(ctx, AV_LOG_WARNING, "Unexpected field flags: " |
101 |
|
|
"state=%d top_field_first=%d repeat_first_field=%d\n", |
102 |
|
✗ |
state, !!(in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST), |
103 |
|
✗ |
in->repeat_pict); |
104 |
|
✗ |
state ^= 1; |
105 |
|
|
} |
106 |
|
|
|
107 |
|
✗ |
if (state == 0) { |
108 |
|
|
AVFrame *new; |
109 |
|
|
|
110 |
|
✗ |
new = av_frame_clone(in); |
111 |
|
✗ |
if (!new) { |
112 |
|
✗ |
av_frame_free(&in); |
113 |
|
✗ |
return AVERROR(ENOMEM); |
114 |
|
|
} |
115 |
|
|
|
116 |
|
✗ |
ret = ff_filter_frame(outlink, new); |
117 |
|
|
|
118 |
|
✗ |
if (in->repeat_pict) { |
119 |
|
✗ |
ret = ff_inlink_make_frame_writable(inlink, &s->frame); |
120 |
|
✗ |
if (ret < 0) { |
121 |
|
✗ |
av_frame_free(&in); |
122 |
|
✗ |
return ret; |
123 |
|
|
} |
124 |
|
✗ |
update_pts(outlink, s->frame, in->pts, 2); |
125 |
|
✗ |
for (i = 0; i < s->nb_planes; i++) { |
126 |
|
✗ |
av_image_copy_plane(s->frame->data[i], s->frame->linesize[i] * 2, |
127 |
|
✗ |
in->data[i], in->linesize[i] * 2, |
128 |
|
✗ |
s->linesize[i], s->planeheight[i] / 2); |
129 |
|
|
} |
130 |
|
✗ |
state = 1; |
131 |
|
|
} |
132 |
|
|
} else { |
133 |
|
✗ |
for (i = 0; i < s->nb_planes; i++) { |
134 |
|
✗ |
ret = ff_inlink_make_frame_writable(inlink, &s->frame); |
135 |
|
✗ |
if (ret < 0) { |
136 |
|
✗ |
av_frame_free(&in); |
137 |
|
✗ |
return ret; |
138 |
|
|
} |
139 |
|
✗ |
av_image_copy_plane(s->frame->data[i] + s->frame->linesize[i], s->frame->linesize[i] * 2, |
140 |
|
✗ |
in->data[i] + in->linesize[i], in->linesize[i] * 2, |
141 |
|
✗ |
s->linesize[i], s->planeheight[i] / 2); |
142 |
|
|
} |
143 |
|
|
|
144 |
|
✗ |
ret = ff_filter_frame(outlink, av_frame_clone(s->frame)); |
145 |
|
|
|
146 |
|
✗ |
if (in->repeat_pict) { |
147 |
|
|
AVFrame *new; |
148 |
|
|
|
149 |
|
✗ |
new = av_frame_clone(in); |
150 |
|
✗ |
if (!new) { |
151 |
|
✗ |
av_frame_free(&in); |
152 |
|
✗ |
return AVERROR(ENOMEM); |
153 |
|
|
} |
154 |
|
|
|
155 |
|
✗ |
ret = ff_filter_frame(outlink, new); |
156 |
|
✗ |
state = 0; |
157 |
|
|
} else { |
158 |
|
✗ |
ret = ff_inlink_make_frame_writable(inlink, &s->frame); |
159 |
|
✗ |
if (ret < 0) { |
160 |
|
✗ |
av_frame_free(&in); |
161 |
|
✗ |
return ret; |
162 |
|
|
} |
163 |
|
✗ |
update_pts(outlink, s->frame, in->pts, 1); |
164 |
|
✗ |
for (i = 0; i < s->nb_planes; i++) { |
165 |
|
✗ |
av_image_copy_plane(s->frame->data[i], s->frame->linesize[i] * 2, |
166 |
|
✗ |
in->data[i], in->linesize[i] * 2, |
167 |
|
✗ |
s->linesize[i], s->planeheight[i] / 2); |
168 |
|
|
} |
169 |
|
|
} |
170 |
|
|
} |
171 |
|
|
|
172 |
|
✗ |
s->state = state; |
173 |
|
|
|
174 |
|
✗ |
av_frame_free(&in); |
175 |
|
✗ |
return ret; |
176 |
|
|
} |
177 |
|
|
|
178 |
|
|
static const AVFilterPad repeatfields_inputs[] = { |
179 |
|
|
{ |
180 |
|
|
.name = "default", |
181 |
|
|
.type = AVMEDIA_TYPE_VIDEO, |
182 |
|
|
.filter_frame = filter_frame, |
183 |
|
|
.config_props = config_input, |
184 |
|
|
}, |
185 |
|
|
}; |
186 |
|
|
|
187 |
|
|
const AVFilter ff_vf_repeatfields = { |
188 |
|
|
.name = "repeatfields", |
189 |
|
|
.description = NULL_IF_CONFIG_SMALL("Hard repeat fields based on MPEG repeat field flag."), |
190 |
|
|
.priv_size = sizeof(RepeatFieldsContext), |
191 |
|
|
.uninit = uninit, |
192 |
|
|
FILTER_INPUTS(repeatfields_inputs), |
193 |
|
|
FILTER_OUTPUTS(ff_video_default_filterpad), |
194 |
|
|
FILTER_PIXFMTS_ARRAY(pixel_fmts_eq), |
195 |
|
|
}; |
196 |
|
|
|