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.

1027 lines
34KB

  1. /*
  2. * Copyright (c) 2010 Mark Heath mjpeg0 @ silicontrip dot org
  3. * Copyright (c) 2014 Clément Bœsch
  4. * Copyright (c) 2014 Dave Rice @dericed
  5. *
  6. * This file is part of FFmpeg.
  7. *
  8. * FFmpeg is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * FFmpeg is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with FFmpeg; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. */
  22. #include "libavutil/intreadwrite.h"
  23. #include "libavutil/opt.h"
  24. #include "libavutil/pixdesc.h"
  25. #include "internal.h"
  26. enum FilterMode {
  27. FILTER_NONE = -1,
  28. FILTER_TOUT,
  29. FILTER_VREP,
  30. FILTER_BRNG,
  31. FILT_NUMB
  32. };
  33. typedef struct SignalstatsContext {
  34. const AVClass *class;
  35. int chromah; // height of chroma plane
  36. int chromaw; // width of chroma plane
  37. int hsub; // horizontal subsampling
  38. int vsub; // vertical subsampling
  39. int depth; // pixel depth
  40. int fs; // pixel count per frame
  41. int cfs; // pixel count per frame of chroma planes
  42. int outfilter; // FilterMode
  43. int filters;
  44. AVFrame *frame_prev;
  45. uint8_t rgba_color[4];
  46. int yuv_color[3];
  47. int nb_jobs;
  48. int *jobs_rets;
  49. int maxsize; // history stats array size
  50. int *histy, *histu, *histv, *histsat;
  51. AVFrame *frame_sat;
  52. AVFrame *frame_hue;
  53. } SignalstatsContext;
  54. typedef struct ThreadData {
  55. const AVFrame *in;
  56. AVFrame *out;
  57. } ThreadData;
  58. typedef struct ThreadDataHueSatMetrics {
  59. const AVFrame *src;
  60. AVFrame *dst_sat, *dst_hue;
  61. } ThreadDataHueSatMetrics;
  62. #define OFFSET(x) offsetof(SignalstatsContext, x)
  63. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  64. static const AVOption signalstats_options[] = {
  65. {"stat", "set statistics filters", OFFSET(filters), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, INT_MAX, FLAGS, "filters"},
  66. {"tout", "analyze pixels for temporal outliers", 0, AV_OPT_TYPE_CONST, {.i64=1<<FILTER_TOUT}, 0, 0, FLAGS, "filters"},
  67. {"vrep", "analyze video lines for vertical line repetition", 0, AV_OPT_TYPE_CONST, {.i64=1<<FILTER_VREP}, 0, 0, FLAGS, "filters"},
  68. {"brng", "analyze for pixels outside of broadcast range", 0, AV_OPT_TYPE_CONST, {.i64=1<<FILTER_BRNG}, 0, 0, FLAGS, "filters"},
  69. {"out", "set video filter", OFFSET(outfilter), AV_OPT_TYPE_INT, {.i64=FILTER_NONE}, -1, FILT_NUMB-1, FLAGS, "out"},
  70. {"tout", "highlight pixels that depict temporal outliers", 0, AV_OPT_TYPE_CONST, {.i64=FILTER_TOUT}, 0, 0, FLAGS, "out"},
  71. {"vrep", "highlight video lines that depict vertical line repetition", 0, AV_OPT_TYPE_CONST, {.i64=FILTER_VREP}, 0, 0, FLAGS, "out"},
  72. {"brng", "highlight pixels that are outside of broadcast range", 0, AV_OPT_TYPE_CONST, {.i64=FILTER_BRNG}, 0, 0, FLAGS, "out"},
  73. {"c", "set highlight color", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str="yellow"}, .flags=FLAGS},
  74. {"color", "set highlight color", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str="yellow"}, .flags=FLAGS},
  75. {NULL}
  76. };
  77. AVFILTER_DEFINE_CLASS(signalstats);
  78. static av_cold int init(AVFilterContext *ctx)
  79. {
  80. uint8_t r, g, b;
  81. SignalstatsContext *s = ctx->priv;
  82. if (s->outfilter != FILTER_NONE)
  83. s->filters |= 1 << s->outfilter;
  84. r = s->rgba_color[0];
  85. g = s->rgba_color[1];
  86. b = s->rgba_color[2];
  87. s->yuv_color[0] = (( 66*r + 129*g + 25*b + (1<<7)) >> 8) + 16;
  88. s->yuv_color[1] = ((-38*r + -74*g + 112*b + (1<<7)) >> 8) + 128;
  89. s->yuv_color[2] = ((112*r + -94*g + -18*b + (1<<7)) >> 8) + 128;
  90. return 0;
  91. }
  92. static av_cold void uninit(AVFilterContext *ctx)
  93. {
  94. SignalstatsContext *s = ctx->priv;
  95. av_frame_free(&s->frame_prev);
  96. av_frame_free(&s->frame_sat);
  97. av_frame_free(&s->frame_hue);
  98. av_freep(&s->jobs_rets);
  99. av_freep(&s->histy);
  100. av_freep(&s->histu);
  101. av_freep(&s->histv);
  102. av_freep(&s->histsat);
  103. }
  104. static int query_formats(AVFilterContext *ctx)
  105. {
  106. // TODO: add more
  107. static const enum AVPixelFormat pix_fmts[] = {
  108. AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P,
  109. AV_PIX_FMT_YUV440P,
  110. AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P,
  111. AV_PIX_FMT_YUVJ440P,
  112. AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV420P9,
  113. AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10,
  114. AV_PIX_FMT_YUV440P10,
  115. AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12,
  116. AV_PIX_FMT_YUV440P12,
  117. AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,
  118. AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV420P16,
  119. AV_PIX_FMT_NONE
  120. };
  121. AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
  122. if (!fmts_list)
  123. return AVERROR(ENOMEM);
  124. return ff_set_common_formats(ctx, fmts_list);
  125. }
  126. static AVFrame *alloc_frame(enum AVPixelFormat pixfmt, int w, int h)
  127. {
  128. AVFrame *frame = av_frame_alloc();
  129. if (!frame)
  130. return NULL;
  131. frame->format = pixfmt;
  132. frame->width = w;
  133. frame->height = h;
  134. if (av_frame_get_buffer(frame, 0) < 0) {
  135. av_frame_free(&frame);
  136. return NULL;
  137. }
  138. return frame;
  139. }
  140. static int config_output(AVFilterLink *outlink)
  141. {
  142. AVFilterContext *ctx = outlink->src;
  143. SignalstatsContext *s = ctx->priv;
  144. AVFilterLink *inlink = outlink->src->inputs[0];
  145. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
  146. s->hsub = desc->log2_chroma_w;
  147. s->vsub = desc->log2_chroma_h;
  148. s->depth = desc->comp[0].depth;
  149. s->maxsize = 1 << s->depth;
  150. s->histy = av_malloc_array(s->maxsize, sizeof(*s->histy));
  151. s->histu = av_malloc_array(s->maxsize, sizeof(*s->histu));
  152. s->histv = av_malloc_array(s->maxsize, sizeof(*s->histv));
  153. s->histsat = av_malloc_array(s->maxsize, sizeof(*s->histsat));
  154. if (!s->histy || !s->histu || !s->histv || !s->histsat)
  155. return AVERROR(ENOMEM);
  156. outlink->w = inlink->w;
  157. outlink->h = inlink->h;
  158. s->chromaw = AV_CEIL_RSHIFT(inlink->w, s->hsub);
  159. s->chromah = AV_CEIL_RSHIFT(inlink->h, s->vsub);
  160. s->fs = inlink->w * inlink->h;
  161. s->cfs = s->chromaw * s->chromah;
  162. s->nb_jobs = FFMAX(1, FFMIN(inlink->h, ff_filter_get_nb_threads(ctx)));
  163. s->jobs_rets = av_malloc_array(s->nb_jobs, sizeof(*s->jobs_rets));
  164. if (!s->jobs_rets)
  165. return AVERROR(ENOMEM);
  166. s->frame_sat = alloc_frame(s->depth > 8 ? AV_PIX_FMT_GRAY16 : AV_PIX_FMT_GRAY8, inlink->w, inlink->h);
  167. s->frame_hue = alloc_frame(AV_PIX_FMT_GRAY16, inlink->w, inlink->h);
  168. if (!s->frame_sat || !s->frame_hue)
  169. return AVERROR(ENOMEM);
  170. return 0;
  171. }
  172. static void burn_frame8(const SignalstatsContext *s, AVFrame *f, int x, int y)
  173. {
  174. const int chromax = x >> s->hsub;
  175. const int chromay = y >> s->vsub;
  176. f->data[0][y * f->linesize[0] + x] = s->yuv_color[0];
  177. f->data[1][chromay * f->linesize[1] + chromax] = s->yuv_color[1];
  178. f->data[2][chromay * f->linesize[2] + chromax] = s->yuv_color[2];
  179. }
  180. static void burn_frame16(const SignalstatsContext *s, AVFrame *f, int x, int y)
  181. {
  182. const int chromax = x >> s->hsub;
  183. const int chromay = y >> s->vsub;
  184. const int mult = 1 << (s->depth - 8);
  185. AV_WN16(f->data[0] + y * f->linesize[0] + x * 2, s->yuv_color[0] * mult);
  186. AV_WN16(f->data[1] + chromay * f->linesize[1] + chromax * 2, s->yuv_color[1] * mult);
  187. AV_WN16(f->data[2] + chromay * f->linesize[2] + chromax * 2, s->yuv_color[2] * mult);
  188. }
  189. static int filter8_brng(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  190. {
  191. ThreadData *td = arg;
  192. const SignalstatsContext *s = ctx->priv;
  193. const AVFrame *in = td->in;
  194. AVFrame *out = td->out;
  195. const int w = in->width;
  196. const int h = in->height;
  197. const int slice_start = (h * jobnr ) / nb_jobs;
  198. const int slice_end = (h * (jobnr+1)) / nb_jobs;
  199. int x, y, score = 0;
  200. for (y = slice_start; y < slice_end; y++) {
  201. const int yc = y >> s->vsub;
  202. const uint8_t *pluma = &in->data[0][y * in->linesize[0]];
  203. const uint8_t *pchromau = &in->data[1][yc * in->linesize[1]];
  204. const uint8_t *pchromav = &in->data[2][yc * in->linesize[2]];
  205. for (x = 0; x < w; x++) {
  206. const int xc = x >> s->hsub;
  207. const int luma = pluma[x];
  208. const int chromau = pchromau[xc];
  209. const int chromav = pchromav[xc];
  210. const int filt = luma < 16 || luma > 235 ||
  211. chromau < 16 || chromau > 240 ||
  212. chromav < 16 || chromav > 240;
  213. score += filt;
  214. if (out && filt)
  215. burn_frame8(s, out, x, y);
  216. }
  217. }
  218. return score;
  219. }
  220. static int filter16_brng(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  221. {
  222. ThreadData *td = arg;
  223. const SignalstatsContext *s = ctx->priv;
  224. const AVFrame *in = td->in;
  225. AVFrame *out = td->out;
  226. const int mult = 1 << (s->depth - 8);
  227. const int w = in->width;
  228. const int h = in->height;
  229. const int slice_start = (h * jobnr ) / nb_jobs;
  230. const int slice_end = (h * (jobnr+1)) / nb_jobs;
  231. int x, y, score = 0;
  232. for (y = slice_start; y < slice_end; y++) {
  233. const int yc = y >> s->vsub;
  234. const uint16_t *pluma = (uint16_t *)&in->data[0][y * in->linesize[0]];
  235. const uint16_t *pchromau = (uint16_t *)&in->data[1][yc * in->linesize[1]];
  236. const uint16_t *pchromav = (uint16_t *)&in->data[2][yc * in->linesize[2]];
  237. for (x = 0; x < w; x++) {
  238. const int xc = x >> s->hsub;
  239. const int luma = pluma[x];
  240. const int chromau = pchromau[xc];
  241. const int chromav = pchromav[xc];
  242. const int filt = luma < 16 * mult || luma > 235 * mult ||
  243. chromau < 16 * mult || chromau > 240 * mult ||
  244. chromav < 16 * mult || chromav > 240 * mult;
  245. score += filt;
  246. if (out && filt)
  247. burn_frame16(s, out, x, y);
  248. }
  249. }
  250. return score;
  251. }
  252. static int filter_tout_outlier(uint8_t x, uint8_t y, uint8_t z)
  253. {
  254. return ((abs(x - y) + abs (z - y)) / 2) - abs(z - x) > 4; // make 4 configurable?
  255. }
  256. static int filter8_tout(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  257. {
  258. ThreadData *td = arg;
  259. const SignalstatsContext *s = ctx->priv;
  260. const AVFrame *in = td->in;
  261. AVFrame *out = td->out;
  262. const int w = in->width;
  263. const int h = in->height;
  264. const int slice_start = (h * jobnr ) / nb_jobs;
  265. const int slice_end = (h * (jobnr+1)) / nb_jobs;
  266. const uint8_t *p = in->data[0];
  267. int lw = in->linesize[0];
  268. int x, y, score = 0, filt;
  269. for (y = slice_start; y < slice_end; y++) {
  270. if (y - 1 < 0 || y + 1 >= h)
  271. continue;
  272. // detect two pixels above and below (to eliminate interlace artefacts)
  273. // should check that video format is infact interlaced.
  274. #define FILTER(i, j) \
  275. filter_tout_outlier(p[(y-j) * lw + x + i], \
  276. p[ y * lw + x + i], \
  277. p[(y+j) * lw + x + i])
  278. #define FILTER3(j) (FILTER(-1, j) && FILTER(0, j) && FILTER(1, j))
  279. if (y - 2 >= 0 && y + 2 < h) {
  280. for (x = 1; x < w - 1; x++) {
  281. filt = FILTER3(2) && FILTER3(1);
  282. score += filt;
  283. if (filt && out)
  284. burn_frame8(s, out, x, y);
  285. }
  286. } else {
  287. for (x = 1; x < w - 1; x++) {
  288. filt = FILTER3(1);
  289. score += filt;
  290. if (filt && out)
  291. burn_frame8(s, out, x, y);
  292. }
  293. }
  294. }
  295. return score;
  296. }
  297. static int filter16_tout(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  298. {
  299. ThreadData *td = arg;
  300. const SignalstatsContext *s = ctx->priv;
  301. const AVFrame *in = td->in;
  302. AVFrame *out = td->out;
  303. const int w = in->width;
  304. const int h = in->height;
  305. const int slice_start = (h * jobnr ) / nb_jobs;
  306. const int slice_end = (h * (jobnr+1)) / nb_jobs;
  307. const uint16_t *p = (uint16_t *)in->data[0];
  308. int lw = in->linesize[0] / 2;
  309. int x, y, score = 0, filt;
  310. for (y = slice_start; y < slice_end; y++) {
  311. if (y - 1 < 0 || y + 1 >= h)
  312. continue;
  313. // detect two pixels above and below (to eliminate interlace artefacts)
  314. // should check that video format is infact interlaced.
  315. if (y - 2 >= 0 && y + 2 < h) {
  316. for (x = 1; x < w - 1; x++) {
  317. filt = FILTER3(2) && FILTER3(1);
  318. score += filt;
  319. if (filt && out)
  320. burn_frame16(s, out, x, y);
  321. }
  322. } else {
  323. for (x = 1; x < w - 1; x++) {
  324. filt = FILTER3(1);
  325. score += filt;
  326. if (filt && out)
  327. burn_frame16(s, out, x, y);
  328. }
  329. }
  330. }
  331. return score;
  332. }
  333. #define VREP_START 4
  334. static int filter8_vrep(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  335. {
  336. ThreadData *td = arg;
  337. const SignalstatsContext *s = ctx->priv;
  338. const AVFrame *in = td->in;
  339. AVFrame *out = td->out;
  340. const int w = in->width;
  341. const int h = in->height;
  342. const int slice_start = (h * jobnr ) / nb_jobs;
  343. const int slice_end = (h * (jobnr+1)) / nb_jobs;
  344. const uint8_t *p = in->data[0];
  345. const int lw = in->linesize[0];
  346. int x, y, score = 0;
  347. for (y = slice_start; y < slice_end; y++) {
  348. const int y2lw = (y - VREP_START) * lw;
  349. const int ylw = y * lw;
  350. int filt, totdiff = 0;
  351. if (y < VREP_START)
  352. continue;
  353. for (x = 0; x < w; x++)
  354. totdiff += abs(p[y2lw + x] - p[ylw + x]);
  355. filt = totdiff < w;
  356. score += filt;
  357. if (filt && out)
  358. for (x = 0; x < w; x++)
  359. burn_frame8(s, out, x, y);
  360. }
  361. return score * w;
  362. }
  363. static int filter16_vrep(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  364. {
  365. ThreadData *td = arg;
  366. const SignalstatsContext *s = ctx->priv;
  367. const AVFrame *in = td->in;
  368. AVFrame *out = td->out;
  369. const int w = in->width;
  370. const int h = in->height;
  371. const int slice_start = (h * jobnr ) / nb_jobs;
  372. const int slice_end = (h * (jobnr+1)) / nb_jobs;
  373. const uint16_t *p = (uint16_t *)in->data[0];
  374. const int lw = in->linesize[0] / 2;
  375. int x, y, score = 0;
  376. for (y = slice_start; y < slice_end; y++) {
  377. const int y2lw = (y - VREP_START) * lw;
  378. const int ylw = y * lw;
  379. int64_t totdiff = 0;
  380. int filt;
  381. if (y < VREP_START)
  382. continue;
  383. for (x = 0; x < w; x++)
  384. totdiff += abs(p[y2lw + x] - p[ylw + x]);
  385. filt = totdiff < w;
  386. score += filt;
  387. if (filt && out)
  388. for (x = 0; x < w; x++)
  389. burn_frame16(s, out, x, y);
  390. }
  391. return score * w;
  392. }
  393. static const struct {
  394. const char *name;
  395. int (*process8)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
  396. int (*process16)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
  397. } filters_def[] = {
  398. {"TOUT", filter8_tout, filter16_tout},
  399. {"VREP", filter8_vrep, filter16_vrep},
  400. {"BRNG", filter8_brng, filter16_brng},
  401. {NULL}
  402. };
  403. static int compute_sat_hue_metrics8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  404. {
  405. int i, j;
  406. ThreadDataHueSatMetrics *td = arg;
  407. const SignalstatsContext *s = ctx->priv;
  408. const AVFrame *src = td->src;
  409. AVFrame *dst_sat = td->dst_sat;
  410. AVFrame *dst_hue = td->dst_hue;
  411. const int slice_start = (s->chromah * jobnr ) / nb_jobs;
  412. const int slice_end = (s->chromah * (jobnr+1)) / nb_jobs;
  413. const int lsz_u = src->linesize[1];
  414. const int lsz_v = src->linesize[2];
  415. const uint8_t *p_u = src->data[1] + slice_start * lsz_u;
  416. const uint8_t *p_v = src->data[2] + slice_start * lsz_v;
  417. const int lsz_sat = dst_sat->linesize[0];
  418. const int lsz_hue = dst_hue->linesize[0];
  419. uint8_t *p_sat = dst_sat->data[0] + slice_start * lsz_sat;
  420. uint8_t *p_hue = dst_hue->data[0] + slice_start * lsz_hue;
  421. for (j = slice_start; j < slice_end; j++) {
  422. for (i = 0; i < s->chromaw; i++) {
  423. const int yuvu = p_u[i];
  424. const int yuvv = p_v[i];
  425. p_sat[i] = hypot(yuvu - 128, yuvv - 128); // int or round?
  426. ((int16_t*)p_hue)[i] = fmod(floor((180 / M_PI) * atan2f(yuvu-128, yuvv-128) + 180), 360.);
  427. }
  428. p_u += lsz_u;
  429. p_v += lsz_v;
  430. p_sat += lsz_sat;
  431. p_hue += lsz_hue;
  432. }
  433. return 0;
  434. }
  435. static int compute_sat_hue_metrics16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  436. {
  437. int i, j;
  438. ThreadDataHueSatMetrics *td = arg;
  439. const SignalstatsContext *s = ctx->priv;
  440. const AVFrame *src = td->src;
  441. AVFrame *dst_sat = td->dst_sat;
  442. AVFrame *dst_hue = td->dst_hue;
  443. const int mid = 1 << (s->depth - 1);
  444. const int slice_start = (s->chromah * jobnr ) / nb_jobs;
  445. const int slice_end = (s->chromah * (jobnr+1)) / nb_jobs;
  446. const int lsz_u = src->linesize[1] / 2;
  447. const int lsz_v = src->linesize[2] / 2;
  448. const uint16_t *p_u = (uint16_t*)src->data[1] + slice_start * lsz_u;
  449. const uint16_t *p_v = (uint16_t*)src->data[2] + slice_start * lsz_v;
  450. const int lsz_sat = dst_sat->linesize[0] / 2;
  451. const int lsz_hue = dst_hue->linesize[0] / 2;
  452. uint16_t *p_sat = (uint16_t*)dst_sat->data[0] + slice_start * lsz_sat;
  453. uint16_t *p_hue = (uint16_t*)dst_hue->data[0] + slice_start * lsz_hue;
  454. for (j = slice_start; j < slice_end; j++) {
  455. for (i = 0; i < s->chromaw; i++) {
  456. const int yuvu = p_u[i];
  457. const int yuvv = p_v[i];
  458. p_sat[i] = hypot(yuvu - mid, yuvv - mid); // int or round?
  459. ((int16_t*)p_hue)[i] = fmod(floor((180 / M_PI) * atan2f(yuvu-mid, yuvv-mid) + 180), 360.);
  460. }
  461. p_u += lsz_u;
  462. p_v += lsz_v;
  463. p_sat += lsz_sat;
  464. p_hue += lsz_hue;
  465. }
  466. return 0;
  467. }
  468. static unsigned compute_bit_depth(uint16_t mask)
  469. {
  470. return av_popcount(mask);
  471. }
  472. static int filter_frame8(AVFilterLink *link, AVFrame *in)
  473. {
  474. AVFilterContext *ctx = link->dst;
  475. SignalstatsContext *s = ctx->priv;
  476. AVFilterLink *outlink = ctx->outputs[0];
  477. AVFrame *out = in;
  478. int i, j;
  479. int w = 0, cw = 0, // in
  480. pw = 0, cpw = 0; // prev
  481. int fil;
  482. char metabuf[128];
  483. unsigned int *histy = s->histy,
  484. *histu = s->histu,
  485. *histv = s->histv,
  486. histhue[360] = {0},
  487. *histsat = s->histsat;
  488. int miny = -1, minu = -1, minv = -1;
  489. int maxy = -1, maxu = -1, maxv = -1;
  490. int lowy = -1, lowu = -1, lowv = -1;
  491. int highy = -1, highu = -1, highv = -1;
  492. int minsat = -1, maxsat = -1, lowsat = -1, highsat = -1;
  493. int lowp, highp, clowp, chighp;
  494. int accy, accu, accv;
  495. int accsat, acchue = 0;
  496. int medhue, maxhue;
  497. int toty = 0, totu = 0, totv = 0, totsat=0;
  498. int tothue = 0;
  499. int dify = 0, difu = 0, difv = 0;
  500. uint16_t masky = 0, masku = 0, maskv = 0;
  501. int filtot[FILT_NUMB] = {0};
  502. AVFrame *prev;
  503. AVFrame *sat = s->frame_sat;
  504. AVFrame *hue = s->frame_hue;
  505. const uint8_t *p_sat = sat->data[0];
  506. const uint8_t *p_hue = hue->data[0];
  507. const int lsz_sat = sat->linesize[0];
  508. const int lsz_hue = hue->linesize[0];
  509. ThreadDataHueSatMetrics td_huesat = {
  510. .src = in,
  511. .dst_sat = sat,
  512. .dst_hue = hue,
  513. };
  514. if (!s->frame_prev)
  515. s->frame_prev = av_frame_clone(in);
  516. prev = s->frame_prev;
  517. if (s->outfilter != FILTER_NONE) {
  518. out = av_frame_clone(in);
  519. av_frame_make_writable(out);
  520. }
  521. ctx->internal->execute(ctx, compute_sat_hue_metrics8, &td_huesat,
  522. NULL, FFMIN(s->chromah, ff_filter_get_nb_threads(ctx)));
  523. // Calculate luma histogram and difference with previous frame or field.
  524. memset(s->histy, 0, s->maxsize * sizeof(*s->histy));
  525. for (j = 0; j < link->h; j++) {
  526. for (i = 0; i < link->w; i++) {
  527. const int yuv = in->data[0][w + i];
  528. masky |= yuv;
  529. histy[yuv]++;
  530. dify += abs(yuv - prev->data[0][pw + i]);
  531. }
  532. w += in->linesize[0];
  533. pw += prev->linesize[0];
  534. }
  535. // Calculate chroma histogram and difference with previous frame or field.
  536. memset(s->histu, 0, s->maxsize * sizeof(*s->histu));
  537. memset(s->histv, 0, s->maxsize * sizeof(*s->histv));
  538. memset(s->histsat, 0, s->maxsize * sizeof(*s->histsat));
  539. for (j = 0; j < s->chromah; j++) {
  540. for (i = 0; i < s->chromaw; i++) {
  541. const int yuvu = in->data[1][cw+i];
  542. const int yuvv = in->data[2][cw+i];
  543. masku |= yuvu;
  544. maskv |= yuvv;
  545. histu[yuvu]++;
  546. difu += abs(yuvu - prev->data[1][cpw+i]);
  547. histv[yuvv]++;
  548. difv += abs(yuvv - prev->data[2][cpw+i]);
  549. histsat[p_sat[i]]++;
  550. histhue[((int16_t*)p_hue)[i]]++;
  551. }
  552. cw += in->linesize[1];
  553. cpw += prev->linesize[1];
  554. p_sat += lsz_sat;
  555. p_hue += lsz_hue;
  556. }
  557. for (fil = 0; fil < FILT_NUMB; fil ++) {
  558. if (s->filters & 1<<fil) {
  559. ThreadData td = {
  560. .in = in,
  561. .out = out != in && s->outfilter == fil ? out : NULL,
  562. };
  563. memset(s->jobs_rets, 0, s->nb_jobs * sizeof(*s->jobs_rets));
  564. ctx->internal->execute(ctx, filters_def[fil].process8,
  565. &td, s->jobs_rets, s->nb_jobs);
  566. for (i = 0; i < s->nb_jobs; i++)
  567. filtot[fil] += s->jobs_rets[i];
  568. }
  569. }
  570. // find low / high based on histogram percentile
  571. // these only need to be calculated once.
  572. lowp = lrint(s->fs * 10 / 100.);
  573. highp = lrint(s->fs * 90 / 100.);
  574. clowp = lrint(s->cfs * 10 / 100.);
  575. chighp = lrint(s->cfs * 90 / 100.);
  576. accy = accu = accv = accsat = 0;
  577. for (fil = 0; fil < s->maxsize; fil++) {
  578. if (miny < 0 && histy[fil]) miny = fil;
  579. if (minu < 0 && histu[fil]) minu = fil;
  580. if (minv < 0 && histv[fil]) minv = fil;
  581. if (minsat < 0 && histsat[fil]) minsat = fil;
  582. if (histy[fil]) maxy = fil;
  583. if (histu[fil]) maxu = fil;
  584. if (histv[fil]) maxv = fil;
  585. if (histsat[fil]) maxsat = fil;
  586. toty += histy[fil] * fil;
  587. totu += histu[fil] * fil;
  588. totv += histv[fil] * fil;
  589. totsat += histsat[fil] * fil;
  590. accy += histy[fil];
  591. accu += histu[fil];
  592. accv += histv[fil];
  593. accsat += histsat[fil];
  594. if (lowy == -1 && accy >= lowp) lowy = fil;
  595. if (lowu == -1 && accu >= clowp) lowu = fil;
  596. if (lowv == -1 && accv >= clowp) lowv = fil;
  597. if (lowsat == -1 && accsat >= clowp) lowsat = fil;
  598. if (highy == -1 && accy >= highp) highy = fil;
  599. if (highu == -1 && accu >= chighp) highu = fil;
  600. if (highv == -1 && accv >= chighp) highv = fil;
  601. if (highsat == -1 && accsat >= chighp) highsat = fil;
  602. }
  603. maxhue = histhue[0];
  604. medhue = -1;
  605. for (fil = 0; fil < 360; fil++) {
  606. tothue += histhue[fil] * fil;
  607. acchue += histhue[fil];
  608. if (medhue == -1 && acchue > s->cfs / 2)
  609. medhue = fil;
  610. if (histhue[fil] > maxhue) {
  611. maxhue = histhue[fil];
  612. }
  613. }
  614. av_frame_free(&s->frame_prev);
  615. s->frame_prev = av_frame_clone(in);
  616. #define SET_META(key, fmt, val) do { \
  617. snprintf(metabuf, sizeof(metabuf), fmt, val); \
  618. av_dict_set(&out->metadata, "lavfi.signalstats." key, metabuf, 0); \
  619. } while (0)
  620. SET_META("YMIN", "%d", miny);
  621. SET_META("YLOW", "%d", lowy);
  622. SET_META("YAVG", "%g", 1.0 * toty / s->fs);
  623. SET_META("YHIGH", "%d", highy);
  624. SET_META("YMAX", "%d", maxy);
  625. SET_META("UMIN", "%d", minu);
  626. SET_META("ULOW", "%d", lowu);
  627. SET_META("UAVG", "%g", 1.0 * totu / s->cfs);
  628. SET_META("UHIGH", "%d", highu);
  629. SET_META("UMAX", "%d", maxu);
  630. SET_META("VMIN", "%d", minv);
  631. SET_META("VLOW", "%d", lowv);
  632. SET_META("VAVG", "%g", 1.0 * totv / s->cfs);
  633. SET_META("VHIGH", "%d", highv);
  634. SET_META("VMAX", "%d", maxv);
  635. SET_META("SATMIN", "%d", minsat);
  636. SET_META("SATLOW", "%d", lowsat);
  637. SET_META("SATAVG", "%g", 1.0 * totsat / s->cfs);
  638. SET_META("SATHIGH", "%d", highsat);
  639. SET_META("SATMAX", "%d", maxsat);
  640. SET_META("HUEMED", "%d", medhue);
  641. SET_META("HUEAVG", "%g", 1.0 * tothue / s->cfs);
  642. SET_META("YDIF", "%g", 1.0 * dify / s->fs);
  643. SET_META("UDIF", "%g", 1.0 * difu / s->cfs);
  644. SET_META("VDIF", "%g", 1.0 * difv / s->cfs);
  645. SET_META("YBITDEPTH", "%d", compute_bit_depth(masky));
  646. SET_META("UBITDEPTH", "%d", compute_bit_depth(masku));
  647. SET_META("VBITDEPTH", "%d", compute_bit_depth(maskv));
  648. for (fil = 0; fil < FILT_NUMB; fil ++) {
  649. if (s->filters & 1<<fil) {
  650. char metaname[128];
  651. snprintf(metabuf, sizeof(metabuf), "%g", 1.0 * filtot[fil] / s->fs);
  652. snprintf(metaname, sizeof(metaname), "lavfi.signalstats.%s", filters_def[fil].name);
  653. av_dict_set(&out->metadata, metaname, metabuf, 0);
  654. }
  655. }
  656. if (in != out)
  657. av_frame_free(&in);
  658. return ff_filter_frame(outlink, out);
  659. }
  660. static int filter_frame16(AVFilterLink *link, AVFrame *in)
  661. {
  662. AVFilterContext *ctx = link->dst;
  663. SignalstatsContext *s = ctx->priv;
  664. AVFilterLink *outlink = ctx->outputs[0];
  665. AVFrame *out = in;
  666. int i, j;
  667. int w = 0, cw = 0, // in
  668. pw = 0, cpw = 0; // prev
  669. int fil;
  670. char metabuf[128];
  671. unsigned int *histy = s->histy,
  672. *histu = s->histu,
  673. *histv = s->histv,
  674. histhue[360] = {0},
  675. *histsat = s->histsat;
  676. int miny = -1, minu = -1, minv = -1;
  677. int maxy = -1, maxu = -1, maxv = -1;
  678. int lowy = -1, lowu = -1, lowv = -1;
  679. int highy = -1, highu = -1, highv = -1;
  680. int minsat = -1, maxsat = -1, lowsat = -1, highsat = -1;
  681. int lowp, highp, clowp, chighp;
  682. int accy, accu, accv;
  683. int accsat, acchue = 0;
  684. int medhue, maxhue;
  685. int64_t toty = 0, totu = 0, totv = 0, totsat=0;
  686. int64_t tothue = 0;
  687. int64_t dify = 0, difu = 0, difv = 0;
  688. uint16_t masky = 0, masku = 0, maskv = 0;
  689. int filtot[FILT_NUMB] = {0};
  690. AVFrame *prev;
  691. AVFrame *sat = s->frame_sat;
  692. AVFrame *hue = s->frame_hue;
  693. const uint16_t *p_sat = (uint16_t *)sat->data[0];
  694. const uint16_t *p_hue = (uint16_t *)hue->data[0];
  695. const int lsz_sat = sat->linesize[0] / 2;
  696. const int lsz_hue = hue->linesize[0] / 2;
  697. ThreadDataHueSatMetrics td_huesat = {
  698. .src = in,
  699. .dst_sat = sat,
  700. .dst_hue = hue,
  701. };
  702. if (!s->frame_prev)
  703. s->frame_prev = av_frame_clone(in);
  704. prev = s->frame_prev;
  705. if (s->outfilter != FILTER_NONE) {
  706. out = av_frame_clone(in);
  707. av_frame_make_writable(out);
  708. }
  709. ctx->internal->execute(ctx, compute_sat_hue_metrics16, &td_huesat,
  710. NULL, FFMIN(s->chromah, ff_filter_get_nb_threads(ctx)));
  711. // Calculate luma histogram and difference with previous frame or field.
  712. memset(s->histy, 0, s->maxsize * sizeof(*s->histy));
  713. for (j = 0; j < link->h; j++) {
  714. for (i = 0; i < link->w; i++) {
  715. const int yuv = AV_RN16(in->data[0] + w + i * 2);
  716. masky |= yuv;
  717. histy[yuv]++;
  718. dify += abs(yuv - (int)AV_RN16(prev->data[0] + pw + i * 2));
  719. }
  720. w += in->linesize[0];
  721. pw += prev->linesize[0];
  722. }
  723. // Calculate chroma histogram and difference with previous frame or field.
  724. memset(s->histu, 0, s->maxsize * sizeof(*s->histu));
  725. memset(s->histv, 0, s->maxsize * sizeof(*s->histv));
  726. memset(s->histsat, 0, s->maxsize * sizeof(*s->histsat));
  727. for (j = 0; j < s->chromah; j++) {
  728. for (i = 0; i < s->chromaw; i++) {
  729. const int yuvu = AV_RN16(in->data[1] + cw + i * 2);
  730. const int yuvv = AV_RN16(in->data[2] + cw + i * 2);
  731. masku |= yuvu;
  732. maskv |= yuvv;
  733. histu[yuvu]++;
  734. difu += abs(yuvu - (int)AV_RN16(prev->data[1] + cpw + i * 2));
  735. histv[yuvv]++;
  736. difv += abs(yuvv - (int)AV_RN16(prev->data[2] + cpw + i * 2));
  737. histsat[p_sat[i]]++;
  738. histhue[((int16_t*)p_hue)[i]]++;
  739. }
  740. cw += in->linesize[1];
  741. cpw += prev->linesize[1];
  742. p_sat += lsz_sat;
  743. p_hue += lsz_hue;
  744. }
  745. for (fil = 0; fil < FILT_NUMB; fil ++) {
  746. if (s->filters & 1<<fil) {
  747. ThreadData td = {
  748. .in = in,
  749. .out = out != in && s->outfilter == fil ? out : NULL,
  750. };
  751. memset(s->jobs_rets, 0, s->nb_jobs * sizeof(*s->jobs_rets));
  752. ctx->internal->execute(ctx, filters_def[fil].process16,
  753. &td, s->jobs_rets, s->nb_jobs);
  754. for (i = 0; i < s->nb_jobs; i++)
  755. filtot[fil] += s->jobs_rets[i];
  756. }
  757. }
  758. // find low / high based on histogram percentile
  759. // these only need to be calculated once.
  760. lowp = lrint(s->fs * 10 / 100.);
  761. highp = lrint(s->fs * 90 / 100.);
  762. clowp = lrint(s->cfs * 10 / 100.);
  763. chighp = lrint(s->cfs * 90 / 100.);
  764. accy = accu = accv = accsat = 0;
  765. for (fil = 0; fil < s->maxsize; fil++) {
  766. if (miny < 0 && histy[fil]) miny = fil;
  767. if (minu < 0 && histu[fil]) minu = fil;
  768. if (minv < 0 && histv[fil]) minv = fil;
  769. if (minsat < 0 && histsat[fil]) minsat = fil;
  770. if (histy[fil]) maxy = fil;
  771. if (histu[fil]) maxu = fil;
  772. if (histv[fil]) maxv = fil;
  773. if (histsat[fil]) maxsat = fil;
  774. toty += histy[fil] * fil;
  775. totu += histu[fil] * fil;
  776. totv += histv[fil] * fil;
  777. totsat += histsat[fil] * fil;
  778. accy += histy[fil];
  779. accu += histu[fil];
  780. accv += histv[fil];
  781. accsat += histsat[fil];
  782. if (lowy == -1 && accy >= lowp) lowy = fil;
  783. if (lowu == -1 && accu >= clowp) lowu = fil;
  784. if (lowv == -1 && accv >= clowp) lowv = fil;
  785. if (lowsat == -1 && accsat >= clowp) lowsat = fil;
  786. if (highy == -1 && accy >= highp) highy = fil;
  787. if (highu == -1 && accu >= chighp) highu = fil;
  788. if (highv == -1 && accv >= chighp) highv = fil;
  789. if (highsat == -1 && accsat >= chighp) highsat = fil;
  790. }
  791. maxhue = histhue[0];
  792. medhue = -1;
  793. for (fil = 0; fil < 360; fil++) {
  794. tothue += histhue[fil] * fil;
  795. acchue += histhue[fil];
  796. if (medhue == -1 && acchue > s->cfs / 2)
  797. medhue = fil;
  798. if (histhue[fil] > maxhue) {
  799. maxhue = histhue[fil];
  800. }
  801. }
  802. av_frame_free(&s->frame_prev);
  803. s->frame_prev = av_frame_clone(in);
  804. SET_META("YMIN", "%d", miny);
  805. SET_META("YLOW", "%d", lowy);
  806. SET_META("YAVG", "%g", 1.0 * toty / s->fs);
  807. SET_META("YHIGH", "%d", highy);
  808. SET_META("YMAX", "%d", maxy);
  809. SET_META("UMIN", "%d", minu);
  810. SET_META("ULOW", "%d", lowu);
  811. SET_META("UAVG", "%g", 1.0 * totu / s->cfs);
  812. SET_META("UHIGH", "%d", highu);
  813. SET_META("UMAX", "%d", maxu);
  814. SET_META("VMIN", "%d", minv);
  815. SET_META("VLOW", "%d", lowv);
  816. SET_META("VAVG", "%g", 1.0 * totv / s->cfs);
  817. SET_META("VHIGH", "%d", highv);
  818. SET_META("VMAX", "%d", maxv);
  819. SET_META("SATMIN", "%d", minsat);
  820. SET_META("SATLOW", "%d", lowsat);
  821. SET_META("SATAVG", "%g", 1.0 * totsat / s->cfs);
  822. SET_META("SATHIGH", "%d", highsat);
  823. SET_META("SATMAX", "%d", maxsat);
  824. SET_META("HUEMED", "%d", medhue);
  825. SET_META("HUEAVG", "%g", 1.0 * tothue / s->cfs);
  826. SET_META("YDIF", "%g", 1.0 * dify / s->fs);
  827. SET_META("UDIF", "%g", 1.0 * difu / s->cfs);
  828. SET_META("VDIF", "%g", 1.0 * difv / s->cfs);
  829. SET_META("YBITDEPTH", "%d", compute_bit_depth(masky));
  830. SET_META("UBITDEPTH", "%d", compute_bit_depth(masku));
  831. SET_META("VBITDEPTH", "%d", compute_bit_depth(maskv));
  832. for (fil = 0; fil < FILT_NUMB; fil ++) {
  833. if (s->filters & 1<<fil) {
  834. char metaname[128];
  835. snprintf(metabuf, sizeof(metabuf), "%g", 1.0 * filtot[fil] / s->fs);
  836. snprintf(metaname, sizeof(metaname), "lavfi.signalstats.%s", filters_def[fil].name);
  837. av_dict_set(&out->metadata, metaname, metabuf, 0);
  838. }
  839. }
  840. if (in != out)
  841. av_frame_free(&in);
  842. return ff_filter_frame(outlink, out);
  843. }
  844. static int filter_frame(AVFilterLink *link, AVFrame *in)
  845. {
  846. AVFilterContext *ctx = link->dst;
  847. SignalstatsContext *s = ctx->priv;
  848. if (s->depth > 8)
  849. return filter_frame16(link, in);
  850. else
  851. return filter_frame8(link, in);
  852. }
  853. static const AVFilterPad signalstats_inputs[] = {
  854. {
  855. .name = "default",
  856. .type = AVMEDIA_TYPE_VIDEO,
  857. .filter_frame = filter_frame,
  858. },
  859. { NULL }
  860. };
  861. static const AVFilterPad signalstats_outputs[] = {
  862. {
  863. .name = "default",
  864. .config_props = config_output,
  865. .type = AVMEDIA_TYPE_VIDEO,
  866. },
  867. { NULL }
  868. };
  869. AVFilter ff_vf_signalstats = {
  870. .name = "signalstats",
  871. .description = "Generate statistics from video analysis.",
  872. .init = init,
  873. .uninit = uninit,
  874. .query_formats = query_formats,
  875. .priv_size = sizeof(SignalstatsContext),
  876. .inputs = signalstats_inputs,
  877. .outputs = signalstats_outputs,
  878. .priv_class = &signalstats_class,
  879. .flags = AVFILTER_FLAG_SLICE_THREADS,
  880. };