blob: cf20c193f7c10262cd863734c021c75060dc4484
1 | /* |
2 | * Copyright (c) 2007 Benoit Fouet |
3 | * Copyright (c) 2010 Stefano Sabatini |
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 |
24 | * horizontal flip filter |
25 | */ |
26 | |
27 | #include <string.h> |
28 | |
29 | #include "libavutil/opt.h" |
30 | #include "avfilter.h" |
31 | #include "formats.h" |
32 | #include "internal.h" |
33 | #include "video.h" |
34 | #include "libavutil/pixdesc.h" |
35 | #include "libavutil/internal.h" |
36 | #include "libavutil/intreadwrite.h" |
37 | #include "libavutil/imgutils.h" |
38 | |
39 | typedef struct FlipContext { |
40 | const AVClass *class; |
41 | int max_step[4]; ///< max pixel step for each plane, expressed as a number of bytes |
42 | int planewidth[4]; ///< width of each plane |
43 | int planeheight[4]; ///< height of each plane |
44 | } FlipContext; |
45 | |
46 | static const AVOption hflip_options[] = { |
47 | { NULL } |
48 | }; |
49 | |
50 | AVFILTER_DEFINE_CLASS(hflip); |
51 | |
52 | static int query_formats(AVFilterContext *ctx) |
53 | { |
54 | AVFilterFormats *pix_fmts = NULL; |
55 | int fmt, ret; |
56 | |
57 | for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) { |
58 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); |
59 | if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL || |
60 | desc->flags & AV_PIX_FMT_FLAG_BITSTREAM || |
61 | (desc->log2_chroma_w != desc->log2_chroma_h && |
62 | desc->comp[0].plane == desc->comp[1].plane)) && |
63 | (ret = ff_add_format(&pix_fmts, fmt)) < 0) |
64 | return ret; |
65 | } |
66 | |
67 | return ff_set_common_formats(ctx, pix_fmts); |
68 | } |
69 | |
70 | static int config_props(AVFilterLink *inlink) |
71 | { |
72 | FlipContext *s = inlink->dst->priv; |
73 | const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format); |
74 | const int hsub = pix_desc->log2_chroma_w; |
75 | const int vsub = pix_desc->log2_chroma_h; |
76 | |
77 | av_image_fill_max_pixsteps(s->max_step, NULL, pix_desc); |
78 | s->planewidth[0] = s->planewidth[3] = inlink->w; |
79 | s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, hsub); |
80 | s->planeheight[0] = s->planeheight[3] = inlink->h; |
81 | s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, vsub); |
82 | |
83 | return 0; |
84 | } |
85 | |
86 | typedef struct ThreadData { |
87 | AVFrame *in, *out; |
88 | } ThreadData; |
89 | |
90 | static int filter_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs) |
91 | { |
92 | FlipContext *s = ctx->priv; |
93 | ThreadData *td = arg; |
94 | AVFrame *in = td->in; |
95 | AVFrame *out = td->out; |
96 | uint8_t *inrow, *outrow; |
97 | int i, j, plane, step; |
98 | |
99 | for (plane = 0; plane < 4 && in->data[plane] && in->linesize[plane]; plane++) { |
100 | const int width = s->planewidth[plane]; |
101 | const int height = s->planeheight[plane]; |
102 | const int start = (height * job ) / nb_jobs; |
103 | const int end = (height * (job+1)) / nb_jobs; |
104 | |
105 | step = s->max_step[plane]; |
106 | |
107 | outrow = out->data[plane] + start * out->linesize[plane]; |
108 | inrow = in ->data[plane] + start * in->linesize[plane] + (width - 1) * step; |
109 | for (i = start; i < end; i++) { |
110 | switch (step) { |
111 | case 1: |
112 | for (j = 0; j < width; j++) |
113 | outrow[j] = inrow[-j]; |
114 | break; |
115 | |
116 | case 2: |
117 | { |
118 | uint16_t *outrow16 = (uint16_t *)outrow; |
119 | uint16_t * inrow16 = (uint16_t *) inrow; |
120 | for (j = 0; j < width; j++) |
121 | outrow16[j] = inrow16[-j]; |
122 | } |
123 | break; |
124 | |
125 | case 3: |
126 | { |
127 | uint8_t *in = inrow; |
128 | uint8_t *out = outrow; |
129 | for (j = 0; j < width; j++, out += 3, in -= 3) { |
130 | int32_t v = AV_RB24(in); |
131 | AV_WB24(out, v); |
132 | } |
133 | } |
134 | break; |
135 | |
136 | case 4: |
137 | { |
138 | uint32_t *outrow32 = (uint32_t *)outrow; |
139 | uint32_t * inrow32 = (uint32_t *) inrow; |
140 | for (j = 0; j < width; j++) |
141 | outrow32[j] = inrow32[-j]; |
142 | } |
143 | break; |
144 | |
145 | default: |
146 | for (j = 0; j < width; j++) |
147 | memcpy(outrow + j*step, inrow - j*step, step); |
148 | } |
149 | |
150 | inrow += in ->linesize[plane]; |
151 | outrow += out->linesize[plane]; |
152 | } |
153 | } |
154 | |
155 | return 0; |
156 | } |
157 | |
158 | static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
159 | { |
160 | AVFilterContext *ctx = inlink->dst; |
161 | AVFilterLink *outlink = ctx->outputs[0]; |
162 | ThreadData td; |
163 | AVFrame *out; |
164 | |
165 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
166 | if (!out) { |
167 | av_frame_free(&in); |
168 | return AVERROR(ENOMEM); |
169 | } |
170 | av_frame_copy_props(out, in); |
171 | |
172 | /* copy palette if required */ |
173 | if (av_pix_fmt_desc_get(inlink->format)->flags & AV_PIX_FMT_FLAG_PAL) |
174 | memcpy(out->data[1], in->data[1], AVPALETTE_SIZE); |
175 | |
176 | td.in = in, td.out = out; |
177 | ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); |
178 | |
179 | av_frame_free(&in); |
180 | return ff_filter_frame(outlink, out); |
181 | } |
182 | |
183 | static const AVFilterPad avfilter_vf_hflip_inputs[] = { |
184 | { |
185 | .name = "default", |
186 | .type = AVMEDIA_TYPE_VIDEO, |
187 | .filter_frame = filter_frame, |
188 | .config_props = config_props, |
189 | }, |
190 | { NULL } |
191 | }; |
192 | |
193 | static const AVFilterPad avfilter_vf_hflip_outputs[] = { |
194 | { |
195 | .name = "default", |
196 | .type = AVMEDIA_TYPE_VIDEO, |
197 | }, |
198 | { NULL } |
199 | }; |
200 | |
201 | AVFilter ff_vf_hflip = { |
202 | .name = "hflip", |
203 | .description = NULL_IF_CONFIG_SMALL("Horizontally flip the input video."), |
204 | .priv_size = sizeof(FlipContext), |
205 | .priv_class = &hflip_class, |
206 | .query_formats = query_formats, |
207 | .inputs = avfilter_vf_hflip_inputs, |
208 | .outputs = avfilter_vf_hflip_outputs, |
209 | .flags = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, |
210 | }; |
211 |