avfilter: add codecview filter

This commit is contained in:
Clément Bœsch 2014-08-18 16:32:26 +02:00 committed by Clément Bœsch
parent 6dfa70f272
commit f888331769
13 changed files with 374 additions and 3 deletions

View File

@ -9,6 +9,7 @@ version <next>:
- support for using metadata in stream specifiers in fftools
- LZMA compression support in TIFF decoder
- support for H.261 RTP payload format (RFC 4587)
- added codecview filter to visualize information exported by some codecs
version 2.3:

View File

@ -37,6 +37,7 @@
• ported lenscorrection filter from frei0r filter
• large optimizations in dctdnoiz to make it usable
• added codecview filter to visualize information exported by some codecs
┌────────────────────────────┐
│ libavutil │
@ -49,3 +50,4 @@
└────────────────────────────┘
• dctdnoiz filter now uses a block size of 8x8 instead of 16x16 by default
• -vismv option is deprecated in favor of the codecview filter

View File

@ -498,6 +498,8 @@ threading operations
@item vismv @var{integer} (@emph{decoding,video})
Visualize motion vectors (MVs).
This option is deprecated, see the codecview filter instead.
Possible values:
@table @samp
@item pf

View File

@ -2805,6 +2805,42 @@ boxblur=luma_radius=min(h\,w)/10:luma_power=1:chroma_radius=min(cw\,ch)/10:chrom
@end example
@end itemize
@section codecview
Visualize information exported by some codecs.
Some codecs can export information through frames using side-data or other
means. For example, some MPEG based codecs export motion vectors through the
@var{export_mvs} flag in the codec @option{flags2} option.
The filter accepts the following option:
@table @option
@item mv
Set motion vectors to visualize.
Available flags for @var{mv} are:
@table @samp
@item pf
forward predicted MVs of P-frames
@item bf
forward predicted MVs of B-frames
@item bb
backward predicted MVs of B-frames
@end table
@end table
@subsection Examples
@itemize
@item
Visualizes multi-directionals MVs from P and B-Frames using @command{ffplay}:
@example
ffplay -flags2 +export_mvs input.mpg -vf codecview=mv=pf+bf+bb
@end example
@end itemize
@section colorbalance
Modify intensity of primary colors (red, green and blue) of input frames.

View File

