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