You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

331 lines
11KB

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