@ -1983,6 +1983,7 @@ void ff_mpv_frame_end(MpegEncContext *s)
}
#if FF_API_VISMV
static int clip_line(int *sx, int *sy, int *ex, int *ey, int maxx)
{
if(*sx > *ex)
@ -2107,6 +2108,7 @@ static void draw_arrow(uint8_t *buf, int sx, int sy, int ex,
}
draw_line(buf, sx, sy, ex, ey, w, h, stride, color);
}
#endif
static int add_mb(AVMotionVector *mb, uint32_t mb_type,
int dst_x, int dst_y,
@ -2292,13 +2294,15 @@ void ff_print_debug_info2(AVCodecContext *avctx, AVFrame *pict, uint8_t *mbskip_
if ((avctx->debug & (FF_DEBUG_VIS_QP | FF_DEBUG_VIS_MB_TYPE)) ||
(avctx->debug_mv)) {
const int shift = 1 + quarter_sample;
int mb_y;
uint8_t *ptr;
int i;
int h_chroma_shift, v_chroma_shift, block_height;
#if FF_API_VISMV
const int shift = 1 + quarter_sample;
uint8_t *ptr;
const int width = avctx->width;
const int height = avctx->height;
#endif
const int mv_sample_log2 = avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_SVQ3 ? 2 : 1;
const int mv_stride = (mb_width << mv_sample_log2) +
(avctx->codec->id == AV_CODEC_ID_H264 ? 0 : 1);
@ -2310,13 +2314,16 @@ void ff_print_debug_info2(AVCodecContext *avctx, AVFrame *pict, uint8_t *mbskip_
av_frame_make_writable(pict);
pict->opaque = NULL;
#if FF_API_VISMV
ptr = pict->data[0];
#endif
block_height = 16 >> v_chroma_shift;
for (mb_y = 0; mb_y < mb_height; mb_y++) {
int mb_x;
for (mb_x = 0; mb_x < mb_width; mb_x++) {
const int mb_index = mb_x + mb_y * mb_stride;
#if FF_API_VISMV
if ((avctx->debug_mv) && motion_val[0]) {
int type;
for (type = 0; type < 3; type++) {
@ -2396,6 +2403,7 @@ void ff_print_debug_info2(AVCodecContext *avctx, AVFrame *pict, uint8_t *mbskip_
}
}
}
#endif
if ((avctx->debug & FF_DEBUG_VIS_QP)) {
uint64_t c = (qscale_table[mb_index] * 128 / 31) *
0x0101010101010101ULL;

View File

@ -262,10 +262,12 @@ static const AVOption avcodec_options[] = {
{"buffers", "picture buffer allocations", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_BUFFERS }, INT_MIN, INT_MAX, V|D, "debug"},
{"thread_ops", "threading operations", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_THREADS }, INT_MIN, INT_MAX, V|A|D, "debug"},
{"nomc", "skip motion compensation", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_NOMC }, INT_MIN, INT_MAX, V|A|D, "debug"},
{"vismv", "visualize motion vectors (MVs)", OFFSET(debug_mv), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, INT_MAX, V|D, "debug_mv"},
#if FF_API_VISMV
{"vismv", "visualize motion vectors (MVs) (deprecated)", OFFSET(debug_mv), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, INT_MAX, V|D, "debug_mv"},
{"pf", "forward predicted MVs of P-frames", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_VIS_MV_P_FOR }, INT_MIN, INT_MAX, V|D, "debug_mv"},
{"bf", "forward predicted MVs of B-frames", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_VIS_MV_B_FOR }, INT_MIN, INT_MAX, V|D, "debug_mv"},
{"bb", "backward predicted MVs of B-frames", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_VIS_MV_B_BACK }, INT_MIN, INT_MAX, V|D, "debug_mv"},
#endif
{"cmp", "full-pel ME compare function", OFFSET(me_cmp), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E, "cmp_func"},
{"subcmp", "sub-pel ME compare function", OFFSET(me_sub_cmp), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E, "cmp_func"},
{"mbcmp", "macroblock compare function", OFFSET(mb_cmp), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E, "cmp_func"},

View File

@ -1435,6 +1435,12 @@ int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *code
goto free_and_end;
}
#if FF_API_VISMV
if (avctx->debug_mv)
av_log(avctx, AV_LOG_WARNING, "The 'vismv' option is deprecated, "
"see the codecview filter instead.\n");
#endif
if (av_codec_is_encoder(avctx->codec)) {
int i;
if (avctx->codec->sample_fmts) {

View File

@ -174,5 +174,9 @@
#ifndef FF_API_AFD
#define FF_API_AFD (LIBAVCODEC_VERSION_MAJOR < 57)
#endif
#ifndef FF_API_VISMV
/* XXX: don't forget to drop the -vismv documentation */
#define FF_API_VISMV (LIBAVCODEC_VERSION_MAJOR < 57)
#endif
#endif /* AVCODEC_VERSION_H */

View File

@ -97,6 +97,7 @@ OBJS-$(CONFIG_BLACKDETECT_FILTER) += vf_blackdetect.o
OBJS-$(CONFIG_BLACKFRAME_FILTER) += vf_blackframe.o
OBJS-$(CONFIG_BLEND_FILTER) += vf_blend.o dualinput.o framesync.o
OBJS-$(CONFIG_BOXBLUR_FILTER) += vf_boxblur.o
OBJS-$(CONFIG_CODECVIEW_FILTER) += vf_codecview.o
OBJS-$(CONFIG_COLORBALANCE_FILTER) += vf_colorbalance.o
OBJS-$(CONFIG_COLORCHANNELMIXER_FILTER) += vf_colorchannelmixer.o
OBJS-$(CONFIG_COLORMATRIX_FILTER) += vf_colormatrix.o

View File

@ -115,6 +115,7 @@ void avfilter_register_all(void)
REGISTER_FILTER(BLACKFRAME, blackframe, vf);
REGISTER_FILTER(BLEND, blend, vf);
REGISTER_FILTER(BOXBLUR, boxblur, vf);
REGISTER_FILTER(CODECVIEW, codecview, vf);
REGISTER_FILTER(COLORBALANCE, colorbalance, vf);
REGISTER_FILTER(COLORCHANNELMIXER, colorchannelmixer, vf);
REGISTER_FILTER(COLORMATRIX, colormatrix, vf);

244
libavfilter/vf_codecview.c Normal file
View File

@ -0,0 +1,244 @@
/*
* Copyright (c) 2002-2004 Michael Niedermayer <michaelni@gmx.at>
* Copyright (c) 2014 Clément Bœsch <u pkh me>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* Codec debug viewer filter.
*
* All the MV drawing code from Michael Niedermayer is extracted from
* libavcodec/mpegvideo.c.
*
* TODO: segmentation
* TODO: quantization
*/
#include "libavutil/imgutils.h"
#include "libavutil/motion_vector.h"
#include "libavutil/opt.h"
#include "avfilter.h"
#include "internal.h"
#define MV_P_FOR (1<<0)
#define MV_B_FOR (1<<1)
#define MV_B_BACK (1<<2)
typedef struct {
const AVClass *class;
unsigned mv;
} CodecViewContext;
#define OFFSET(x) offsetof(CodecViewContext, x)
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
static const AVOption codecview_options[] = {
{ "mv", "set motion vectors to visualize", OFFSET(mv), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, INT_MAX, FLAGS, "mv" },
{"pf", "forward predicted MVs of P-frames", 0, AV_OPT_TYPE_CONST, {.i64 = MV_P_FOR }, INT_MIN, INT_MAX, FLAGS, "mv"},
{"bf", "forward predicted MVs of B-frames", 0, AV_OPT_TYPE_CONST, {.i64 = MV_B_FOR }, INT_MIN, INT_MAX, FLAGS, "mv"},
{"bb", "backward predicted MVs of B-frames", 0, AV_OPT_TYPE_CONST, {.i64 = MV_B_BACK }, INT_MIN, INT_MAX, FLAGS, "mv"},
{ NULL }
};
AVFILTER_DEFINE_CLASS(codecview);
static int query_formats(AVFilterContext *ctx)
{
// TODO: we can probably add way more pixel formats without any other
// changes; anything with 8-bit luma in first plane should be working
static const enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE};
ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
return 0;
}
static int clip_line(int *sx, int *sy, int *ex, int *ey, int maxx)
{
if(*sx > *ex)
return clip_line(ex, ey, sx, sy, maxx);
if (*sx < 0) {
if (*ex < 0)
return 1;
*sy = *ey + (*sy - *ey) * (int64_t)*ex / (*ex - *sx);
*sx = 0;
}
if (*ex > maxx) {
if (*sx > maxx)
return 1;
*ey = *sy + (*ey - *sy) * (int64_t)(maxx - *sx) / (*ex - *sx);
*ex = maxx;
}
return 0;
}
/**
* Draw a line from (ex, ey) -> (sx, sy).
* @param w width of the image
* @param h height of the image
* @param stride stride/linesize of the image
* @param color color of the arrow
*/
static void draw_line(uint8_t *buf, int sx, int sy, int ex, int ey,
int w, int h, int stride, int color)
{
int x, y, fr, f;
if (clip_line(&sx, &sy, &ex, &ey, w - 1))
return;
if (clip_line(&sy, &sx, &ey, &ex, h - 1))
return;
sx = av_clip(sx, 0, w - 1);
sy = av_clip(sy, 0, h - 1);
ex = av_clip(ex, 0, w - 1);
ey = av_clip(ey, 0, h - 1);
buf[sy * stride + sx] += color;
if (FFABS(ex - sx) > FFABS(ey - sy)) {
if (sx > ex) {
FFSWAP(int, sx, ex);
FFSWAP(int, sy, ey);
}
buf += sx + sy * stride;
ex -= sx;
f = ((ey - sy) << 16) / ex;
for (x = 0; x <= ex; x++) {
y = (x * f) >> 16;
fr = (x * f) & 0xFFFF;
buf[ y * stride + x] += (color * (0x10000 - fr)) >> 16;
if(fr) buf[(y + 1) * stride + x] += (color * fr ) >> 16;
}
} else {
if (sy > ey) {
FFSWAP(int, sx, ex);
FFSWAP(int, sy, ey);
}
buf += sx + sy * stride;
ey -= sy;
if (ey)
f = ((ex - sx) << 16) / ey;
else
f = 0;
for(y= 0; y <= ey; y++){
x = (y*f) >> 16;
fr = (y*f) & 0xFFFF;
buf[y * stride + x ] += (color * (0x10000 - fr)) >> 16;
if(fr) buf[y * stride + x + 1] += (color * fr ) >> 16;
}
}
}
/**
* Draw an arrow from (ex, ey) -> (sx, sy).
* @param w width of the image
* @param h height of the image
* @param stride stride/linesize of the image
* @param color color of the arrow
*/
static void draw_arrow(uint8_t *buf, int sx, int sy, int ex,
int ey, int w, int h, int stride, int color, int tail, int direction)
{
int dx,dy;
if (direction) {
FFSWAP(int, sx, ex);
FFSWAP(int, sy, ey);
}
sx = av_clip(sx, -100, w + 100);
sy = av_clip(sy, -100, h + 100);
ex = av_clip(ex, -100, w + 100);
ey = av_clip(ey, -100, h + 100);
dx = ex - sx;
dy = ey - sy;
if (dx * dx + dy * dy > 3 * 3) {
int rx = dx + dy;
int ry = -dx + dy;
int length = sqrt((rx * rx + ry * ry) << 8);
// FIXME subpixel accuracy
rx = ROUNDED_DIV(rx * 3 << 4, length);
ry = ROUNDED_DIV(ry * 3 << 4, length);
if (tail) {
rx = -rx;
ry = -ry;
}
draw_line(buf, sx, sy, sx + rx, sy + ry, w, h, stride, color);
draw_line(buf, sx, sy, sx - ry, sy + rx, w, h, stride, color);
}
draw_line(buf, sx, sy, ex, ey, w, h, stride, color);
}
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
AVFilterContext *ctx = inlink->dst;
CodecViewContext *s = ctx->priv;
AVFilterLink *outlink = ctx->outputs[0];
AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_MOTION_VECTORS);
if (sd) {
int i;
const AVMotionVector *mvs = (const AVMotionVector *)sd->data;
for (i = 0; i < sd->size / sizeof(*mvs); i++) {
const AVMotionVector *mv = &mvs[i];
const int direction = mv->source > 0;
if ((direction == 0 && (s->mv & MV_P_FOR) && frame->pict_type == AV_PICTURE_TYPE_P) ||
(direction == 0 && (s->mv & MV_B_FOR) && frame->pict_type == AV_PICTURE_TYPE_B) ||
(direction == 1 && (s->mv & MV_B_BACK) && frame->pict_type == AV_PICTURE_TYPE_B))
draw_arrow(frame->data[0], mv->dst_x, mv->dst_y, mv->src_x, mv->src_y,
frame->width, frame->height, frame->linesize[0],
100, 0, mv->source > 0);
}
}
return ff_filter_frame(outlink, frame);
}
static const AVFilterPad codecview_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.filter_frame = filter_frame,
.needs_writable = 1,
},
{ NULL }
};
static const AVFilterPad codecview_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
},
{ NULL }
};
AVFilter ff_vf_codecview = {
.name = "codecview",
.description = NULL_IF_CONFIG_SMALL("Visualize information about some codecs"),
.priv_size = sizeof(CodecViewContext),
.query_formats = query_formats,
.inputs = codecview_inputs,
.outputs = codecview_outputs,
.priv_class = &codecview_class,
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
};

