vf_cover_rect.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /*
  2. * Copyright (c) 2014-2015 Michael Niedermayer <michaelni@gmx.at>
  3. *
  4. * This file is part of FFmpeg.
  5. *
  6. * FFmpeg is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (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
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. */
  20. /**
  21. * @todo switch to dualinput
  22. */
  23. #include "libavutil/avassert.h"
  24. #include "libavutil/imgutils.h"
  25. #include "libavutil/opt.h"
  26. #include "internal.h"
  27. #include "lavfutils.h"
  28. enum mode {
  29. MODE_COVER,
  30. MODE_BLUR,
  31. NB_MODES
  32. };
  33. typedef struct CoverContext {
  34. AVClass *class;
  35. int mode;
  36. char *cover_filename;
  37. AVFrame *cover_frame;
  38. int width, height;
  39. } CoverContext;
  40. #define OFFSET(x) offsetof(CoverContext, x)
  41. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  42. static const AVOption cover_rect_options[] = {
  43. { "cover", "cover bitmap filename", OFFSET(cover_filename), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS },
  44. { "mode", "set removal mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_BLUR}, 0, NB_MODES - 1, FLAGS, "mode" },
  45. { "cover", "cover area with bitmap", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_COVER}, INT_MIN, INT_MAX, FLAGS, "mode" },
  46. { "blur", "blur area", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_BLUR}, INT_MIN, INT_MAX, FLAGS, "mode" },
  47. { NULL }
  48. };
  49. AVFILTER_DEFINE_CLASS(cover_rect);
  50. static int query_formats(AVFilterContext *ctx)
  51. {
  52. static const enum AVPixelFormat pix_fmts[] = {
  53. AV_PIX_FMT_YUV420P,
  54. AV_PIX_FMT_YUVJ420P,
  55. AV_PIX_FMT_NONE
  56. };
  57. return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
  58. }
  59. static int config_input(AVFilterLink *inlink)
  60. {
  61. return 0;
  62. }
  63. static void cover_rect(CoverContext *cover, AVFrame *in, int offx, int offy)
  64. {
  65. int x, y, p;
  66. for (p = 0; p < 3; p++) {
  67. uint8_t *data = in->data[p] + (offx>>!!p) + (offy>>!!p) * in->linesize[p];
  68. const uint8_t *src = cover->cover_frame->data[p];
  69. int w = AV_CEIL_RSHIFT(cover->cover_frame->width , !!p);
  70. int h = AV_CEIL_RSHIFT(cover->cover_frame->height, !!p);
  71. for (y = 0; y < h; y++) {
  72. for (x = 0; x < w; x++) {
  73. data[x] = src[x];
  74. }
  75. data += in->linesize[p];
  76. src += cover->cover_frame->linesize[p];
  77. }
  78. }
  79. }
  80. static void blur(CoverContext *cover, AVFrame *in, int offx, int offy)
  81. {
  82. int x, y, p;
  83. for (p=0; p<3; p++) {
  84. int ox = offx>>!!p;
  85. int oy = offy>>!!p;
  86. int stride = in->linesize[p];
  87. uint8_t *data = in->data[p] + ox + oy * stride;
  88. int w = AV_CEIL_RSHIFT(cover->width , !!p);
  89. int h = AV_CEIL_RSHIFT(cover->height, !!p);
  90. int iw = AV_CEIL_RSHIFT(in->width , !!p);
  91. int ih = AV_CEIL_RSHIFT(in->height, !!p);
  92. for (y = 0; y < h; y++) {
  93. for (x = 0; x < w; x++) {
  94. int c = 0;
  95. int s = 0;
  96. if (ox) {
  97. int scale = 65536 / (x + 1);
  98. s += data[-1 + y*stride] * scale;
  99. c += scale;
  100. }
  101. if (oy) {
  102. int scale = 65536 / (y + 1);
  103. s += data[x - stride] * scale;
  104. c += scale;
  105. }
  106. if (ox + w < iw) {
  107. int scale = 65536 / (w - x);
  108. s += data[w + y*stride] * scale;
  109. c += scale;
  110. }
  111. if (oy + h < ih) {
  112. int scale = 65536 / (h - y);
  113. s += data[x + h*stride] * scale;
  114. c += scale;
  115. }
  116. data[x + y*stride] = c ? (s + (c>>1)) / c : 0;
  117. }
  118. }
  119. }
  120. }
  121. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  122. {
  123. AVFilterContext *ctx = inlink->dst;
  124. CoverContext *cover = ctx->priv;
  125. AVDictionaryEntry *ex, *ey, *ew, *eh;
  126. int x = -1, y = -1, w = -1, h = -1;
  127. char *xendptr = NULL, *yendptr = NULL, *wendptr = NULL, *hendptr = NULL;
  128. ex = av_dict_get(in->metadata, "lavfi.rect.x", NULL, AV_DICT_MATCH_CASE);
  129. ey = av_dict_get(in->metadata, "lavfi.rect.y", NULL, AV_DICT_MATCH_CASE);
  130. ew = av_dict_get(in->metadata, "lavfi.rect.w", NULL, AV_DICT_MATCH_CASE);
  131. eh = av_dict_get(in->metadata, "lavfi.rect.h", NULL, AV_DICT_MATCH_CASE);
  132. if (ex && ey && ew && eh) {
  133. x = strtol(ex->value, &xendptr, 10);
  134. y = strtol(ey->value, &yendptr, 10);
  135. w = strtol(ew->value, &wendptr, 10);
  136. h = strtol(eh->value, &hendptr, 10);
  137. }
  138. if (!xendptr || *xendptr || !yendptr || *yendptr ||
  139. !wendptr || *wendptr || !hendptr || !hendptr
  140. ) {
  141. return ff_filter_frame(ctx->outputs[0], in);
  142. }
  143. if (x < 0) {
  144. w += x;
  145. x = 0;
  146. }
  147. if (y < 0) {
  148. h += y;
  149. y = 0;
  150. }
  151. w = FFMIN(w, in->width - x);
  152. h = FFMIN(h, in->height - y);
  153. if (w > in->width || h > in->height || w <= 0 || h <= 0)
  154. return AVERROR(EINVAL);
  155. if (cover->cover_frame) {
  156. if (w != cover->cover_frame->width || h != cover->cover_frame->height)
  157. return AVERROR(EINVAL);
  158. }
  159. cover->width = w;
  160. cover->height = h;
  161. x = av_clip(x, 0, in->width - w);
  162. y = av_clip(y, 0, in->height - h);
  163. av_frame_make_writable(in);
  164. if (cover->mode == MODE_BLUR) {
  165. blur (cover, in, x, y);
  166. } else {
  167. cover_rect(cover, in, x, y);
  168. }
  169. return ff_filter_frame(ctx->outputs[0], in);
  170. }
  171. static av_cold void uninit(AVFilterContext *ctx)
  172. {
  173. CoverContext *cover = ctx->priv;
  174. if (cover->cover_frame)
  175. av_freep(&cover->cover_frame->data[0]);
  176. }
  177. static av_cold int init(AVFilterContext *ctx)
  178. {
  179. CoverContext *cover = ctx->priv;
  180. int ret;
  181. if (cover->mode == MODE_COVER) {
  182. if (!cover->cover_filename) {
  183. av_log(ctx, AV_LOG_ERROR, "cover filename not set\n");
  184. return AVERROR(EINVAL);
  185. }
  186. cover->cover_frame = av_frame_alloc();
  187. if (!cover->cover_frame)
  188. return AVERROR(ENOMEM);
  189. if ((ret = ff_load_image(cover->cover_frame->data, cover->cover_frame->linesize,
  190. &cover->cover_frame->width, &cover->cover_frame->height,
  191. &cover->cover_frame->format, cover->cover_filename, ctx)) < 0)
  192. return ret;
  193. if (cover->cover_frame->format != AV_PIX_FMT_YUV420P && cover->cover_frame->format != AV_PIX_FMT_YUVJ420P) {
  194. av_log(ctx, AV_LOG_ERROR, "cover image is not a YUV420 image\n");
  195. return AVERROR(EINVAL);
  196. }
  197. }
  198. return 0;
  199. }
  200. static const AVFilterPad cover_rect_inputs[] = {
  201. {
  202. .name = "default",
  203. .type = AVMEDIA_TYPE_VIDEO,
  204. .config_props = config_input,
  205. .filter_frame = filter_frame,
  206. },
  207. { NULL }
  208. };
  209. static const AVFilterPad cover_rect_outputs[] = {
  210. {
  211. .name = "default",
  212. .type = AVMEDIA_TYPE_VIDEO,
  213. },
  214. { NULL }
  215. };
  216. AVFilter ff_vf_cover_rect = {
  217. .name = "cover_rect",
  218. .description = NULL_IF_CONFIG_SMALL("Find and cover a user specified object."),
  219. .priv_size = sizeof(CoverContext),
  220. .init = init,
  221. .uninit = uninit,
  222. .query_formats = query_formats,
  223. .inputs = cover_rect_inputs,
  224. .outputs = cover_rect_outputs,
  225. .priv_class = &cover_rect_class,
  226. };