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.

457 lines
18KB

  1. /*
  2. * Copyright (c) 2020 Paul B Mahol
  3. *
  4. * This file is part of FFmpeg.
  5. *
  6. * FFmpeg is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * FFmpeg is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with FFmpeg; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19. */
  20. #include "libavutil/avassert.h"
  21. #include "libavutil/avstring.h"
  22. #include "libavutil/common.h"
  23. #include "libavutil/internal.h"
  24. #include "libavutil/imgutils.h"
  25. #include "libavutil/lfg.h"
  26. #include "libavutil/opt.h"
  27. #include "libavutil/pixdesc.h"
  28. #include "libavutil/random_seed.h"
  29. #include "avfilter.h"
  30. #include "internal.h"
  31. #include "video.h"
  32. typedef struct ShufflePixelsContext {
  33. const AVClass *class;
  34. int block_w, block_h;
  35. int mode;
  36. int direction;
  37. int64_t seed;
  38. int depth;
  39. int nb_planes;
  40. int linesize[4];
  41. int planewidth[4];
  42. int planeheight[4];
  43. int nb_blocks;
  44. uint8_t *used;
  45. int32_t *map;
  46. AVLFG c;
  47. int (*shuffle_pixels)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
  48. } ShufflePixelsContext;
  49. static int query_formats(AVFilterContext *ctx)
  50. {
  51. static const enum AVPixelFormat pix_fmts[] = {
  52. AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
  53. AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P,
  54. AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10,
  55. AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
  56. AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GBRAP,
  57. AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUVA444P10,
  58. AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUVA444P16,
  59. AV_PIX_FMT_NONE
  60. };
  61. AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
  62. if (!fmts_list)
  63. return AVERROR(ENOMEM);
  64. return ff_set_common_formats(ctx, fmts_list);
  65. }
  66. static void make_horizontal_map(AVFilterContext *ctx)
  67. {
  68. ShufflePixelsContext *s = ctx->priv;
  69. const int nb_blocks = s->nb_blocks;
  70. AVLFG *c = &s->c;
  71. uint8_t *used = s->used;
  72. int32_t *map = s->map;
  73. for (int x = 0; x < s->planewidth[0];) {
  74. int rand = av_lfg_get(c) % nb_blocks;
  75. if (used[rand] == 0) {
  76. int width;
  77. if (s->direction) {
  78. width = FFMIN(s->block_w, s->planewidth[0] - x);
  79. map[rand * s->block_w] = x;
  80. } else {
  81. width = FFMIN(s->block_w, s->planewidth[0] - rand * s->block_w);
  82. map[x] = rand * s->block_w;
  83. }
  84. used[rand] = 1;
  85. if (s->direction) {
  86. for (int i = 1; i < width; i++) {
  87. map[rand * s->block_w + i] = map[rand * s->block_w] + i;
  88. }
  89. } else {
  90. for (int i = 1; i < width; i++) {
  91. map[x + i] = map[x] + i;
  92. }
  93. }
  94. x += width;
  95. }
  96. }
  97. }
  98. static void make_vertical_map(AVFilterContext *ctx)
  99. {
  100. ShufflePixelsContext *s = ctx->priv;
  101. const int nb_blocks = s->nb_blocks;
  102. AVLFG *c = &s->c;
  103. uint8_t *used = s->used;
  104. int32_t *map = s->map;
  105. for (int y = 0; y < s->planeheight[0];) {
  106. int rand = av_lfg_get(c) % nb_blocks;
  107. if (used[rand] == 0) {
  108. int height;
  109. if (s->direction) {
  110. height = FFMIN(s->block_h, s->planeheight[0] - y);
  111. map[rand * s->block_h] = y;
  112. } else {
  113. height = FFMIN(s->block_h, s->planeheight[0] - rand * s->block_h);
  114. map[y] = rand * s->block_h;
  115. }
  116. used[rand] = 1;
  117. if (s->direction) {
  118. for (int i = 1; i < height; i++) {
  119. map[rand * s->block_h + i] = map[rand * s->block_h] + i;
  120. }
  121. } else {
  122. for (int i = 1; i < height; i++) {
  123. map[y + i] = map[y] + i;
  124. }
  125. }
  126. y += height;
  127. }
  128. }
  129. }
  130. static void make_block_map(AVFilterContext *ctx)
  131. {
  132. ShufflePixelsContext *s = ctx->priv;
  133. const int nb_blocks = s->nb_blocks;
  134. int nb_blocks_w = s->planewidth[0] / s->block_w;
  135. AVLFG *c = &s->c;
  136. uint8_t *used = s->used;
  137. int32_t *map = s->map;
  138. for (int i = 0; i < nb_blocks;) {
  139. int rand = av_lfg_get(c) % nb_blocks;
  140. if (used[rand] == 0) {
  141. int yin = i / nb_blocks_w;
  142. int xin = i % nb_blocks_w;
  143. int in = yin * s->block_h * s->planewidth[0] + xin * s->block_w;
  144. int yout = rand / nb_blocks_w;
  145. int xout = rand % nb_blocks_w;
  146. int out = yout * s->block_h * s->planewidth[0] + xout * s->block_w;
  147. if (s->direction) {
  148. map[out] = in;
  149. } else {
  150. map[in] = out;
  151. }
  152. used[rand] = 1;
  153. if (s->direction) {
  154. for (int y = 0; y < s->block_h; y++) {
  155. for (int x = 0; x < s->block_w; x++) {
  156. map[out + y * s->planewidth[0] + x] = map[out] + x + y * s->planewidth[0];
  157. }
  158. }
  159. } else {
  160. for (int y = 0; y < s->block_h; y++) {
  161. for (int x = 0; x < s->block_w; x++) {
  162. map[in + y * s->planewidth[0] + x] = map[in] + x + y * s->planewidth[0];
  163. }
  164. }
  165. }
  166. i++;
  167. }
  168. }
  169. }
  170. typedef struct ThreadData {
  171. AVFrame *in, *out;
  172. } ThreadData;
  173. #define SHUFFLE_HORIZONTAL(name, type) \
  174. static int shuffle_horizontal## name(AVFilterContext *ctx, void *arg, \
  175. int jobnr, int nb_jobs) \
  176. { \
  177. ShufflePixelsContext *s = ctx->priv; \
  178. ThreadData *td = arg; \
  179. AVFrame *in = td->in; \
  180. AVFrame *out = td->out; \
  181. \
  182. for (int p = 0; p < s->nb_planes; p++) { \
  183. const int slice_start = (s->planeheight[p] * jobnr) / nb_jobs; \
  184. const int slice_end = (s->planeheight[p] * (jobnr+1)) / nb_jobs; \
  185. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  186. const type *src = (const type *)(in->data[p] + \
  187. slice_start * in->linesize[p]); \
  188. const int32_t *map = s->map; \
  189. \
  190. for (int y = slice_start; y < slice_end; y++) { \
  191. for (int x = 0; x < s->planewidth[p]; x++) { \
  192. dst[x] = src[map[x]]; \
  193. } \
  194. \
  195. dst += out->linesize[p] / sizeof(type); \
  196. src += in->linesize[p] / sizeof(type); \
  197. } \
  198. } \
  199. \
  200. return 0; \
  201. }
  202. SHUFFLE_HORIZONTAL(8, uint8_t)
  203. SHUFFLE_HORIZONTAL(16, uint16_t)
  204. #define SHUFFLE_VERTICAL(name, type) \
  205. static int shuffle_vertical## name(AVFilterContext *ctx, void *arg, \
  206. int jobnr, int nb_jobs) \
  207. { \
  208. ShufflePixelsContext *s = ctx->priv; \
  209. ThreadData *td = arg; \
  210. AVFrame *in = td->in; \
  211. AVFrame *out = td->out; \
  212. \
  213. for (int p = 0; p < s->nb_planes; p++) { \
  214. const int slice_start = (s->planeheight[p] * jobnr) / nb_jobs; \
  215. const int slice_end = (s->planeheight[p] * (jobnr+1)) / nb_jobs; \
  216. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  217. const int32_t *map = s->map; \
  218. \
  219. for (int y = slice_start; y < slice_end; y++) { \
  220. const type *src = (const type *)(in->data[p] + \
  221. map[y] * in->linesize[p]); \
  222. \
  223. memcpy(dst, src, s->linesize[p]); \
  224. dst += out->linesize[p] / sizeof(type); \
  225. } \
  226. } \
  227. \
  228. return 0; \
  229. }
  230. SHUFFLE_VERTICAL(8, uint8_t)
  231. SHUFFLE_VERTICAL(16, uint16_t)
  232. #define SHUFFLE_BLOCK(name, type) \
  233. static int shuffle_block## name(AVFilterContext *ctx, void *arg, \
  234. int jobnr, int nb_jobs) \
  235. { \
  236. ShufflePixelsContext *s = ctx->priv; \
  237. ThreadData *td = arg; \
  238. AVFrame *in = td->in; \
  239. AVFrame *out = td->out; \
  240. \
  241. for (int p = 0; p < s->nb_planes; p++) { \
  242. const int slice_start = (s->planeheight[p] * jobnr) / nb_jobs; \
  243. const int slice_end = (s->planeheight[p] * (jobnr+1)) / nb_jobs; \
  244. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  245. const type *src = (const type *)in->data[p]; \
  246. const int32_t *map = s->map + slice_start * s->planewidth[p]; \
  247. \
  248. for (int y = slice_start; y < slice_end; y++) { \
  249. for (int x = 0; x < s->planewidth[p]; x++) { \
  250. int ymap = map[x] / s->planewidth[p]; \
  251. int xmap = map[x] % s->planewidth[p]; \
  252. \
  253. dst[x] = src[xmap + ymap * in->linesize[p] / sizeof(type)]; \
  254. } \
  255. \
  256. dst += out->linesize[p] / sizeof(type); \
  257. map += s->planewidth[p]; \
  258. } \
  259. } \
  260. \
  261. return 0; \
  262. }
  263. SHUFFLE_BLOCK(8, uint8_t)
  264. SHUFFLE_BLOCK(16, uint16_t)
  265. static int config_output(AVFilterLink *outlink)
  266. {
  267. AVFilterContext *ctx = outlink->src;
  268. ShufflePixelsContext *s = ctx->priv;
  269. AVFilterLink *inlink = ctx->inputs[0];
  270. const AVPixFmtDescriptor *desc;
  271. int ret;
  272. if (s->seed == -1)
  273. s->seed = av_get_random_seed();
  274. av_lfg_init(&s->c, s->seed);
  275. desc = av_pix_fmt_desc_get(outlink->format);
  276. if (!desc)
  277. return AVERROR_BUG;
  278. s->nb_planes = av_pix_fmt_count_planes(outlink->format);
  279. s->depth = desc->comp[0].depth;
  280. if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0)
  281. return ret;
  282. s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
  283. s->planewidth[0] = s->planewidth[3] = inlink->w;
  284. s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
  285. s->planeheight[0] = s->planeheight[3] = inlink->h;
  286. s->map = av_calloc(inlink->w * inlink->h, sizeof(*s->map));
  287. if (!s->map)
  288. return AVERROR(ENOMEM);
  289. switch (s->mode) {
  290. case 0:
  291. s->shuffle_pixels = s->depth <= 8 ? shuffle_horizontal8 : shuffle_horizontal16;
  292. s->nb_blocks = (s->planewidth[0] + s->block_w - 1) / s->block_w;
  293. break;
  294. case 1:
  295. s->shuffle_pixels = s->depth <= 8 ? shuffle_vertical8 : shuffle_vertical16;
  296. s->nb_blocks = (s->planeheight[0] + s->block_h - 1) / s->block_h;
  297. break;
  298. case 2:
  299. s->shuffle_pixels = s->depth <= 8 ? shuffle_block8 : shuffle_block16;
  300. s->nb_blocks = (s->planeheight[0] / s->block_h) *
  301. (s->planewidth[0] / s->block_w);
  302. break;
  303. default:
  304. av_assert0(0);
  305. }
  306. s->used = av_calloc(s->nb_blocks, sizeof(*s->used));
  307. if (!s->used)
  308. return AVERROR(ENOMEM);
  309. switch (s->mode) {
  310. case 0:
  311. make_horizontal_map(ctx);
  312. break;
  313. case 1:
  314. make_vertical_map(ctx);
  315. break;
  316. case 2:
  317. make_block_map(ctx);
  318. break;
  319. default:
  320. av_assert0(0);
  321. }
  322. return 0;
  323. }
  324. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  325. {
  326. AVFilterContext *ctx = inlink->dst;
  327. ShufflePixelsContext *s = ctx->priv;
  328. AVFrame *out = ff_get_video_buffer(ctx->outputs[0], in->width, in->height);
  329. ThreadData td;
  330. int ret;
  331. ret = av_frame_copy_props(out, in);
  332. if (ret < 0) {
  333. av_frame_free(&out);
  334. goto fail;
  335. }
  336. td.out = out;
  337. td.in = in;
  338. ctx->internal->execute(ctx, s->shuffle_pixels, &td, NULL, FFMIN(s->planeheight[1], ff_filter_get_nb_threads(ctx)));
  339. av_frame_free(&in);
  340. return ff_filter_frame(ctx->outputs[0], out);
  341. fail:
  342. av_frame_free(&in);
  343. return ret;
  344. }
  345. static av_cold void uninit(AVFilterContext *ctx)
  346. {
  347. ShufflePixelsContext *s = ctx->priv;
  348. av_freep(&s->map);
  349. av_freep(&s->used);
  350. }
  351. #define OFFSET(x) offsetof(ShufflePixelsContext, x)
  352. #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
  353. static const AVOption shufflepixels_options[] = {
  354. { "direction", "set shuffle direction", OFFSET(direction), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "dir" },
  355. { "d", "set shuffle direction", OFFSET(direction), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "dir" },
  356. { "forward", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "dir" },
  357. { "inverse", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "dir" },
  358. { "mode", "set shuffle mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "mode" },
  359. { "m", "set shuffle mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "mode" },
  360. { "horizontal", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" },
  361. { "vertical", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" },
  362. { "block", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "mode" },
  363. { "width", "set block width", OFFSET(block_w), AV_OPT_TYPE_INT, {.i64=10}, 1, 8000, FLAGS },
  364. { "w", "set block width", OFFSET(block_w), AV_OPT_TYPE_INT, {.i64=10}, 1, 8000, FLAGS },
  365. { "height", "set block height", OFFSET(block_h), AV_OPT_TYPE_INT, {.i64=10}, 1, 8000, FLAGS },
  366. { "h", "set block height", OFFSET(block_h), AV_OPT_TYPE_INT, {.i64=10}, 1, 8000, FLAGS },
  367. { "seed", "set random seed", OFFSET(seed), AV_OPT_TYPE_INT64, {.i64=-1}, -1, UINT_MAX, FLAGS },
  368. { "s", "set random seed", OFFSET(seed), AV_OPT_TYPE_INT64, {.i64=-1}, -1, UINT_MAX, FLAGS },
  369. { NULL },
  370. };
  371. AVFILTER_DEFINE_CLASS(shufflepixels);
  372. static const AVFilterPad shufflepixels_inputs[] = {
  373. {
  374. .name = "default",
  375. .type = AVMEDIA_TYPE_VIDEO,
  376. .filter_frame = filter_frame,
  377. },
  378. { NULL },
  379. };
  380. static const AVFilterPad shufflepixels_outputs[] = {
  381. {
  382. .name = "default",
  383. .type = AVMEDIA_TYPE_VIDEO,
  384. .config_props = config_output,
  385. },
  386. { NULL },
  387. };
  388. AVFilter ff_vf_shufflepixels = {
  389. .name = "shufflepixels",
  390. .description = NULL_IF_CONFIG_SMALL("Shuffle video pixels."),
  391. .priv_size = sizeof(ShufflePixelsContext),
  392. .priv_class = &shufflepixels_class,
  393. .query_formats = query_formats,
  394. .uninit = uninit,
  395. .inputs = shufflepixels_inputs,
  396. .outputs = shufflepixels_outputs,
  397. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
  398. };