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.

1025 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 *histy, *histu, *histv, *histsat;
  50. AVFrame *frame_sat;
  51. AVFrame *frame_hue;
  52. } SignalstatsContext;
  53. typedef struct ThreadData {
  54. const AVFrame *in;
  55. AVFrame *out;
  56. } ThreadData;
  57. typedef struct ThreadDataHueSatMetrics {
  58. const AVFrame *src;
  59. AVFrame *dst_sat, *dst_hue;
  60. } ThreadDataHueSatMetrics;
  61. #define OFFSET(x) offsetof(SignalstatsContext, x)
  62. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  63. static const AVOption signalstats_options[] = {
  64. {"stat", "set statistics filters", OFFSET(filters), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, INT_MAX, FLAGS, "filters"},
  65. {"tout", "analyze pixels for temporal outliers", 0, AV_OPT_TYPE_CONST, {.i64=1<<FILTER_TOUT}, 0, 0, FLAGS, "filters"},
  66. {"vrep", "analyze video lines for vertical line repetition", 0, AV_OPT_TYPE_CONST, {.i64=1<<FILTER_VREP}, 0, 0, FLAGS, "filters"},
  67. {"brng", "analyze for pixels outside of broadcast range", 0, AV_OPT_TYPE_CONST, {.i64=1<<FILTER_BRNG}, 0, 0, FLAGS, "filters"},
  68. {"out", "set video filter", OFFSET(outfilter), AV_OPT_TYPE_INT, {.i64=FILTER_NONE}, -1, FILT_NUMB-1, FLAGS, "out"},
  69. {"tout", "highlight pixels that depict temporal outliers", 0, AV_OPT_TYPE_CONST, {.i64=FILTER_TOUT}, 0, 0, FLAGS, "out"},
  70. {"vrep", "highlight video lines that depict vertical line repetition", 0, AV_OPT_TYPE_CONST, {.i64=FILTER_VREP}, 0, 0, FLAGS, "out"},
  71. {"brng", "highlight pixels that are outside of broadcast range", 0, AV_OPT_TYPE_CONST, {.i64=FILTER_BRNG}, 0, 0, FLAGS, "out"},
  72. {"c", "set highlight color", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str="yellow"}, .flags=FLAGS},
  73. {"color", "set highlight color", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str="yellow"}, .flags=FLAGS},
  74. {NULL}
  75. };
  76. AVFILTER_DEFINE_CLASS(signalstats);
  77. static av_cold int init(AVFilterContext *ctx)
  78. {
  79. uint8_t r, g, b;
  80. SignalstatsContext *s = ctx->priv;
  81. if (s->outfilter != FILTER_NONE)
  82. s->filters |= 1 << s->outfilter;
  83. r = s->rgba_color[0];
  84. g = s->rgba_color[1];
  85. b = s->rgba_color[2];
  86. s->yuv_color[0] = (( 66*r + 129*g + 25*b + (1<<7)) >> 8) + 16;
  87. s->yuv_color[1] = ((-38*r + -74*g + 112*b + (1<<7)) >> 8) + 128;
  88. s->yuv_color[2] = ((112*r + -94*g + -18*b + (1<<7)) >> 8) + 128;
  89. return 0;
  90. }
  91. static av_cold void uninit(AVFilterContext *ctx)
  92. {
  93. SignalstatsContext *s = ctx->priv;
  94. av_frame_free(&s->frame_prev);
  95. av_frame_free(&s->frame_sat);
  96. av_frame_free(&s->frame_hue);
  97. av_freep(&s->jobs_rets);
  98. av_freep(&s->histy);
  99. av_freep(&s->histu);
  100. av_freep(&s->histv);
  101. av_freep(&s->histsat);
  102. }
  103. static int query_formats(AVFilterContext *ctx)
  104. {
  105. // TODO: add more
  106. static const enum AVPixelFormat pix_fmts[] = {
  107. AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P,
  108. AV_PIX_FMT_YUV440P,
  109. AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P,
  110. AV_PIX_FMT_YUVJ440P,
  111. AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV420P9,
  112. AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10,
  113. AV_PIX_FMT_YUV440P10,
  114. AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12,
  115. AV_PIX_FMT_YUV440P12,
  116. AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,
  117. AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV420P16,
  118. AV_PIX_FMT_NONE
  119. };
  120. AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
  121. if (!fmts_list)
  122. return AVERROR(ENOMEM);
  123. return ff_set_common_formats(ctx, fmts_list);
  124. }
  125. static AVFrame *alloc_frame(enum AVPixelFormat pixfmt, int w, int h)
  126. {
  127. AVFrame *frame = av_frame_alloc();
  128. if (!frame)
  129. return NULL;
  130. frame->format = pixfmt;
  131. frame->width = w;
  132. frame->height = h;
  133. if (av_frame_get_buffer(frame, 32) < 0) {
  134. av_frame_free(&frame);
  135. return NULL;
  136. }
  137. return frame;
  138. }
  139. static int config_props(AVFilterLink *outlink)
  140. {
  141. AVFilterContext *ctx = outlink->src;
  142. SignalstatsContext *s = ctx->priv;
  143. AVFilterLink *inlink = outlink->src->inputs[0];
  144. const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
  145. s->hsub = desc->log2_chroma_w;
  146. s->vsub = desc->log2_chroma_h;
  147. s->depth = desc->comp[0].depth;
  148. if (s->depth > 8) {
  149. s->histy = av_malloc_array(1 << s->depth, sizeof(*s->histy));
  150. s->histu = av_malloc_array(1 << s->depth, sizeof(*s->histu));
  151. s->histv = av_malloc_array(1 << s->depth, sizeof(*s->histv));
  152. s->histsat = av_malloc_array(1 << s->depth, sizeof(*s->histsat));
  153. if (!s->histy || !s->histu || !s->histv || !s->histsat)
  154. return AVERROR(ENOMEM);
  155. }
  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. #define DEPTH 256
  404. static int compute_sat_hue_metrics8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  405. {
  406. int i, j;
  407. ThreadDataHueSatMetrics *td = arg;
  408. const SignalstatsContext *s = ctx->priv;
  409. const AVFrame *src = td->src;
  410. AVFrame *dst_sat = td->dst_sat;
  411. AVFrame *dst_hue = td->dst_hue;
  412. const int slice_start = (s->chromah * jobnr ) / nb_jobs;
  413. const int slice_end = (s->chromah * (jobnr+1)) / nb_jobs;
  414. const int lsz_u = src->linesize[1];
  415. const int lsz_v = src->linesize[2];
  416. const uint8_t *p_u = src->data[1] + slice_start * lsz_u;
  417. const uint8_t *p_v = src->data[2] + slice_start * lsz_v;
  418. const int lsz_sat = dst_sat->linesize[0];
  419. const int lsz_hue = dst_hue->linesize[0];
  420. uint8_t *p_sat = dst_sat->data[0] + slice_start * lsz_sat;
  421. uint8_t *p_hue = dst_hue->data[0] + slice_start * lsz_hue;
  422. for (j = slice_start; j < slice_end; j++) {
  423. for (i = 0; i < s->chromaw; i++) {
  424. const int yuvu = p_u[i];
  425. const int yuvv = p_v[i];
  426. p_sat[i] = hypot(yuvu - 128, yuvv - 128); // int or round?
  427. ((int16_t*)p_hue)[i] = floor((180 / M_PI) * atan2f(yuvu-128, yuvv-128) + 180);
  428. }
  429. p_u += lsz_u;
  430. p_v += lsz_v;
  431. p_sat += lsz_sat;
  432. p_hue += lsz_hue;
  433. }
  434. return 0;
  435. }
  436. static int compute_sat_hue_metrics16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  437. {
  438. int i, j;
  439. ThreadDataHueSatMetrics *td = arg;
  440. const SignalstatsContext *s = ctx->priv;
  441. const AVFrame *src = td->src;
  442. AVFrame *dst_sat = td->dst_sat;
  443. AVFrame *dst_hue = td->dst_hue;
  444. const int mid = 1 << (s->depth - 1);
  445. const int slice_start = (s->chromah * jobnr ) / nb_jobs;
  446. const int slice_end = (s->chromah * (jobnr+1)) / nb_jobs;
  447. const int lsz_u = src->linesize[1] / 2;
  448. const int lsz_v = src->linesize[2] / 2;
  449. const uint16_t *p_u = (uint16_t*)src->data[1] + slice_start * lsz_u;
  450. const uint16_t *p_v = (uint16_t*)src->data[2] + slice_start * lsz_v;
  451. const int lsz_sat = dst_sat->linesize[0] / 2;
  452. const int lsz_hue = dst_hue->linesize[0] / 2;
  453. uint16_t *p_sat = (uint16_t*)dst_sat->data[0] + slice_start * lsz_sat;
  454. uint16_t *p_hue = (uint16_t*)dst_hue->data[0] + slice_start * lsz_hue;
  455. for (j = slice_start; j < slice_end; j++) {
  456. for (i = 0; i < s->chromaw; i++) {
  457. const int yuvu = p_u[i];
  458. const int yuvv = p_v[i];
  459. p_sat[i] = hypot(yuvu - mid, yuvv - mid); // int or round?
  460. ((int16_t*)p_hue)[i] = floor((180 / M_PI) * atan2f(yuvu-mid, yuvv-mid) + 180);
  461. }
  462. p_u += lsz_u;
  463. p_v += lsz_v;
  464. p_sat += lsz_sat;
  465. p_hue += lsz_hue;
  466. }
  467. return 0;
  468. }
  469. static unsigned compute_bit_depth(uint16_t mask)
  470. {
  471. return av_popcount(mask);
  472. }
  473. static int filter_frame8(AVFilterLink *link, AVFrame *in)
  474. {
  475. AVFilterContext *ctx = link->dst;
  476. SignalstatsContext *s = ctx->priv;
  477. AVFilterLink *outlink = ctx->outputs[0];
  478. AVFrame *out = in;
  479. int i, j;
  480. int w = 0, cw = 0, // in
  481. pw = 0, cpw = 0; // prev
  482. int fil;
  483. char metabuf[128];
  484. unsigned int histy[DEPTH] = {0},
  485. histu[DEPTH] = {0},
  486. histv[DEPTH] = {0},
  487. histhue[360] = {0},
  488. histsat[DEPTH] = {0}; // limited to 8 bit data.
  489. int miny = -1, minu = -1, minv = -1;
  490. int maxy = -1, maxu = -1, maxv = -1;
  491. int lowy = -1, lowu = -1, lowv = -1;
  492. int highy = -1, highu = -1, highv = -1;
  493. int minsat = -1, maxsat = -1, lowsat = -1, highsat = -1;
  494. int lowp, highp, clowp, chighp;
  495. int accy, accu, accv;
  496. int accsat, acchue = 0;
  497. int medhue, maxhue;
  498. int toty = 0, totu = 0, totv = 0, totsat=0;
  499. int tothue = 0;
  500. int dify = 0, difu = 0, difv = 0;
  501. uint16_t masky = 0, masku = 0, maskv = 0;
  502. int filtot[FILT_NUMB] = {0};
  503. AVFrame *prev;
  504. AVFrame *sat = s->frame_sat;
  505. AVFrame *hue = s->frame_hue;
  506. const uint8_t *p_sat = sat->data[0];
  507. const uint8_t *p_hue = hue->data[0];
  508. const int lsz_sat = sat->linesize[0];
  509. const int lsz_hue = hue->linesize[0];
  510. ThreadDataHueSatMetrics td_huesat = {
  511. .src = in,
  512. .dst_sat = sat,
  513. .dst_hue = hue,
  514. };
  515. if (!s->frame_prev)
  516. s->frame_prev = av_frame_clone(in);
  517. prev = s->frame_prev;
  518. if (s->outfilter != FILTER_NONE) {
  519. out = av_frame_clone(in);
  520. av_frame_make_writable(out);
  521. }
  522. ctx->internal->execute(ctx, compute_sat_hue_metrics8, &td_huesat,
  523. NULL, FFMIN(s->chromah, ff_filter_get_nb_threads(ctx)));
  524. // Calculate luma histogram and difference with previous frame or field.
  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. for (j = 0; j < s->chromah; j++) {
  537. for (i = 0; i < s->chromaw; i++) {
  538. const int yuvu = in->data[1][cw+i];
  539. const int yuvv = in->data[2][cw+i];
  540. masku |= yuvu;
  541. maskv |= yuvv;
  542. histu[yuvu]++;
  543. difu += abs(yuvu - prev->data[1][cpw+i]);
  544. histv[yuvv]++;
  545. difv += abs(yuvv - prev->data[2][cpw+i]);
  546. histsat[p_sat[i]]++;
  547. histhue[((int16_t*)p_hue)[i]]++;
  548. }
  549. cw += in->linesize[1];
  550. cpw += prev->linesize[1];
  551. p_sat += lsz_sat;
  552. p_hue += lsz_hue;
  553. }
  554. for (fil = 0; fil < FILT_NUMB; fil ++) {
  555. if (s->filters & 1<<fil) {
  556. ThreadData td = {
  557. .in = in,
  558. .out = out != in && s->outfilter == fil ? out : NULL,
  559. };
  560. memset(s->jobs_rets, 0, s->nb_jobs * sizeof(*s->jobs_rets));
  561. ctx->internal->execute(ctx, filters_def[fil].process8,
  562. &td, s->jobs_rets, s->nb_jobs);
  563. for (i = 0; i < s->nb_jobs; i++)
  564. filtot[fil] += s->jobs_rets[i];
  565. }
  566. }
  567. // find low / high based on histogram percentile
  568. // these only need to be calculated once.
  569. lowp = lrint(s->fs * 10 / 100.);
  570. highp = lrint(s->fs * 90 / 100.);
  571. clowp = lrint(s->cfs * 10 / 100.);
  572. chighp = lrint(s->cfs * 90 / 100.);
  573. accy = accu = accv = accsat = 0;
  574. for (fil = 0; fil < DEPTH; fil++) {
  575. if (miny < 0 && histy[fil]) miny = fil;
  576. if (minu < 0 && histu[fil]) minu = fil;
  577. if (minv < 0 && histv[fil]) minv = fil;
  578. if (minsat < 0 && histsat[fil]) minsat = fil;
  579. if (histy[fil]) maxy = fil;
  580. if (histu[fil]) maxu = fil;
  581. if (histv[fil]) maxv = fil;
  582. if (histsat[fil]) maxsat = fil;
  583. toty += histy[fil] * fil;
  584. totu += histu[fil] * fil;
  585. totv += histv[fil] * fil;
  586. totsat += histsat[fil] * fil;
  587. accy += histy[fil];
  588. accu += histu[fil];
  589. accv += histv[fil];
  590. accsat += histsat[fil];
  591. if (lowy == -1 && accy >= lowp) lowy = fil;
  592. if (lowu == -1 && accu >= clowp) lowu = fil;
  593. if (lowv == -1 && accv >= clowp) lowv = fil;
  594. if (lowsat == -1 && accsat >= clowp) lowsat = fil;
  595. if (highy == -1 && accy >= highp) highy = fil;
  596. if (highu == -1 && accu >= chighp) highu = fil;
  597. if (highv == -1 && accv >= chighp) highv = fil;
  598. if (highsat == -1 && accsat >= chighp) highsat = fil;
  599. }
  600. maxhue = histhue[0];
  601. medhue = -1;
  602. for (fil = 0; fil < 360; fil++) {
  603. tothue += histhue[fil] * fil;
  604. acchue += histhue[fil];
  605. if (medhue == -1 && acchue > s->cfs / 2)
  606. medhue = fil;
  607. if (histhue[fil] > maxhue) {
  608. maxhue = histhue[fil];
  609. }
  610. }
  611. av_frame_free(&s->frame_prev);
  612. s->frame_prev = av_frame_clone(in);
  613. #define SET_META(key, fmt, val) do { \
  614. snprintf(metabuf, sizeof(metabuf), fmt, val); \
  615. av_dict_set(&out->metadata, "lavfi.signalstats." key, metabuf, 0); \
  616. } while (0)
  617. SET_META("YMIN", "%d", miny);
  618. SET_META("YLOW", "%d", lowy);
  619. SET_META("YAVG", "%g", 1.0 * toty / s->fs);
  620. SET_META("YHIGH", "%d", highy);
  621. SET_META("YMAX", "%d", maxy);
  622. SET_META("UMIN", "%d", minu);
  623. SET_META("ULOW", "%d", lowu);
  624. SET_META("UAVG", "%g", 1.0 * totu / s->cfs);
  625. SET_META("UHIGH", "%d", highu);
  626. SET_META("UMAX", "%d", maxu);
  627. SET_META("VMIN", "%d", minv);
  628. SET_META("VLOW", "%d", lowv);
  629. SET_META("VAVG", "%g", 1.0 * totv / s->cfs);
  630. SET_META("VHIGH", "%d", highv);
  631. SET_META("VMAX", "%d", maxv);
  632. SET_META("SATMIN", "%d", minsat);
  633. SET_META("SATLOW", "%d", lowsat);
  634. SET_META("SATAVG", "%g", 1.0 * totsat / s->cfs);
  635. SET_META("SATHIGH", "%d", highsat);
  636. SET_META("SATMAX", "%d", maxsat);
  637. SET_META("HUEMED", "%d", medhue);
  638. SET_META("HUEAVG", "%g", 1.0 * tothue / s->cfs);
  639. SET_META("YDIF", "%g", 1.0 * dify / s->fs);
  640. SET_META("UDIF", "%g", 1.0 * difu / s->cfs);
  641. SET_META("VDIF", "%g", 1.0 * difv / s->cfs);
  642. SET_META("YBITDEPTH", "%d", compute_bit_depth(masky));
  643. SET_META("UBITDEPTH", "%d", compute_bit_depth(masku));
  644. SET_META("VBITDEPTH", "%d", compute_bit_depth(maskv));
  645. for (fil = 0; fil < FILT_NUMB; fil ++) {
  646. if (s->filters & 1<<fil) {
  647. char metaname[128];
  648. snprintf(metabuf, sizeof(metabuf), "%g", 1.0 * filtot[fil] / s->fs);
  649. snprintf(metaname, sizeof(metaname), "lavfi.signalstats.%s", filters_def[fil].name);
  650. av_dict_set(&out->metadata, metaname, metabuf, 0);
  651. }
  652. }
  653. if (in != out)
  654. av_frame_free(&in);
  655. return ff_filter_frame(outlink, out);
  656. }
  657. static int filter_frame16(AVFilterLink *link, AVFrame *in)
  658. {
  659. AVFilterContext *ctx = link->dst;
  660. SignalstatsContext *s = ctx->priv;
  661. AVFilterLink *outlink = ctx->outputs[0];
  662. AVFrame *out = in;
  663. int i, j;
  664. int w = 0, cw = 0, // in
  665. pw = 0, cpw = 0; // prev
  666. int fil;
  667. char metabuf[128];
  668. unsigned int *histy = s->histy,
  669. *histu = s->histu,
  670. *histv = s->histv,
  671. histhue[360] = {0},
  672. *histsat = s->histsat;
  673. int miny = -1, minu = -1, minv = -1;
  674. int maxy = -1, maxu = -1, maxv = -1;
  675. int lowy = -1, lowu = -1, lowv = -1;
  676. int highy = -1, highu = -1, highv = -1;
  677. int minsat = -1, maxsat = -1, lowsat = -1, highsat = -1;
  678. int lowp, highp, clowp, chighp;
  679. int accy, accu, accv;
  680. int accsat, acchue = 0;
  681. int medhue, maxhue;
  682. int64_t toty = 0, totu = 0, totv = 0, totsat=0;
  683. int64_t tothue = 0;
  684. int64_t dify = 0, difu = 0, difv = 0;
  685. uint16_t masky = 0, masku = 0, maskv = 0;
  686. int filtot[FILT_NUMB] = {0};
  687. AVFrame *prev;
  688. AVFrame *sat = s->frame_sat;
  689. AVFrame *hue = s->frame_hue;
  690. const uint16_t *p_sat = (uint16_t *)sat->data[0];
  691. const uint16_t *p_hue = (uint16_t *)hue->data[0];
  692. const int lsz_sat = sat->linesize[0] / 2;
  693. const int lsz_hue = hue->linesize[0] / 2;
  694. ThreadDataHueSatMetrics td_huesat = {
  695. .src = in,
  696. .dst_sat = sat,
  697. .dst_hue = hue,
  698. };
  699. if (!s->frame_prev)
  700. s->frame_prev = av_frame_clone(in);
  701. prev = s->frame_prev;
  702. if (s->outfilter != FILTER_NONE) {
  703. out = av_frame_clone(in);
  704. av_frame_make_writable(out);
  705. }
  706. ctx->internal->execute(ctx, compute_sat_hue_metrics16, &td_huesat,
  707. NULL, FFMIN(s->chromah, ff_filter_get_nb_threads(ctx)));
  708. // Calculate luma histogram and difference with previous frame or field.
  709. memset(s->histy, 0, (1 << s->depth) * sizeof(*s->histy));
  710. for (j = 0; j < link->h; j++) {
  711. for (i = 0; i < link->w; i++) {
  712. const int yuv = AV_RN16(in->data[0] + w + i * 2);
  713. masky |= yuv;
  714. histy[yuv]++;
  715. dify += abs(yuv - AV_RN16(prev->data[0] + pw + i * 2));
  716. }
  717. w += in->linesize[0];
  718. pw += prev->linesize[0];
  719. }
  720. // Calculate chroma histogram and difference with previous frame or field.
  721. memset(s->histu, 0, (1 << s->depth) * sizeof(*s->histu));
  722. memset(s->histv, 0, (1 << s->depth) * sizeof(*s->histv));
  723. memset(s->histsat, 0, (1 << s->depth) * sizeof(*s->histsat));
  724. for (j = 0; j < s->chromah; j++) {
  725. for (i = 0; i < s->chromaw; i++) {
  726. const int yuvu = AV_RN16(in->data[1] + cw + i * 2);
  727. const int yuvv = AV_RN16(in->data[2] + cw + i * 2);
  728. masku |= yuvu;
  729. maskv |= yuvv;
  730. histu[yuvu]++;
  731. difu += abs(yuvu - AV_RN16(prev->data[1] + cpw + i * 2));
  732. histv[yuvv]++;
  733. difv += abs(yuvv - AV_RN16(prev->data[2] + cpw + i * 2));
  734. histsat[p_sat[i]]++;
  735. histhue[((int16_t*)p_hue)[i]]++;
  736. }
  737. cw += in->linesize[1];
  738. cpw += prev->linesize[1];
  739. p_sat += lsz_sat;
  740. p_hue += lsz_hue;
  741. }
  742. for (fil = 0; fil < FILT_NUMB; fil ++) {
  743. if (s->filters & 1<<fil) {
  744. ThreadData td = {
  745. .in = in,
  746. .out = out != in && s->outfilter == fil ? out : NULL,
  747. };
  748. memset(s->jobs_rets, 0, s->nb_jobs * sizeof(*s->jobs_rets));
  749. ctx->internal->execute(ctx, filters_def[fil].process16,
  750. &td, s->jobs_rets, s->nb_jobs);
  751. for (i = 0; i < s->nb_jobs; i++)
  752. filtot[fil] += s->jobs_rets[i];
  753. }
  754. }
  755. // find low / high based on histogram percentile
  756. // these only need to be calculated once.
  757. lowp = lrint(s->fs * 10 / 100.);
  758. highp = lrint(s->fs * 90 / 100.);
  759. clowp = lrint(s->cfs * 10 / 100.);
  760. chighp = lrint(s->cfs * 90 / 100.);
  761. accy = accu = accv = accsat = 0;
  762. for (fil = 0; fil < 1 << s->depth; fil++) {
  763. if (miny < 0 && histy[fil]) miny = fil;
  764. if (minu < 0 && histu[fil]) minu = fil;
  765. if (minv < 0 && histv[fil]) minv = fil;
  766. if (minsat < 0 && histsat[fil]) minsat = fil;
  767. if (histy[fil]) maxy = fil;
  768. if (histu[fil]) maxu = fil;
  769. if (histv[fil]) maxv = fil;
  770. if (histsat[fil]) maxsat = fil;
  771. toty += histy[fil] * fil;
  772. totu += histu[fil] * fil;
  773. totv += histv[fil] * fil;
  774. totsat += histsat[fil] * fil;
  775. accy += histy[fil];
  776. accu += histu[fil];
  777. accv += histv[fil];
  778. accsat += histsat[fil];
  779. if (lowy == -1 && accy >= lowp) lowy = fil;
  780. if (lowu == -1 && accu >= clowp) lowu = fil;
  781. if (lowv == -1 && accv >= clowp) lowv = fil;
  782. if (lowsat == -1 && accsat >= clowp) lowsat = fil;
  783. if (highy == -1 && accy >= highp) highy = fil;
  784. if (highu == -1 && accu >= chighp) highu = fil;
  785. if (highv == -1 && accv >= chighp) highv = fil;
  786. if (highsat == -1 && accsat >= chighp) highsat = fil;
  787. }
  788. maxhue = histhue[0];
  789. medhue = -1;
  790. for (fil = 0; fil < 360; fil++) {
  791. tothue += histhue[fil] * fil;
  792. acchue += histhue[fil];
  793. if (medhue == -1 && acchue > s->cfs / 2)
  794. medhue = fil;
  795. if (histhue[fil] > maxhue) {
  796. maxhue = histhue[fil];
  797. }
  798. }
  799. av_frame_free(&s->frame_prev);
  800. s->frame_prev = av_frame_clone(in);
  801. SET_META("YMIN", "%d", miny);
  802. SET_META("YLOW", "%d", lowy);
  803. SET_META("YAVG", "%g", 1.0 * toty / s->fs);
  804. SET_META("YHIGH", "%d", highy);
  805. SET_META("YMAX", "%d", maxy);
  806. SET_META("UMIN", "%d", minu);
  807. SET_META("ULOW", "%d", lowu);
  808. SET_META("UAVG", "%g", 1.0 * totu / s->cfs);
  809. SET_META("UHIGH", "%d", highu);
  810. SET_META("UMAX", "%d", maxu);
  811. SET_META("VMIN", "%d", minv);
  812. SET_META("VLOW", "%d", lowv);
  813. SET_META("VAVG", "%g", 1.0 * totv / s->cfs);
  814. SET_META("VHIGH", "%d", highv);
  815. SET_META("VMAX", "%d", maxv);
  816. SET_META("SATMIN", "%d", minsat);
  817. SET_META("SATLOW", "%d", lowsat);
  818. SET_META("SATAVG", "%g", 1.0 * totsat / s->cfs);
  819. SET_META("SATHIGH", "%d", highsat);
  820. SET_META("SATMAX", "%d", maxsat);
  821. SET_META("HUEMED", "%d", medhue);
  822. SET_META("HUEAVG", "%g", 1.0 * tothue / s->cfs);
  823. SET_META("YDIF", "%g", 1.0 * dify / s->fs);
  824. SET_META("UDIF", "%g", 1.0 * difu / s->cfs);
  825. SET_META("VDIF", "%g", 1.0 * difv / s->cfs);
  826. SET_META("YBITDEPTH", "%d", compute_bit_depth(masky));
  827. SET_META("UBITDEPTH", "%d", compute_bit_depth(masku));
  828. SET_META("VBITDEPTH", "%d", compute_bit_depth(maskv));
  829. for (fil = 0; fil < FILT_NUMB; fil ++) {
  830. if (s->filters & 1<<fil) {
  831. char metaname[128];
  832. snprintf(metabuf, sizeof(metabuf), "%g", 1.0 * filtot[fil] / s->fs);
  833. snprintf(metaname, sizeof(metaname), "lavfi.signalstats.%s", filters_def[fil].name);
  834. av_dict_set(&out->metadata, metaname, metabuf, 0);
  835. }
  836. }
  837. if (in != out)
  838. av_frame_free(&in);
  839. return ff_filter_frame(outlink, out);
  840. }
  841. static int filter_frame(AVFilterLink *link, AVFrame *in)
  842. {
  843. AVFilterContext *ctx = link->dst;
  844. SignalstatsContext *s = ctx->priv;
  845. if (s->depth > 8)
  846. return filter_frame16(link, in);
  847. else
  848. return filter_frame8(link, in);
  849. }
  850. static const AVFilterPad signalstats_inputs[] = {
  851. {
  852. .name = "default",
  853. .type = AVMEDIA_TYPE_VIDEO,
  854. .filter_frame = filter_frame,
  855. },
  856. { NULL }
  857. };
  858. static const AVFilterPad signalstats_outputs[] = {
  859. {
  860. .name = "default",
  861. .config_props = config_props,
  862. .type = AVMEDIA_TYPE_VIDEO,
  863. },
  864. { NULL }
  865. };
  866. AVFilter ff_vf_signalstats = {
  867. .name = "signalstats",
  868. .description = "Generate statistics from video analysis.",
  869. .init = init,
  870. .uninit = uninit,
  871. .query_formats = query_formats,
  872. .priv_size = sizeof(SignalstatsContext),
  873. .inputs = signalstats_inputs,
  874. .outputs = signalstats_outputs,
  875. .priv_class = &signalstats_class,
  876. .flags = AVFILTER_FLAG_SLICE_THREADS,
  877. };