yading@11
|
1 /*
|
yading@11
|
2 * Copyright (c) 2010 Stefano Sabatini
|
yading@11
|
3 * Copyright (c) 2008 Vitor Sessak
|
yading@11
|
4 *
|
yading@11
|
5 * This file is part of FFmpeg.
|
yading@11
|
6 *
|
yading@11
|
7 * FFmpeg is free software; you can redistribute it and/or
|
yading@11
|
8 * modify it under the terms of the GNU Lesser General Public
|
yading@11
|
9 * License as published by the Free Software Foundation; either
|
yading@11
|
10 * version 2.1 of the License, or (at your option) any later version.
|
yading@11
|
11 *
|
yading@11
|
12 * FFmpeg is distributed in the hope that it will be useful,
|
yading@11
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
yading@11
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
yading@11
|
15 * Lesser General Public License for more details.
|
yading@11
|
16 *
|
yading@11
|
17 * You should have received a copy of the GNU Lesser General Public
|
yading@11
|
18 * License along with FFmpeg; if not, write to the Free Software
|
yading@11
|
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
yading@11
|
20 */
|
yading@11
|
21
|
yading@11
|
22 /**
|
yading@11
|
23 * @file
|
yading@11
|
24 * transposition filter
|
yading@11
|
25 * Based on MPlayer libmpcodecs/vf_rotate.c.
|
yading@11
|
26 */
|
yading@11
|
27
|
yading@11
|
28 #include <stdio.h>
|
yading@11
|
29
|
yading@11
|
30 #include "libavutil/intreadwrite.h"
|
yading@11
|
31 #include "libavutil/opt.h"
|
yading@11
|
32 #include "libavutil/pixdesc.h"
|
yading@11
|
33 #include "libavutil/imgutils.h"
|
yading@11
|
34 #include "libavutil/internal.h"
|
yading@11
|
35 #include "libavutil/opt.h"
|
yading@11
|
36 #include "avfilter.h"
|
yading@11
|
37 #include "formats.h"
|
yading@11
|
38 #include "internal.h"
|
yading@11
|
39 #include "video.h"
|
yading@11
|
40
|
yading@11
|
41 typedef enum {
|
yading@11
|
42 TRANSPOSE_PT_TYPE_NONE,
|
yading@11
|
43 TRANSPOSE_PT_TYPE_LANDSCAPE,
|
yading@11
|
44 TRANSPOSE_PT_TYPE_PORTRAIT,
|
yading@11
|
45 } PassthroughType;
|
yading@11
|
46
|
yading@11
|
47 enum TransposeDir {
|
yading@11
|
48 TRANSPOSE_CCLOCK_FLIP,
|
yading@11
|
49 TRANSPOSE_CLOCK,
|
yading@11
|
50 TRANSPOSE_CCLOCK,
|
yading@11
|
51 TRANSPOSE_CLOCK_FLIP,
|
yading@11
|
52 };
|
yading@11
|
53
|
yading@11
|
54 typedef struct {
|
yading@11
|
55 const AVClass *class;
|
yading@11
|
56 int hsub, vsub;
|
yading@11
|
57 int pixsteps[4];
|
yading@11
|
58
|
yading@11
|
59 PassthroughType passthrough; ///< landscape passthrough mode enabled
|
yading@11
|
60 enum TransposeDir dir;
|
yading@11
|
61 } TransContext;
|
yading@11
|
62
|
yading@11
|
63 static int query_formats(AVFilterContext *ctx)
|
yading@11
|
64 {
|
yading@11
|
65 AVFilterFormats *pix_fmts = NULL;
|
yading@11
|
66 int fmt;
|
yading@11
|
67
|
yading@11
|
68 for (fmt = 0; fmt < AV_PIX_FMT_NB; fmt++) {
|
yading@11
|
69 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
|
yading@11
|
70 if (!(desc->flags & PIX_FMT_PAL ||
|
yading@11
|
71 desc->flags & PIX_FMT_HWACCEL ||
|
yading@11
|
72 desc->flags & PIX_FMT_BITSTREAM ||
|
yading@11
|
73 desc->log2_chroma_w != desc->log2_chroma_h))
|
yading@11
|
74 ff_add_format(&pix_fmts, fmt);
|
yading@11
|
75 }
|
yading@11
|
76
|
yading@11
|
77
|
yading@11
|
78 ff_set_common_formats(ctx, pix_fmts);
|
yading@11
|
79 return 0;
|
yading@11
|
80 }
|
yading@11
|
81
|
yading@11
|
82 static int config_props_output(AVFilterLink *outlink)
|
yading@11
|
83 {
|
yading@11
|
84 AVFilterContext *ctx = outlink->src;
|
yading@11
|
85 TransContext *trans = ctx->priv;
|
yading@11
|
86 AVFilterLink *inlink = ctx->inputs[0];
|
yading@11
|
87 const AVPixFmtDescriptor *desc_out = av_pix_fmt_desc_get(outlink->format);
|
yading@11
|
88 const AVPixFmtDescriptor *desc_in = av_pix_fmt_desc_get(inlink->format);
|
yading@11
|
89
|
yading@11
|
90 if (trans->dir&4) {
|
yading@11
|
91 av_log(ctx, AV_LOG_WARNING,
|
yading@11
|
92 "dir values greater than 3 are deprecated, use the passthrough option instead\n");
|
yading@11
|
93 trans->dir &= 3;
|
yading@11
|
94 trans->passthrough = TRANSPOSE_PT_TYPE_LANDSCAPE;
|
yading@11
|
95 }
|
yading@11
|
96
|
yading@11
|
97 if ((inlink->w >= inlink->h && trans->passthrough == TRANSPOSE_PT_TYPE_LANDSCAPE) ||
|
yading@11
|
98 (inlink->w <= inlink->h && trans->passthrough == TRANSPOSE_PT_TYPE_PORTRAIT)) {
|
yading@11
|
99 av_log(ctx, AV_LOG_VERBOSE,
|
yading@11
|
100 "w:%d h:%d -> w:%d h:%d (passthrough mode)\n",
|
yading@11
|
101 inlink->w, inlink->h, inlink->w, inlink->h);
|
yading@11
|
102 return 0;
|
yading@11
|
103 } else {
|
yading@11
|
104 trans->passthrough = TRANSPOSE_PT_TYPE_NONE;
|
yading@11
|
105 }
|
yading@11
|
106
|
yading@11
|
107 trans->hsub = desc_in->log2_chroma_w;
|
yading@11
|
108 trans->vsub = desc_in->log2_chroma_h;
|
yading@11
|
109
|
yading@11
|
110 av_image_fill_max_pixsteps(trans->pixsteps, NULL, desc_out);
|
yading@11
|
111
|
yading@11
|
112 outlink->w = inlink->h;
|
yading@11
|
113 outlink->h = inlink->w;
|
yading@11
|
114
|
yading@11
|
115 if (inlink->sample_aspect_ratio.num){
|
yading@11
|
116 outlink->sample_aspect_ratio = av_div_q((AVRational){1,1}, inlink->sample_aspect_ratio);
|
yading@11
|
117 } else
|
yading@11
|
118 outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
|
yading@11
|
119
|
yading@11
|
120 av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d dir:%d -> w:%d h:%d rotation:%s vflip:%d\n",
|
yading@11
|
121 inlink->w, inlink->h, trans->dir, outlink->w, outlink->h,
|
yading@11
|
122 trans->dir == 1 || trans->dir == 3 ? "clockwise" : "counterclockwise",
|
yading@11
|
123 trans->dir == 0 || trans->dir == 3);
|
yading@11
|
124 return 0;
|
yading@11
|
125 }
|
yading@11
|
126
|
yading@11
|
127 static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h)
|
yading@11
|
128 {
|
yading@11
|
129 TransContext *trans = inlink->dst->priv;
|
yading@11
|
130
|
yading@11
|
131 return trans->passthrough ?
|
yading@11
|
132 ff_null_get_video_buffer (inlink, w, h) :
|
yading@11
|
133 ff_default_get_video_buffer(inlink, w, h);
|
yading@11
|
134 }
|
yading@11
|
135
|
yading@11
|
136 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
|
yading@11
|
137 {
|
yading@11
|
138 TransContext *trans = inlink->dst->priv;
|
yading@11
|
139 AVFilterLink *outlink = inlink->dst->outputs[0];
|
yading@11
|
140 AVFrame *out;
|
yading@11
|
141 int plane;
|
yading@11
|
142
|
yading@11
|
143 if (trans->passthrough)
|
yading@11
|
144 return ff_filter_frame(outlink, in);
|
yading@11
|
145
|
yading@11
|
146 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
|
yading@11
|
147 if (!out) {
|
yading@11
|
148 av_frame_free(&in);
|
yading@11
|
149 return AVERROR(ENOMEM);
|
yading@11
|
150 }
|
yading@11
|
151
|
yading@11
|
152 out->pts = in->pts;
|
yading@11
|
153
|
yading@11
|
154 if (in->sample_aspect_ratio.num == 0) {
|
yading@11
|
155 out->sample_aspect_ratio = in->sample_aspect_ratio;
|
yading@11
|
156 } else {
|
yading@11
|
157 out->sample_aspect_ratio.num = in->sample_aspect_ratio.den;
|
yading@11
|
158 out->sample_aspect_ratio.den = in->sample_aspect_ratio.num;
|
yading@11
|
159 }
|
yading@11
|
160
|
yading@11
|
161 for (plane = 0; out->data[plane]; plane++) {
|
yading@11
|
162 int hsub = plane == 1 || plane == 2 ? trans->hsub : 0;
|
yading@11
|
163 int vsub = plane == 1 || plane == 2 ? trans->vsub : 0;
|
yading@11
|
164 int pixstep = trans->pixsteps[plane];
|
yading@11
|
165 int inh = in->height >> vsub;
|
yading@11
|
166 int outw = out->width >> hsub;
|
yading@11
|
167 int outh = out->height >> vsub;
|
yading@11
|
168 uint8_t *dst, *src;
|
yading@11
|
169 int dstlinesize, srclinesize;
|
yading@11
|
170 int x, y;
|
yading@11
|
171
|
yading@11
|
172 dst = out->data[plane];
|
yading@11
|
173 dstlinesize = out->linesize[plane];
|
yading@11
|
174 src = in->data[plane];
|
yading@11
|
175 srclinesize = in->linesize[plane];
|
yading@11
|
176
|
yading@11
|
177 if (trans->dir&1) {
|
yading@11
|
178 src += in->linesize[plane] * (inh-1);
|
yading@11
|
179 srclinesize *= -1;
|
yading@11
|
180 }
|
yading@11
|
181
|
yading@11
|
182 if (trans->dir&2) {
|
yading@11
|
183 dst += out->linesize[plane] * (outh-1);
|
yading@11
|
184 dstlinesize *= -1;
|
yading@11
|
185 }
|
yading@11
|
186
|
yading@11
|
187 for (y = 0; y < outh; y++) {
|
yading@11
|
188 switch (pixstep) {
|
yading@11
|
189 case 1:
|
yading@11
|
190 for (x = 0; x < outw; x++)
|
yading@11
|
191 dst[x] = src[x*srclinesize + y];
|
yading@11
|
192 break;
|
yading@11
|
193 case 2:
|
yading@11
|
194 for (x = 0; x < outw; x++)
|
yading@11
|
195 *((uint16_t *)(dst + 2*x)) = *((uint16_t *)(src + x*srclinesize + y*2));
|
yading@11
|
196 break;
|
yading@11
|
197 case 3:
|
yading@11
|
198 for (x = 0; x < outw; x++) {
|
yading@11
|
199 int32_t v = AV_RB24(src + x*srclinesize + y*3);
|
yading@11
|
200 AV_WB24(dst + 3*x, v);
|
yading@11
|
201 }
|
yading@11
|
202 break;
|
yading@11
|
203 case 4:
|
yading@11
|
204 for (x = 0; x < outw; x++)
|
yading@11
|
205 *((uint32_t *)(dst + 4*x)) = *((uint32_t *)(src + x*srclinesize + y*4));
|
yading@11
|
206 break;
|
yading@11
|
207 case 6:
|
yading@11
|
208 for (x = 0; x < outw; x++) {
|
yading@11
|
209 int64_t v = AV_RB48(src + x*srclinesize + y*6);
|
yading@11
|
210 AV_WB48(dst + 6*x, v);
|
yading@11
|
211 }
|
yading@11
|
212 break;
|
yading@11
|
213 case 8:
|
yading@11
|
214 for (x = 0; x < outw; x++)
|
yading@11
|
215 *((uint64_t *)(dst + 8*x)) = *((uint64_t *)(src + x*srclinesize + y*8));
|
yading@11
|
216 break;
|
yading@11
|
217 }
|
yading@11
|
218 dst += dstlinesize;
|
yading@11
|
219 }
|
yading@11
|
220 }
|
yading@11
|
221
|
yading@11
|
222 av_frame_free(&in);
|
yading@11
|
223 return ff_filter_frame(outlink, out);
|
yading@11
|
224 }
|
yading@11
|
225
|
yading@11
|
226 #define OFFSET(x) offsetof(TransContext, x)
|
yading@11
|
227 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
|
yading@11
|
228
|
yading@11
|
229 static const AVOption transpose_options[] = {
|
yading@11
|
230 { "dir", "Transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP },
|
yading@11
|
231 TRANSPOSE_CCLOCK_FLIP, TRANSPOSE_CLOCK_FLIP, FLAGS, "dir" },
|
yading@11
|
232 { "cclock_flip", "counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .unit = "dir" },
|
yading@11
|
233 { "clock", "clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .unit = "dir" },
|
yading@11
|
234 { "cclock", "counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .unit = "dir" },
|
yading@11
|
235 { "clock_flip", "clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .unit = "dir" },
|
yading@11
|
236
|
yading@11
|
237 { "passthrough", "do not apply transposition if the input matches the specified geometry",
|
yading@11
|
238 OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, "passthrough" },
|
yading@11
|
239 { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, "passthrough" },
|
yading@11
|
240 { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, "passthrough" },
|
yading@11
|
241 { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, "passthrough" },
|
yading@11
|
242
|
yading@11
|
243 { NULL },
|
yading@11
|
244 };
|
yading@11
|
245
|
yading@11
|
246 AVFILTER_DEFINE_CLASS(transpose);
|
yading@11
|
247
|
yading@11
|
248 static const AVFilterPad avfilter_vf_transpose_inputs[] = {
|
yading@11
|
249 {
|
yading@11
|
250 .name = "default",
|
yading@11
|
251 .type = AVMEDIA_TYPE_VIDEO,
|
yading@11
|
252 .get_video_buffer= get_video_buffer,
|
yading@11
|
253 .filter_frame = filter_frame,
|
yading@11
|
254 },
|
yading@11
|
255 { NULL }
|
yading@11
|
256 };
|
yading@11
|
257
|
yading@11
|
258 static const AVFilterPad avfilter_vf_transpose_outputs[] = {
|
yading@11
|
259 {
|
yading@11
|
260 .name = "default",
|
yading@11
|
261 .config_props = config_props_output,
|
yading@11
|
262 .type = AVMEDIA_TYPE_VIDEO,
|
yading@11
|
263 },
|
yading@11
|
264 { NULL }
|
yading@11
|
265 };
|
yading@11
|
266
|
yading@11
|
267 AVFilter avfilter_vf_transpose = {
|
yading@11
|
268 .name = "transpose",
|
yading@11
|
269 .description = NULL_IF_CONFIG_SMALL("Transpose input video."),
|
yading@11
|
270
|
yading@11
|
271 .priv_size = sizeof(TransContext),
|
yading@11
|
272 .priv_class = &transpose_class,
|
yading@11
|
273
|
yading@11
|
274 .query_formats = query_formats,
|
yading@11
|
275
|
yading@11
|
276 .inputs = avfilter_vf_transpose_inputs,
|
yading@11
|
277 .outputs = avfilter_vf_transpose_outputs,
|
yading@11
|
278 };
|