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/avassert.h"
31 #include "libavutil/imgutils.h"
32 #include "libavutil/internal.h"
33 #include "libavutil/intreadwrite.h"
34 #include "libavutil/opt.h"
35 #include "libavutil/pixdesc.h"
41 #include "transpose.h"
43 typedef struct TransContext
{
49 int passthrough
; ///< PassthroughType, landscape passthrough mode enabled
50 int dir
; ///< TransposeDir
52 TransVtable vtables
[4];
55 static int query_formats(const AVFilterContext
*ctx
,
56 AVFilterFormatsConfig
**cfg_in
,
57 AVFilterFormatsConfig
**cfg_out
)
59 AVFilterFormats
*pix_fmts
= NULL
;
60 const AVPixFmtDescriptor
*desc
;
63 for (fmt
= 0; desc
= av_pix_fmt_desc_get(fmt
); fmt
++) {
64 if (!(desc
->flags
& AV_PIX_FMT_FLAG_PAL
||
65 desc
->flags
& AV_PIX_FMT_FLAG_HWACCEL
||
66 desc
->flags
& AV_PIX_FMT_FLAG_BITSTREAM
||
67 desc
->log2_chroma_w
!= desc
->log2_chroma_h
) &&
68 (ret
= ff_add_format(&pix_fmts
, fmt
)) < 0)
73 return ff_set_common_formats2(ctx
, cfg_in
, cfg_out
, pix_fmts
);
76 static inline void transpose_block_8_c(uint8_t *src
, ptrdiff_t src_linesize
,
77 uint8_t *dst
, ptrdiff_t dst_linesize
,
81 for (y
= 0; y
< h
; y
++, dst
+= dst_linesize
, src
++)
82 for (x
= 0; x
< w
; x
++)
83 dst
[x
] = src
[x
*src_linesize
];
86 static void transpose_8x8_8_c(uint8_t *src
, ptrdiff_t src_linesize
,
87 uint8_t *dst
, ptrdiff_t dst_linesize
)
89 transpose_block_8_c(src
, src_linesize
, dst
, dst_linesize
, 8, 8);
92 static inline void transpose_block_16_c(uint8_t *src
, ptrdiff_t src_linesize
,
93 uint8_t *dst
, ptrdiff_t dst_linesize
,
97 for (y
= 0; y
< h
; y
++, dst
+= dst_linesize
, src
+= 2)
98 for (x
= 0; x
< w
; x
++)
99 *((uint16_t *)(dst
+ 2*x
)) = *((uint16_t *)(src
+ x
*src_linesize
));
102 static void transpose_8x8_16_c(uint8_t *src
, ptrdiff_t src_linesize
,
103 uint8_t *dst
, ptrdiff_t dst_linesize
)
105 transpose_block_16_c(src
, src_linesize
, dst
, dst_linesize
, 8, 8);
108 static inline void transpose_block_24_c(uint8_t *src
, ptrdiff_t src_linesize
,
109 uint8_t *dst
, ptrdiff_t dst_linesize
,
113 for (y
= 0; y
< h
; y
++, dst
+= dst_linesize
) {
114 for (x
= 0; x
< w
; x
++) {
115 int32_t v
= AV_RB24(src
+ x
*src_linesize
+ y
*3);
116 AV_WB24(dst
+ 3*x
, v
);
121 static void transpose_8x8_24_c(uint8_t *src
, ptrdiff_t src_linesize
,
122 uint8_t *dst
, ptrdiff_t dst_linesize
)
124 transpose_block_24_c(src
, src_linesize
, dst
, dst_linesize
, 8, 8);
127 static inline void transpose_block_32_c(uint8_t *src
, ptrdiff_t src_linesize
,
128 uint8_t *dst
, ptrdiff_t dst_linesize
,
132 for (y
= 0; y
< h
; y
++, dst
+= dst_linesize
, src
+= 4) {
133 for (x
= 0; x
< w
; x
++)
134 *((uint32_t *)(dst
+ 4*x
)) = *((uint32_t *)(src
+ x
*src_linesize
));
138 static void transpose_8x8_32_c(uint8_t *src
, ptrdiff_t src_linesize
,
139 uint8_t *dst
, ptrdiff_t dst_linesize
)
141 transpose_block_32_c(src
, src_linesize
, dst
, dst_linesize
, 8, 8);
144 static inline void transpose_block_48_c(uint8_t *src
, ptrdiff_t src_linesize
,
145 uint8_t *dst
, ptrdiff_t dst_linesize
,
149 for (y
= 0; y
< h
; y
++, dst
+= dst_linesize
, src
+= 6) {
150 for (x
= 0; x
< w
; x
++) {
151 int64_t v
= AV_RB48(src
+ x
*src_linesize
);
152 AV_WB48(dst
+ 6*x
, v
);
157 static void transpose_8x8_48_c(uint8_t *src
, ptrdiff_t src_linesize
,
158 uint8_t *dst
, ptrdiff_t dst_linesize
)
160 transpose_block_48_c(src
, src_linesize
, dst
, dst_linesize
, 8, 8);
163 static inline void transpose_block_64_c(uint8_t *src
, ptrdiff_t src_linesize
,
164 uint8_t *dst
, ptrdiff_t dst_linesize
,
168 for (y
= 0; y
< h
; y
++, dst
+= dst_linesize
, src
+= 8)
169 for (x
= 0; x
< w
; x
++)
170 *((uint64_t *)(dst
+ 8*x
)) = *((uint64_t *)(src
+ x
*src_linesize
));
173 static void transpose_8x8_64_c(uint8_t *src
, ptrdiff_t src_linesize
,
174 uint8_t *dst
, ptrdiff_t dst_linesize
)
176 transpose_block_64_c(src
, src_linesize
, dst
, dst_linesize
, 8, 8);
179 static int config_props_output(AVFilterLink
*outlink
)
181 AVFilterContext
*ctx
= outlink
->src
;
182 TransContext
*s
= ctx
->priv
;
183 AVFilterLink
*inlink
= ctx
->inputs
[0];
184 const AVPixFmtDescriptor
*desc_out
= av_pix_fmt_desc_get(outlink
->format
);
185 const AVPixFmtDescriptor
*desc_in
= av_pix_fmt_desc_get(inlink
->format
);
188 av_log(ctx
, AV_LOG_WARNING
,
189 "dir values greater than 3 are deprecated, use the passthrough option instead\n");
191 s
->passthrough
= TRANSPOSE_PT_TYPE_LANDSCAPE
;
194 if ((inlink
->w
>= inlink
->h
&& s
->passthrough
== TRANSPOSE_PT_TYPE_LANDSCAPE
) ||
195 (inlink
->w
<= inlink
->h
&& s
->passthrough
== TRANSPOSE_PT_TYPE_PORTRAIT
)) {
196 av_log(ctx
, AV_LOG_VERBOSE
,
197 "w:%d h:%d -> w:%d h:%d (passthrough mode)\n",
198 inlink
->w
, inlink
->h
, inlink
->w
, inlink
->h
);
201 s
->passthrough
= TRANSPOSE_PT_TYPE_NONE
;
204 s
->hsub
= desc_in
->log2_chroma_w
;
205 s
->vsub
= desc_in
->log2_chroma_h
;
206 s
->planes
= av_pix_fmt_count_planes(outlink
->format
);
208 av_assert0(desc_in
->nb_components
== desc_out
->nb_components
);
211 av_image_fill_max_pixsteps(s
->pixsteps
, NULL
, desc_out
);
213 outlink
->w
= inlink
->h
;
214 outlink
->h
= inlink
->w
;
216 if (inlink
->sample_aspect_ratio
.num
)
217 outlink
->sample_aspect_ratio
= av_div_q((AVRational
) { 1, 1 },
218 inlink
->sample_aspect_ratio
);
220 outlink
->sample_aspect_ratio
= inlink
->sample_aspect_ratio
;
222 for (int i
= 0; i
< 4; i
++) {
223 TransVtable
*v
= &s
->vtables
[i
];
224 switch (s
->pixsteps
[i
]) {
225 case 1: v
->transpose_block
= transpose_block_8_c
;
226 v
->transpose_8x8
= transpose_8x8_8_c
; break;
227 case 2: v
->transpose_block
= transpose_block_16_c
;
228 v
->transpose_8x8
= transpose_8x8_16_c
; break;
229 case 3: v
->transpose_block
= transpose_block_24_c
;
230 v
->transpose_8x8
= transpose_8x8_24_c
; break;
231 case 4: v
->transpose_block
= transpose_block_32_c
;
232 v
->transpose_8x8
= transpose_8x8_32_c
; break;
233 case 6: v
->transpose_block
= transpose_block_48_c
;
234 v
->transpose_8x8
= transpose_8x8_48_c
; break;
235 case 8: v
->transpose_block
= transpose_block_64_c
;
236 v
->transpose_8x8
= transpose_8x8_64_c
; break;
241 for (int i
= 0; i
< 4; i
++) {
242 TransVtable
*v
= &s
->vtables
[i
];
244 ff_transpose_init_x86(v
, s
->pixsteps
[i
]);
248 av_log(ctx
, AV_LOG_VERBOSE
,
249 "w:%d h:%d dir:%d -> w:%d h:%d rotation:%s vflip:%d\n",
250 inlink
->w
, inlink
->h
, s
->dir
, outlink
->w
, outlink
->h
,
251 s
->dir
== 1 || s
->dir
== 3 ? "clockwise" : "counterclockwise",
252 s
->dir
== 0 || s
->dir
== 3);
256 static AVFrame
*get_video_buffer(AVFilterLink
*inlink
, int w
, int h
)
258 TransContext
*s
= inlink
->dst
->priv
;
260 return s
->passthrough
?
261 ff_null_get_video_buffer (inlink
, w
, h
) :
262 ff_default_get_video_buffer(inlink
, w
, h
);
265 typedef struct ThreadData
{
269 static int filter_slice(AVFilterContext
*ctx
, void *arg
, int jobnr
,
272 TransContext
*s
= ctx
->priv
;
273 ThreadData
*td
= arg
;
274 AVFrame
*out
= td
->out
;
275 AVFrame
*in
= td
->in
;
278 for (plane
= 0; plane
< s
->planes
; plane
++) {
279 int hsub
= plane
== 1 || plane
== 2 ? s
->hsub
: 0;
280 int vsub
= plane
== 1 || plane
== 2 ? s
->vsub
: 0;
281 int pixstep
= s
->pixsteps
[plane
];
282 int inh
= AV_CEIL_RSHIFT(in
->height
, vsub
);
283 int outw
= AV_CEIL_RSHIFT(out
->width
, hsub
);
284 int outh
= AV_CEIL_RSHIFT(out
->height
, vsub
);
285 int start
= (outh
* jobnr
) / nb_jobs
;
286 int end
= (outh
* (jobnr
+1)) / nb_jobs
;
288 int dstlinesize
, srclinesize
;
290 TransVtable
*v
= &s
->vtables
[plane
];
292 dstlinesize
= out
->linesize
[plane
];
293 dst
= out
->data
[plane
] + start
* dstlinesize
;
294 src
= in
->data
[plane
];
295 srclinesize
= in
->linesize
[plane
];
298 src
+= in
->linesize
[plane
] * (inh
- 1);
303 dst
= out
->data
[plane
] + dstlinesize
* (outh
- start
- 1);
307 for (y
= start
; y
< end
- 7; y
+= 8) {
308 for (x
= 0; x
< outw
- 7; x
+= 8) {
309 v
->transpose_8x8(src
+ x
* srclinesize
+ y
* pixstep
,
311 dst
+ (y
- start
) * dstlinesize
+ x
* pixstep
,
314 if (outw
- x
> 0 && end
- y
> 0)
315 v
->transpose_block(src
+ x
* srclinesize
+ y
* pixstep
,
317 dst
+ (y
- start
) * dstlinesize
+ x
* pixstep
,
318 dstlinesize
, outw
- x
, end
- y
);
322 v
->transpose_block(src
+ 0 * srclinesize
+ y
* pixstep
,
324 dst
+ (y
- start
) * dstlinesize
+ 0 * pixstep
,
325 dstlinesize
, outw
, end
- y
);
331 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
334 AVFilterContext
*ctx
= inlink
->dst
;
335 TransContext
*s
= ctx
->priv
;
336 AVFilterLink
*outlink
= ctx
->outputs
[0];
341 return ff_filter_frame(outlink
, in
);
343 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
345 err
= AVERROR(ENOMEM
);
349 err
= av_frame_copy_props(out
, in
);
353 if (in
->sample_aspect_ratio
.num
== 0) {
354 out
->sample_aspect_ratio
= in
->sample_aspect_ratio
;
356 out
->sample_aspect_ratio
.num
= in
->sample_aspect_ratio
.den
;
357 out
->sample_aspect_ratio
.den
= in
->sample_aspect_ratio
.num
;
360 td
.in
= in
, td
.out
= out
;
361 ff_filter_execute(ctx
, filter_slice
, &td
, NULL
,
362 FFMIN(outlink
->h
, ff_filter_get_nb_threads(ctx
)));
364 return ff_filter_frame(outlink
, out
);
372 #define OFFSET(x) offsetof(TransContext, x)
373 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
375 static const AVOption transpose_options
[] = {
376 { "dir", "set transpose direction", OFFSET(dir
), AV_OPT_TYPE_INT
, { .i64
= TRANSPOSE_CCLOCK_FLIP
}, 0, 7, FLAGS
, .unit
= "dir" },
377 { "cclock_flip", "rotate counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST
, { .i64
= TRANSPOSE_CCLOCK_FLIP
}, .flags
=FLAGS
, .unit
= "dir" },
378 { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST
, { .i64
= TRANSPOSE_CLOCK
}, .flags
=FLAGS
, .unit
= "dir" },
379 { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST
, { .i64
= TRANSPOSE_CCLOCK
}, .flags
=FLAGS
, .unit
= "dir" },
380 { "clock_flip", "rotate clockwise with vertical flip", 0, AV_OPT_TYPE_CONST
, { .i64
= TRANSPOSE_CLOCK_FLIP
}, .flags
=FLAGS
, .unit
= "dir" },
382 { "passthrough", "do not apply transposition if the input matches the specified geometry",
383 OFFSET(passthrough
), AV_OPT_TYPE_INT
, {.i64
=TRANSPOSE_PT_TYPE_NONE
}, 0, INT_MAX
, FLAGS
, .unit
= "passthrough" },
384 { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST
, {.i64
=TRANSPOSE_PT_TYPE_NONE
}, INT_MIN
, INT_MAX
, FLAGS
, .unit
= "passthrough" },
385 { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST
, {.i64
=TRANSPOSE_PT_TYPE_PORTRAIT
}, INT_MIN
, INT_MAX
, FLAGS
, .unit
= "passthrough" },
386 { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST
, {.i64
=TRANSPOSE_PT_TYPE_LANDSCAPE
}, INT_MIN
, INT_MAX
, FLAGS
, .unit
= "passthrough" },
391 AVFILTER_DEFINE_CLASS(transpose
);
393 static const AVFilterPad avfilter_vf_transpose_inputs
[] = {
396 .type
= AVMEDIA_TYPE_VIDEO
,
397 .get_buffer
.video
= get_video_buffer
,
398 .filter_frame
= filter_frame
,
402 static const AVFilterPad avfilter_vf_transpose_outputs
[] = {
405 .config_props
= config_props_output
,
406 .type
= AVMEDIA_TYPE_VIDEO
,
410 const FFFilter ff_vf_transpose
= {
411 .p
.name
= "transpose",
412 .p
.description
= NULL_IF_CONFIG_SMALL("Transpose input video."),
413 .p
.priv_class
= &transpose_class
,
414 .p
.flags
= AVFILTER_FLAG_SLICE_THREADS
,
415 .priv_size
= sizeof(TransContext
),
416 FILTER_INPUTS(avfilter_vf_transpose_inputs
),
417 FILTER_OUTPUTS(avfilter_vf_transpose_outputs
),
418 FILTER_QUERY_FUNC2(query_formats
),