LCOV - code coverage report
Current view: top level - libavfilter - vf_codecview.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 93 127 73.2 %
Date: 2017-12-13 18:07:29 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2002-2004 Michael Niedermayer <michaelni@gmx.at>
       3             :  * Copyright (c) 2014 Clément Bœsch <u pkh me>
       4             :  *
       5             :  * This file is part of FFmpeg.
       6             :  *
       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.
      11             :  *
      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.
      16             :  *
      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
      20             :  */
      21             : 
      22             : /**
      23             :  * @file
      24             :  * Codec debug viewer filter.
      25             :  *
      26             :  * All the MV drawing code from Michael Niedermayer is extracted from
      27             :  * libavcodec/mpegvideo.c.
      28             :  *
      29             :  * TODO: segmentation
      30             :  */
      31             : 
      32             : #include "libavutil/imgutils.h"
      33             : #include "libavutil/motion_vector.h"
      34             : #include "libavutil/opt.h"
      35             : #include "avfilter.h"
      36             : #include "internal.h"
      37             : 
      38             : #define MV_P_FOR  (1<<0)
      39             : #define MV_B_FOR  (1<<1)
      40             : #define MV_B_BACK (1<<2)
      41             : #define MV_TYPE_FOR  (1<<0)
      42             : #define MV_TYPE_BACK (1<<1)
      43             : #define FRAME_TYPE_I (1<<0)
      44             : #define FRAME_TYPE_P (1<<1)
      45             : #define FRAME_TYPE_B (1<<2)
      46             : 
      47             : typedef struct CodecViewContext {
      48             :     const AVClass *class;
      49             :     unsigned mv;
      50             :     unsigned frame_type;
      51             :     unsigned mv_type;
      52             :     int hsub, vsub;
      53             :     int qp;
      54             : } CodecViewContext;
      55             : 
      56             : #define OFFSET(x) offsetof(CodecViewContext, x)
      57             : #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
      58             : #define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, unit }
      59             : 
      60             : static const AVOption codecview_options[] = {
      61             :     { "mv", "set motion vectors to visualize", OFFSET(mv), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, INT_MAX, FLAGS, "mv" },
      62             :         CONST("pf", "forward predicted MVs of P-frames",  MV_P_FOR,  "mv"),
      63             :         CONST("bf", "forward predicted MVs of B-frames",  MV_B_FOR,  "mv"),
      64             :         CONST("bb", "backward predicted MVs of B-frames", MV_B_BACK, "mv"),
      65             :     { "qp", NULL, OFFSET(qp), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, .flags = FLAGS },
      66             :     { "mv_type", "set motion vectors type", OFFSET(mv_type), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, INT_MAX, FLAGS, "mv_type" },
      67             :     { "mvt",     "set motion vectors type", OFFSET(mv_type), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, INT_MAX, FLAGS, "mv_type" },
      68             :         CONST("fp", "forward predicted MVs",  MV_TYPE_FOR,  "mv_type"),
      69             :         CONST("bp", "backward predicted MVs", MV_TYPE_BACK, "mv_type"),
      70             :     { "frame_type", "set frame types to visualize motion vectors of", OFFSET(frame_type), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, INT_MAX, FLAGS, "frame_type" },
      71             :     { "ft",         "set frame types to visualize motion vectors of", OFFSET(frame_type), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, INT_MAX, FLAGS, "frame_type" },
      72             :         CONST("if", "I-frames", FRAME_TYPE_I, "frame_type"),
      73             :         CONST("pf", "P-frames", FRAME_TYPE_P, "frame_type"),
      74             :         CONST("bf", "B-frames", FRAME_TYPE_B, "frame_type"),
      75             :     { NULL }
      76             : };
      77             : 
      78             : AVFILTER_DEFINE_CLASS(codecview);
      79             : 
      80           1 : static int query_formats(AVFilterContext *ctx)
      81             : {
      82             :     // TODO: we can probably add way more pixel formats without any other
      83             :     // changes; anything with 8-bit luma in first plane should be working
      84             :     static const enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE};
      85           1 :     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
      86           1 :     if (!fmts_list)
      87           0 :         return AVERROR(ENOMEM);
      88           1 :     return ff_set_common_formats(ctx, fmts_list);
      89             : }
      90             : 
      91        5086 : static int clip_line(int *sx, int *sy, int *ex, int *ey, int maxx)
      92             : {
      93        5086 :     if(*sx > *ex)
      94        1250 :         return clip_line(ex, ey, sx, sy, maxx);
      95             : 
      96        3836 :     if (*sx < 0) {
      97           0 :         if (*ex < 0)
      98           0 :             return 1;
      99           0 :         *sy = *ey + (*sy - *ey) * (int64_t)*ex / (*ex - *sx);
     100           0 :         *sx = 0;
     101             :     }
     102             : 
     103        3836 :     if (*ex > maxx) {
     104           0 :         if (*sx > maxx)
     105           0 :             return 1;
     106           0 :         *ey = *sy + (*ey - *sy) * (int64_t)(maxx - *sx) / (*ex - *sx);
     107           0 :         *ex = maxx;
     108             :     }
     109        3836 :     return 0;
     110             : }
     111             : 
     112             : /**
     113             :  * Draw a line from (ex, ey) -> (sx, sy).
     114             :  * @param w width of the image
     115             :  * @param h height of the image
     116             :  * @param stride stride/linesize of the image
     117             :  * @param color color of the arrow
     118             :  */
     119        1918 : static void draw_line(uint8_t *buf, int sx, int sy, int ex, int ey,
     120             :                       int w, int h, int stride, int color)
     121             : {
     122             :     int x, y, fr, f;
     123             : 
     124        1918 :     if (clip_line(&sx, &sy, &ex, &ey, w - 1))
     125           0 :         return;
     126        1918 :     if (clip_line(&sy, &sx, &ey, &ex, h - 1))
     127           0 :         return;
     128             : 
     129        1918 :     sx = av_clip(sx, 0, w - 1);
     130        1918 :     sy = av_clip(sy, 0, h - 1);
     131        1918 :     ex = av_clip(ex, 0, w - 1);
     132        1918 :     ey = av_clip(ey, 0, h - 1);
     133             : 
     134        1918 :     buf[sy * stride + sx] += color;
     135             : 
     136        1918 :     if (FFABS(ex - sx) > FFABS(ey - sy)) {
     137         602 :         if (sx > ex) {
     138         310 :             FFSWAP(int, sx, ex);
     139         310 :             FFSWAP(int, sy, ey);
     140             :         }
     141         602 :         buf += sx + sy * stride;
     142         602 :         ex  -= sx;
     143         602 :         f    = ((ey - sy) << 16) / ex;
     144       13637 :         for (x = 0; x <= ex; x++) {
     145       13035 :             y  = (x * f) >> 16;
     146       13035 :             fr = (x * f) & 0xFFFF;
     147       13035 :                    buf[ y      * stride + x] += (color * (0x10000 - fr)) >> 16;
     148       13035 :             if(fr) buf[(y + 1) * stride + x] += (color *            fr ) >> 16;
     149             :         }
     150             :     } else {
     151        1316 :         if (sy > ey) {
     152         359 :             FFSWAP(int, sx, ex);
     153         359 :             FFSWAP(int, sy, ey);
     154             :         }
     155        1316 :         buf += sx + sy * stride;
     156        1316 :         ey  -= sy;
     157        1316 :         if (ey)
     158         804 :             f = ((ex - sx) << 16) / ey;
     159             :         else
     160         512 :             f = 0;
     161        4564 :         for(y= 0; y <= ey; y++){
     162        3248 :             x  = (y*f) >> 16;
     163        3248 :             fr = (y*f) & 0xFFFF;
     164        3248 :                    buf[y * stride + x    ] += (color * (0x10000 - fr)) >> 16;
     165        3248 :             if(fr) buf[y * stride + x + 1] += (color *            fr ) >> 16;
     166             :         }
     167             :     }
     168             : }
     169             : 
     170             : /**
     171             :  * Draw an arrow from (ex, ey) -> (sx, sy).
     172             :  * @param w width of the image
     173             :  * @param h height of the image
     174             :  * @param stride stride/linesize of the image
     175             :  * @param color color of the arrow
     176             :  */
     177        1014 : static void draw_arrow(uint8_t *buf, int sx, int sy, int ex,
     178             :                        int ey, int w, int h, int stride, int color, int tail, int direction)
     179             : {
     180             :     int dx,dy;
     181             : 
     182        1014 :     if (direction) {
     183         102 :         FFSWAP(int, sx, ex);
     184         102 :         FFSWAP(int, sy, ey);
     185             :     }
     186             : 
     187        1014 :     sx = av_clip(sx, -100, w + 100);
     188        1014 :     sy = av_clip(sy, -100, h + 100);
     189        1014 :     ex = av_clip(ex, -100, w + 100);
     190        1014 :     ey = av_clip(ey, -100, h + 100);
     191             : 
     192        1014 :     dx = ex - sx;
     193        1014 :     dy = ey - sy;
     194             : 
     195        1014 :     if (dx * dx + dy * dy > 3 * 3) {
     196         452 :         int rx =  dx + dy;
     197         452 :         int ry = -dx + dy;
     198         452 :         int length = sqrt((rx * rx + ry * ry) << 8);
     199             : 
     200             :         // FIXME subpixel accuracy
     201         452 :         rx = ROUNDED_DIV(rx * 3 << 4, length);
     202         452 :         ry = ROUNDED_DIV(ry * 3 << 4, length);
     203             : 
     204         452 :         if (tail) {
     205           0 :             rx = -rx;
     206           0 :             ry = -ry;
     207             :         }
     208             : 
     209         452 :         draw_line(buf, sx, sy, sx + rx, sy + ry, w, h, stride, color);
     210         452 :         draw_line(buf, sx, sy, sx - ry, sy + rx, w, h, stride, color);
     211             :     }
     212        1014 :     draw_line(buf, sx, sy, ex, ey, w, h, stride, color);
     213        1014 : }
     214             : 
     215          61 : static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
     216             : {
     217          61 :     AVFilterContext *ctx = inlink->dst;
     218          61 :     CodecViewContext *s = ctx->priv;
     219          61 :     AVFilterLink *outlink = ctx->outputs[0];
     220             : 
     221          61 :     if (s->qp) {
     222             :         int qstride, qp_type;
     223           0 :         int8_t *qp_table = av_frame_get_qp_table(frame, &qstride, &qp_type);
     224             : 
     225           0 :         if (qp_table) {
     226             :             int x, y;
     227           0 :             const int w = AV_CEIL_RSHIFT(frame->width,  s->hsub);
     228           0 :             const int h = AV_CEIL_RSHIFT(frame->height, s->vsub);
     229           0 :             uint8_t *pu = frame->data[1];
     230           0 :             uint8_t *pv = frame->data[2];
     231           0 :             const int lzu = frame->linesize[1];
     232           0 :             const int lzv = frame->linesize[2];
     233             : 
     234           0 :             for (y = 0; y < h; y++) {
     235           0 :                 for (x = 0; x < w; x++) {
     236           0 :                     const int qp = ff_norm_qscale(qp_table[(y >> 3) * qstride + (x >> 3)], qp_type) * 128/31;
     237           0 :                     pu[x] = pv[x] = qp;
     238             :                 }
     239           0 :                 pu += lzu;
     240           0 :                 pv += lzv;
     241             :             }
     242             :         }
     243             :     }
     244             : 
     245          61 :     if (s->mv || s->mv_type) {
     246          61 :         AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_MOTION_VECTORS);
     247          61 :         if (sd) {
     248             :             int i;
     249          27 :             const AVMotionVector *mvs = (const AVMotionVector *)sd->data;
     250          27 :             const int is_iframe = (s->frame_type & FRAME_TYPE_I) && frame->pict_type == AV_PICTURE_TYPE_I;
     251          27 :             const int is_pframe = (s->frame_type & FRAME_TYPE_P) && frame->pict_type == AV_PICTURE_TYPE_P;
     252          27 :             const int is_bframe = (s->frame_type & FRAME_TYPE_B) && frame->pict_type == AV_PICTURE_TYPE_B;
     253             : 
     254        1041 :             for (i = 0; i < sd->size / sizeof(*mvs); i++) {
     255        1014 :                 const AVMotionVector *mv = &mvs[i];
     256        1014 :                 const int direction = mv->source > 0;
     257             : 
     258        1014 :                 if (s->mv_type) {
     259           0 :                     const int is_fp = direction == 0 && (s->mv_type & MV_TYPE_FOR);
     260           0 :                     const int is_bp = direction == 1 && (s->mv_type & MV_TYPE_BACK);
     261             : 
     262           0 :                     if ((!s->frame_type && (is_fp || is_bp)) ||
     263           0 :                         is_iframe && is_fp || is_iframe && is_bp ||
     264           0 :                         is_pframe && is_fp ||
     265           0 :                         is_bframe && is_fp || is_bframe && is_bp)
     266           0 :                         draw_arrow(frame->data[0], mv->dst_x, mv->dst_y, mv->src_x, mv->src_y,
     267             :                                    frame->width, frame->height, frame->linesize[0],
     268             :                                    100, 0, direction);
     269        1014 :                 } else if (s->mv)
     270        1014 :                     if ((direction == 0 && (s->mv & MV_P_FOR)  && frame->pict_type == AV_PICTURE_TYPE_P) ||
     271          99 :                         (direction == 0 && (s->mv & MV_B_FOR)  && frame->pict_type == AV_PICTURE_TYPE_B) ||
     272         102 :                         (direction == 1 && (s->mv & MV_B_BACK) && frame->pict_type == AV_PICTURE_TYPE_B))
     273        1014 :                         draw_arrow(frame->data[0], mv->dst_x, mv->dst_y, mv->src_x, mv->src_y,
     274             :                                    frame->width, frame->height, frame->linesize[0],
     275             :                                    100, 0, direction);
     276             :             }
     277             :         }
     278             :     }
     279             : 
     280          61 :     return ff_filter_frame(outlink, frame);
     281             : }
     282             : 
     283           1 : static int config_input(AVFilterLink *inlink)
     284             : {
     285           1 :     AVFilterContext *ctx = inlink->dst;
     286           1 :     CodecViewContext *s = ctx->priv;
     287           1 :     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
     288             : 
     289           1 :     s->hsub = desc->log2_chroma_w;
     290           1 :     s->vsub = desc->log2_chroma_h;
     291           1 :     return 0;
     292             : }
     293             : 
     294             : static const AVFilterPad codecview_inputs[] = {
     295             :     {
     296             :         .name           = "default",
     297             :         .type           = AVMEDIA_TYPE_VIDEO,
     298             :         .filter_frame   = filter_frame,
     299             :         .config_props   = config_input,
     300             :         .needs_writable = 1,
     301             :     },
     302             :     { NULL }
     303             : };
     304             : 
     305             : static const AVFilterPad codecview_outputs[] = {
     306             :     {
     307             :         .name = "default",
     308             :         .type = AVMEDIA_TYPE_VIDEO,
     309             :     },
     310             :     { NULL }
     311             : };
     312             : 
     313             : AVFilter ff_vf_codecview = {
     314             :     .name          = "codecview",
     315             :     .description   = NULL_IF_CONFIG_SMALL("Visualize information about some codecs."),
     316             :     .priv_size     = sizeof(CodecViewContext),
     317             :     .query_formats = query_formats,
     318             :     .inputs        = codecview_inputs,
     319             :     .outputs       = codecview_outputs,
     320             :     .priv_class    = &codecview_class,
     321             :     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
     322             : };

Generated by: LCOV version 1.13