View File

@ -23,6 +23,9 @@ fate-filter-mcdeint-medium: CMD = framecrc -flags bitexact -idct simple -i $(TAR
FATE_FILTER-$(call ALLYES, MCDEINT_FILTER, MPEGTS_DEMUXER, MPEG2VIDEO_DECODER SNOW_ENCODER) += $(FATE_MCDEINT)
FATE_FILTER-$(call ALLYES, CODECVIEW_FILTER RM_DEMUXER RV40_DECODER) += fate-filter-codecview-mvs
fate-filter-codecview-mvs: CMD = framecrc -flags2 +export_mvs -i $(TARGET_SAMPLES)/real/spygames-2MB.rmvb -vf codecview=mv=pf+bf+bb -vframes 60 -an
FATE_SAMPLES_AVCONV += $(FATE_FILTER-yes)
FATE_FILTER-$(call ALLYES, AVDEVICE LIFE_FILTER) += fate-filter-lavd-life

View File

@ -0,0 +1,61 @@
#tb 0: 32768/785647
0, 0, 0, 1, 276480, 0x5f7a0d4f
0, 1, 1, 1, 276480, 0x5f7a0d4f
0, 2, 2, 1, 276480, 0x5f7a0d4f
0, 3, 3, 1, 276480, 0x5f7a0d4f
0, 4, 4, 1, 276480, 0x5f7a0d4f
0, 5, 5, 1, 276480, 0x5f7a0d4f
0, 6, 6, 1, 276480, 0x5f7a0d4f
0, 7, 7, 1, 276480, 0x5f7a0d4f
0, 8, 8, 1, 276480, 0x5f7a0d4f
0, 9, 9, 1, 276480, 0x5f7a0d4f
0, 10, 10, 1, 276480, 0x5f7a0d4f
0, 11, 11, 1, 276480, 0x5f7a0d4f
0, 12, 12, 1, 276480, 0x5f7a0d4f
0, 13, 13, 1, 276480, 0x5f7a0d4f
0, 14, 14, 1, 276480, 0x5f7a0d4f
0, 15, 15, 1, 276480, 0x5f7a0d4f
0, 16, 16, 1, 276480, 0x5f7a0d4f
0, 17, 17, 1, 276480, 0x5f7a0d4f
0, 18, 18, 1, 276480, 0x5f7a0d4f
0, 19, 19, 1, 276480, 0x5f7a0d4f
0, 20, 20, 1, 276480, 0x5f7a0d4f
0, 21, 21, 1, 276480, 0x5f7a0d4f
0, 22, 22, 1, 276480, 0x5f7a0d4f
0, 23, 23, 1, 276480, 0x5f7a0d4f
0, 24, 24, 1, 276480, 0x5f7a0d4f
0, 25, 25, 1, 276480, 0x5f7a0d4f
0, 26, 26, 1, 276480, 0x5f7a0d4f
0, 27, 27, 1, 276480, 0x5f7a0d4f
0, 28, 28, 1, 276480, 0x5f7a0d4f
0, 29, 29, 1, 276480, 0x5f7a0d4f
0, 30, 30, 1, 276480, 0x5f7a0d4f
0, 31, 31, 1, 276480, 0x5f7a0d4f
0, 32, 32, 1, 276480, 0x5f7a0d4f
0, 33, 33, 1, 276480, 0x75641594
0, 34, 34, 1, 276480, 0x32ee3526
0, 35, 35, 1, 276480, 0xcb53479a
0, 36, 36, 1, 276480, 0x7ca9658e
0, 37, 37, 1, 276480, 0x5ce39368
0, 38, 38, 1, 276480, 0x4ec1e418
0, 39, 39, 1, 276480, 0x23c418ae
0, 40, 40, 1, 276480, 0x70ac4c76
0, 41, 41, 1, 276480, 0x166ef020
0, 42, 42, 1, 276480, 0xaa0614ab
0, 43, 43, 1, 276480, 0x8bc2fa2b
0, 44, 44, 1, 276480, 0x07956a97
0, 45, 45, 1, 276480, 0x6fcb808b
0, 46, 46, 1, 276480, 0x841cf493
0, 47, 47, 1, 276480, 0xe7834514
0, 48, 48, 1, 276480, 0xb805fcb1
0, 49, 49, 1, 276480, 0xe29bacc8
0, 50, 50, 1, 276480, 0x6b79c3d3
0, 51, 51, 1, 276480, 0x798c35c6
0, 52, 52, 1, 276480, 0x1b31d2ed
0, 53, 53, 1, 276480, 0x6ceebf3e
0, 54, 54, 1, 276480, 0x7ac8de3c
0, 55, 55, 1, 276480, 0x14d6768c
0, 56, 56, 1, 276480, 0xcb08af08
0, 57, 57, 1, 276480, 0xed3053ea
0, 58, 58, 1, 276480, 0x015782fb
0, 59, 59, 1, 276480, 0xf849eb88