2 * Copyright (c) 2002-2004 Michael Niedermayer <michaelni@gmx.at>
3 * Copyright (c) 2014 Clément Bœsch <u pkh me>
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 * Codec debug viewer filter.
26 * All the MV drawing code from Michael Niedermayer is extracted from
27 * libavcodec/mpegvideo.c.
33 #include "libavutil/imgutils.h"
34 #include "libavutil/motion_vector.h"
35 #include "libavutil/opt.h"
39 #define MV_P_FOR (1<<0)
40 #define MV_B_FOR (1<<1)
41 #define MV_B_BACK (1<<2)
48 #define OFFSET(x) offsetof(CodecViewContext, x)
49 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
50 static const AVOption codecview_options
[] = {
51 { "mv", "set motion vectors to visualize", OFFSET(mv
), AV_OPT_TYPE_FLAGS
, {.i64
=0}, 0, INT_MAX
, FLAGS
, "mv" },
52 {"pf", "forward predicted MVs of P-frames", 0, AV_OPT_TYPE_CONST
, {.i64
= MV_P_FOR
}, INT_MIN
, INT_MAX
, FLAGS
, "mv"},
53 {"bf", "forward predicted MVs of B-frames", 0, AV_OPT_TYPE_CONST
, {.i64
= MV_B_FOR
}, INT_MIN
, INT_MAX
, FLAGS
, "mv"},
54 {"bb", "backward predicted MVs of B-frames", 0, AV_OPT_TYPE_CONST
, {.i64
= MV_B_BACK
}, INT_MIN
, INT_MAX
, FLAGS
, "mv"},
58 AVFILTER_DEFINE_CLASS(codecview
);
60 static int query_formats(AVFilterContext
*ctx
)
62 // TODO: we can probably add way more pixel formats without any other
63 // changes; anything with 8-bit luma in first plane should be working
64 static const enum AVPixelFormat pix_fmts
[] = {AV_PIX_FMT_YUV420P
, AV_PIX_FMT_NONE
};
65 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
69 static int clip_line(int *sx
, int *sy
, int *ex
, int *ey
, int maxx
)
72 return clip_line(ex
, ey
, sx
, sy
, maxx
);
77 *sy
= *ey
+ (*sy
- *ey
) * (int64_t)*ex
/ (*ex
- *sx
);
84 *ey
= *sy
+ (*ey
- *sy
) * (int64_t)(maxx
- *sx
) / (*ex
- *sx
);
91 * Draw a line from (ex, ey) -> (sx, sy).
92 * @param w width of the image
93 * @param h height of the image
94 * @param stride stride/linesize of the image
95 * @param color color of the arrow
97 static void draw_line(uint8_t *buf
, int sx
, int sy
, int ex
, int ey
,
98 int w
, int h
, int stride
, int color
)
102 if (clip_line(&sx
, &sy
, &ex
, &ey
, w
- 1))
104 if (clip_line(&sy
, &sx
, &ey
, &ex
, h
- 1))
107 sx
= av_clip(sx
, 0, w
- 1);
108 sy
= av_clip(sy
, 0, h
- 1);
109 ex
= av_clip(ex
, 0, w
- 1);
110 ey
= av_clip(ey
, 0, h
- 1);
112 buf
[sy
* stride
+ sx
] += color
;
114 if (FFABS(ex
- sx
) > FFABS(ey
- sy
)) {
119 buf
+= sx
+ sy
* stride
;
121 f
= ((ey
- sy
) << 16) / ex
;
122 for (x
= 0; x
<= ex
; x
++) {
124 fr
= (x
* f
) & 0xFFFF;
125 buf
[ y
* stride
+ x
] += (color
* (0x10000 - fr
)) >> 16;
126 if(fr
) buf
[(y
+ 1) * stride
+ x
] += (color
* fr
) >> 16;
133 buf
+= sx
+ sy
* stride
;
136 f
= ((ex
- sx
) << 16) / ey
;
139 for(y
= 0; y
<= ey
; y
++){
142 buf
[y
* stride
+ x
] += (color
* (0x10000 - fr
)) >> 16;
143 if(fr
) buf
[y
* stride
+ x
+ 1] += (color
* fr
) >> 16;
149 * Draw an arrow from (ex, ey) -> (sx, sy).
150 * @param w width of the image
151 * @param h height of the image
152 * @param stride stride/linesize of the image
153 * @param color color of the arrow
155 static void draw_arrow(uint8_t *buf
, int sx
, int sy
, int ex
,
156 int ey
, int w
, int h
, int stride
, int color
, int tail
, int direction
)
165 sx
= av_clip(sx
, -100, w
+ 100);
166 sy
= av_clip(sy
, -100, h
+ 100);
167 ex
= av_clip(ex
, -100, w
+ 100);
168 ey
= av_clip(ey
, -100, h
+ 100);
173 if (dx
* dx
+ dy
* dy
> 3 * 3) {
176 int length
= sqrt((rx
* rx
+ ry
* ry
) << 8);
178 // FIXME subpixel accuracy
179 rx
= ROUNDED_DIV(rx
* 3 << 4, length
);
180 ry
= ROUNDED_DIV(ry
* 3 << 4, length
);
187 draw_line(buf
, sx
, sy
, sx
+ rx
, sy
+ ry
, w
, h
, stride
, color
);
188 draw_line(buf
, sx
, sy
, sx
- ry
, sy
+ rx
, w
, h
, stride
, color
);
190 draw_line(buf
, sx
, sy
, ex
, ey
, w
, h
, stride
, color
);
193 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*frame
)
195 AVFilterContext
*ctx
= inlink
->dst
;
196 CodecViewContext
*s
= ctx
->priv
;
197 AVFilterLink
*outlink
= ctx
->outputs
[0];
199 AVFrameSideData
*sd
= av_frame_get_side_data(frame
, AV_FRAME_DATA_MOTION_VECTORS
);
202 const AVMotionVector
*mvs
= (const AVMotionVector
*)sd
->data
;
203 for (i
= 0; i
< sd
->size
/ sizeof(*mvs
); i
++) {
204 const AVMotionVector
*mv
= &mvs
[i
];
205 const int direction
= mv
->source
> 0;
206 if ((direction
== 0 && (s
->mv
& MV_P_FOR
) && frame
->pict_type
== AV_PICTURE_TYPE_P
) ||
207 (direction
== 0 && (s
->mv
& MV_B_FOR
) && frame
->pict_type
== AV_PICTURE_TYPE_B
) ||
208 (direction
== 1 && (s
->mv
& MV_B_BACK
) && frame
->pict_type
== AV_PICTURE_TYPE_B
))
209 draw_arrow(frame
->data
[0], mv
->dst_x
, mv
->dst_y
, mv
->src_x
, mv
->src_y
,
210 frame
->width
, frame
->height
, frame
->linesize
[0],
211 100, 0, mv
->source
> 0);
214 return ff_filter_frame(outlink
, frame
);
217 static const AVFilterPad codecview_inputs
[] = {
220 .type
= AVMEDIA_TYPE_VIDEO
,
221 .filter_frame
= filter_frame
,
227 static const AVFilterPad codecview_outputs
[] = {
230 .type
= AVMEDIA_TYPE_VIDEO
,
235 AVFilter ff_vf_codecview
= {
237 .description
= NULL_IF_CONFIG_SMALL("Visualize information about some codecs"),
238 .priv_size
= sizeof(CodecViewContext
),
239 .query_formats
= query_formats
,
240 .inputs
= codecview_inputs
,
241 .outputs
= codecview_outputs
,
242 .priv_class
= &codecview_class
,
243 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,