2 * Copyright (c) 2010 Stefano Sabatini
3 * Copyright (c) 2008 Vitor Sessak
5 * This file is part of FFmpeg.
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.
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.
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
24 * transposition filter
25 * Based on MPlayer libmpcodecs/vf_rotate.c.
30 #include "libavutil/imgutils.h"
31 #include "libavutil/internal.h"
32 #include "libavutil/intreadwrite.h"
33 #include "libavutil/opt.h"
34 #include "libavutil/pixdesc.h"
42 TRANSPOSE_PT_TYPE_NONE
,
43 TRANSPOSE_PT_TYPE_LANDSCAPE
,
44 TRANSPOSE_PT_TYPE_PORTRAIT
,
48 TRANSPOSE_CCLOCK_FLIP
,
54 typedef struct TransContext
{
59 PassthroughType passthrough
; ///< landscape passthrough mode enabled
60 enum TransposeDir dir
;
63 static int query_formats(AVFilterContext
*ctx
)
65 AVFilterFormats
*pix_fmts
= NULL
;
68 for (fmt
= 0; av_pix_fmt_desc_get(fmt
); fmt
++) {
69 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(fmt
);
70 if (!(desc
->flags
& AV_PIX_FMT_FLAG_PAL
||
71 desc
->flags
& AV_PIX_FMT_FLAG_HWACCEL
||
72 desc
->flags
& AV_PIX_FMT_FLAG_BITSTREAM
||
73 desc
->log2_chroma_w
!= desc
->log2_chroma_h
))
74 ff_add_format(&pix_fmts
, fmt
);
78 ff_set_common_formats(ctx
, pix_fmts
);
82 static int config_props_output(AVFilterLink
*outlink
)
84 AVFilterContext
*ctx
= outlink
->src
;
85 TransContext
*trans
= ctx
->priv
;
86 AVFilterLink
*inlink
= ctx
->inputs
[0];
87 const AVPixFmtDescriptor
*desc_out
= av_pix_fmt_desc_get(outlink
->format
);
88 const AVPixFmtDescriptor
*desc_in
= av_pix_fmt_desc_get(inlink
->format
);
91 av_log(ctx
, AV_LOG_WARNING
,
92 "dir values greater than 3 are deprecated, use the passthrough option instead\n");
94 trans
->passthrough
= TRANSPOSE_PT_TYPE_LANDSCAPE
;
97 if ((inlink
->w
>= inlink
->h
&& trans
->passthrough
== TRANSPOSE_PT_TYPE_LANDSCAPE
) ||
98 (inlink
->w
<= inlink
->h
&& trans
->passthrough
== TRANSPOSE_PT_TYPE_PORTRAIT
)) {
99 av_log(ctx
, AV_LOG_VERBOSE
,
100 "w:%d h:%d -> w:%d h:%d (passthrough mode)\n",
101 inlink
->w
, inlink
->h
, inlink
->w
, inlink
->h
);
104 trans
->passthrough
= TRANSPOSE_PT_TYPE_NONE
;
107 trans
->hsub
= desc_in
->log2_chroma_w
;
108 trans
->vsub
= desc_in
->log2_chroma_h
;
110 av_image_fill_max_pixsteps(trans
->pixsteps
, NULL
, desc_out
);
112 outlink
->w
= inlink
->h
;
113 outlink
->h
= inlink
->w
;
115 if (inlink
->sample_aspect_ratio
.num
)
116 outlink
->sample_aspect_ratio
= av_div_q((AVRational
) { 1, 1 },
117 inlink
->sample_aspect_ratio
);
119 outlink
->sample_aspect_ratio
= inlink
->sample_aspect_ratio
;
121 av_log(ctx
, AV_LOG_VERBOSE
,
122 "w:%d h:%d dir:%d -> w:%d h:%d rotation:%s vflip:%d\n",
123 inlink
->w
, inlink
->h
, trans
->dir
, outlink
->w
, outlink
->h
,
124 trans
->dir
== 1 || trans
->dir
== 3 ? "clockwise" : "counterclockwise",
125 trans
->dir
== 0 || trans
->dir
== 3);
129 static AVFrame
*get_video_buffer(AVFilterLink
*inlink
, int w
, int h
)
131 TransContext
*trans
= inlink
->dst
->priv
;
133 return trans
->passthrough
?
134 ff_null_get_video_buffer (inlink
, w
, h
) :
135 ff_default_get_video_buffer(inlink
, w
, h
);
138 typedef struct ThreadData
{
142 static int filter_slice(AVFilterContext
*ctx
, void *arg
, int jobnr
,
145 TransContext
*trans
= ctx
->priv
;
146 ThreadData
*td
= arg
;
147 AVFrame
*out
= td
->out
;
148 AVFrame
*in
= td
->in
;
151 for (plane
= 0; out
->data
[plane
]; plane
++) {
152 int hsub
= plane
== 1 || plane
== 2 ? trans
->hsub
: 0;
153 int vsub
= plane
== 1 || plane
== 2 ? trans
->vsub
: 0;
154 int pixstep
= trans
->pixsteps
[plane
];
155 int inh
= in
->height
>> vsub
;
156 int outw
= FF_CEIL_RSHIFT(out
->width
, hsub
);
157 int outh
= FF_CEIL_RSHIFT(out
->height
, vsub
);
158 int start
= (outh
* jobnr
) / nb_jobs
;
159 int end
= (outh
* (jobnr
+1)) / nb_jobs
;
161 int dstlinesize
, srclinesize
;
164 dstlinesize
= out
->linesize
[plane
];
165 dst
= out
->data
[plane
] + start
* dstlinesize
;
166 src
= in
->data
[plane
];
167 srclinesize
= in
->linesize
[plane
];
169 if (trans
->dir
& 1) {
170 src
+= in
->linesize
[plane
] * (inh
- 1);
174 if (trans
->dir
& 2) {
175 dst
= out
->data
[plane
] + dstlinesize
* (outh
- start
- 1);
181 for (y
= start
; y
< end
; y
++, dst
+= dstlinesize
)
182 for (x
= 0; x
< outw
; x
++)
183 dst
[x
] = src
[x
* srclinesize
+ y
];
186 for (y
= start
; y
< end
; y
++, dst
+= dstlinesize
) {
187 for (x
= 0; x
< outw
; x
++)
188 *((uint16_t *)(dst
+ 2 * x
)) =
189 *((uint16_t *)(src
+ x
* srclinesize
+ y
* 2));
193 for (y
= start
; y
< end
; y
++, dst
+= dstlinesize
) {
194 for (x
= 0; x
< outw
; x
++) {
195 int32_t v
= AV_RB24(src
+ x
* srclinesize
+ y
* 3);
196 AV_WB24(dst
+ 3 * x
, v
);
201 for (y
= start
; y
< end
; y
++, dst
+= dstlinesize
) {
202 for (x
= 0; x
< outw
; x
++)
203 *((uint32_t *)(dst
+ 4 * x
)) =
204 *((uint32_t *)(src
+ x
* srclinesize
+ y
* 4));
208 for (y
= start
; y
< end
; y
++, dst
+= dstlinesize
) {
209 for (x
= 0; x
< outw
; x
++) {
210 int64_t v
= AV_RB48(src
+ x
* srclinesize
+ y
*6);
211 AV_WB48(dst
+ 6*x
, v
);
216 for (y
= start
; y
< end
; y
++, dst
+= dstlinesize
) {
217 for (x
= 0; x
< outw
; x
++)
218 *((uint64_t *)(dst
+ 8*x
)) = *((uint64_t *)(src
+ x
* srclinesize
+ y
*8));
227 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
229 AVFilterContext
*ctx
= inlink
->dst
;
230 TransContext
*trans
= ctx
->priv
;
231 AVFilterLink
*outlink
= ctx
->outputs
[0];
235 if (trans
->passthrough
)
236 return ff_filter_frame(outlink
, in
);
238 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
241 return AVERROR(ENOMEM
);
243 av_frame_copy_props(out
, in
);
245 if (in
->sample_aspect_ratio
.num
== 0) {
246 out
->sample_aspect_ratio
= in
->sample_aspect_ratio
;
248 out
->sample_aspect_ratio
.num
= in
->sample_aspect_ratio
.den
;
249 out
->sample_aspect_ratio
.den
= in
->sample_aspect_ratio
.num
;
252 td
.in
= in
, td
.out
= out
;
253 ctx
->internal
->execute(ctx
, filter_slice
, &td
, NULL
, FFMIN(outlink
->h
, ctx
->graph
->nb_threads
));
255 return ff_filter_frame(outlink
, out
);
258 #define OFFSET(x) offsetof(TransContext, x)
259 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
261 static const AVOption transpose_options
[] = {
262 { "dir", "set transpose direction", OFFSET(dir
), AV_OPT_TYPE_INT
, { .i64
= TRANSPOSE_CCLOCK_FLIP
}, 0, 7, FLAGS
, "dir" },
263 { "cclock_flip", "rotate counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST
, { .i64
= TRANSPOSE_CCLOCK_FLIP
}, .unit
= "dir" },
264 { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST
, { .i64
= TRANSPOSE_CLOCK
}, .unit
= "dir" },
265 { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST
, { .i64
= TRANSPOSE_CCLOCK
}, .unit
= "dir" },
266 { "clock_flip", "rotate clockwise with vertical flip", 0, AV_OPT_TYPE_CONST
, { .i64
= TRANSPOSE_CLOCK_FLIP
}, .unit
= "dir" },
268 { "passthrough", "do not apply transposition if the input matches the specified geometry",
269 OFFSET(passthrough
), AV_OPT_TYPE_INT
, {.i64
=TRANSPOSE_PT_TYPE_NONE
}, 0, INT_MAX
, FLAGS
, "passthrough" },
270 { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST
, {.i64
=TRANSPOSE_PT_TYPE_NONE
}, INT_MIN
, INT_MAX
, FLAGS
, "passthrough" },
271 { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST
, {.i64
=TRANSPOSE_PT_TYPE_PORTRAIT
}, INT_MIN
, INT_MAX
, FLAGS
, "passthrough" },
272 { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST
, {.i64
=TRANSPOSE_PT_TYPE_LANDSCAPE
}, INT_MIN
, INT_MAX
, FLAGS
, "passthrough" },
277 AVFILTER_DEFINE_CLASS(transpose
);
279 static const AVFilterPad avfilter_vf_transpose_inputs
[] = {
282 .type
= AVMEDIA_TYPE_VIDEO
,
283 .get_video_buffer
= get_video_buffer
,
284 .filter_frame
= filter_frame
,
289 static const AVFilterPad avfilter_vf_transpose_outputs
[] = {
292 .config_props
= config_props_output
,
293 .type
= AVMEDIA_TYPE_VIDEO
,
298 AVFilter ff_vf_transpose
= {
300 .description
= NULL_IF_CONFIG_SMALL("Transpose input video."),
301 .priv_size
= sizeof(TransContext
),
302 .priv_class
= &transpose_class
,
303 .query_formats
= query_formats
,
304 .inputs
= avfilter_vf_transpose_inputs
,
305 .outputs
= avfilter_vf_transpose_outputs
,
306 .flags
= AVFILTER_FLAG_SLICE_THREADS
,