Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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. /**
  22.  * @todo switch to dualinput
  23.  */
  24.  
  25. #include "libavutil/avassert.h"
  26. #include "libavutil/imgutils.h"
  27. #include "libavutil/opt.h"
  28. #include "internal.h"
  29.  
  30. #include "lavfutils.h"
  31.  
  32. enum mode {
  33.     MODE_COVER,
  34.     MODE_BLUR,
  35.     NB_MODES
  36. };
  37.  
  38. typedef struct CoverContext {
  39.     AVClass *class;
  40.     int mode;
  41.     char *cover_filename;
  42.     AVFrame *cover_frame;
  43.     int width, height;
  44. } CoverContext;
  45.  
  46. #define OFFSET(x) offsetof(CoverContext, x)
  47. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  48. static const AVOption cover_rect_options[] = {
  49.     { "cover",  "cover bitmap filename",  OFFSET(cover_filename),  AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS },
  50.     { "mode", "set removal mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_BLUR}, 0, NB_MODES - 1, FLAGS, "mode" },
  51.         { "cover", "cover area with bitmap", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_COVER}, INT_MIN, INT_MAX, FLAGS, "mode" },
  52.         { "blur", "blur area", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_BLUR}, INT_MIN, INT_MAX, FLAGS, "mode" },
  53.     { NULL }
  54. };
  55.  
  56. AVFILTER_DEFINE_CLASS(cover_rect);
  57.  
  58. static int query_formats(AVFilterContext *ctx)
  59. {
  60.     static const enum AVPixelFormat pix_fmts[] = {
  61.         AV_PIX_FMT_YUV420P,
  62.         AV_PIX_FMT_YUVJ420P,
  63.         AV_PIX_FMT_NONE
  64.     };
  65.  
  66.     return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
  67. }
  68.  
  69. static int config_input(AVFilterLink *inlink)
  70. {
  71.     return 0;
  72. }
  73.  
  74. static void cover_rect(CoverContext *cover, AVFrame *in, int offx, int offy)
  75. {
  76.     int x, y, p;
  77.  
  78.     for (p = 0; p < 3; p++) {
  79.         uint8_t *data = in->data[p] + (offx>>!!p) + (offy>>!!p) * in->linesize[p];
  80.         const uint8_t *src = cover->cover_frame->data[p];
  81.         int w = FF_CEIL_RSHIFT(cover->cover_frame->width , !!p);
  82.         int h = FF_CEIL_RSHIFT(cover->cover_frame->height, !!p);
  83.         for (y = 0; y < h; y++) {
  84.             for (x = 0; x < w; x++) {
  85.                 data[x] = src[x];
  86.             }
  87.             data += in->linesize[p];
  88.             src += cover->cover_frame->linesize[p];
  89.         }
  90.     }
  91. }
  92. static void blur(CoverContext *cover, AVFrame *in, int offx, int offy)
  93. {
  94.     int x, y, p;
  95.  
  96.     for (p=0; p<3; p++) {
  97.         int ox = offx>>!!p;
  98.         int oy = offy>>!!p;
  99.         int stride = in->linesize[p];
  100.         uint8_t *data = in->data[p] + ox + oy * stride;
  101.         int w = FF_CEIL_RSHIFT(cover->width , !!p);
  102.         int h = FF_CEIL_RSHIFT(cover->height, !!p);
  103.         int iw = FF_CEIL_RSHIFT(in->width , !!p);
  104.         int ih = FF_CEIL_RSHIFT(in->height, !!p);
  105.         for (y = 0; y < h; y++) {
  106.             for (x = 0; x < w; x++) {
  107.                 int c = 0;
  108.                 int s = 0;
  109.                 if (ox) {
  110.                     int scale = 65536 / (x + 1);
  111.                     s += data[-1 + y*stride] * scale;
  112.                     c += scale;
  113.                 }
  114.                 if (oy) {
  115.                     int scale = 65536 / (y + 1);
  116.                     s += data[x - stride] * scale;
  117.                     c += scale;
  118.                 }
  119.                 if (ox + w < iw) {
  120.                     int scale = 65536 / (w - x);
  121.                     s += data[w + y*stride] * scale;
  122.                     c += scale;
  123.                 }
  124.                 if (oy + h < ih) {
  125.                     int scale = 65536 / (h - y);
  126.                     s += data[x + h*stride] * scale;
  127.                     c += scale;
  128.                 }
  129.                 data[x + y*stride] = c ? (s + (c>>1)) / c : 0;
  130.             }
  131.         }
  132.     }
  133. }
  134.  
  135. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  136. {
  137.     AVFilterContext *ctx = inlink->dst;
  138.     CoverContext *cover = ctx->priv;
  139.     AVDictionaryEntry *ex, *ey, *ew, *eh;
  140.     int x = -1, y = -1, w = -1, h = -1;
  141.     char *xendptr = NULL, *yendptr = NULL, *wendptr = NULL, *hendptr = NULL;
  142.  
  143.     ex = av_dict_get(in->metadata, "lavfi.rect.x", NULL, AV_DICT_MATCH_CASE);
  144.     ey = av_dict_get(in->metadata, "lavfi.rect.y", NULL, AV_DICT_MATCH_CASE);
  145.     ew = av_dict_get(in->metadata, "lavfi.rect.w", NULL, AV_DICT_MATCH_CASE);
  146.     eh = av_dict_get(in->metadata, "lavfi.rect.h", NULL, AV_DICT_MATCH_CASE);
  147.     if (ex && ey && ew && eh) {
  148.         x = strtol(ex->value, &xendptr, 10);
  149.         y = strtol(ey->value, &yendptr, 10);
  150.         w = strtol(ew->value, &wendptr, 10);
  151.         h = strtol(eh->value, &hendptr, 10);
  152.     }
  153.  
  154.     if (!xendptr || *xendptr || !yendptr || *yendptr ||
  155.         !wendptr || *wendptr || !hendptr || !hendptr
  156.     ) {
  157.         return ff_filter_frame(ctx->outputs[0], in);
  158.     }
  159.  
  160.     if (x < 0) {
  161.         w += x;
  162.         x = 0;
  163.     }
  164.     if (y < 0) {
  165.         h += y;
  166.         y = 0;
  167.     }
  168.     w = FFMIN(w, in->width  - x);
  169.     h = FFMIN(h, in->height - y);
  170.  
  171.     if (w > in->width || h > in->height || w <= 0 || h <= 0)
  172.         return AVERROR(EINVAL);
  173.  
  174.     if (cover->cover_frame) {
  175.         if (w != cover->cover_frame->width || h != cover->cover_frame->height)
  176.             return AVERROR(EINVAL);
  177.     }
  178.  
  179.     cover->width  = w;
  180.     cover->height = h;
  181.  
  182.     x = av_clip(x, 0, in->width  - w);
  183.     y = av_clip(y, 0, in->height - h);
  184.  
  185.     av_frame_make_writable(in);
  186.  
  187.     if (cover->mode == MODE_BLUR) {
  188.         blur (cover, in, x, y);
  189.     } else {
  190.         cover_rect(cover, in, x, y);
  191.     }
  192.     return ff_filter_frame(ctx->outputs[0], in);
  193. }
  194.  
  195. static av_cold void uninit(AVFilterContext *ctx)
  196. {
  197.     CoverContext *cover = ctx->priv;
  198.  
  199.     if (cover->cover_frame)
  200.         av_freep(&cover->cover_frame->data[0]);
  201. }
  202.  
  203. static av_cold int init(AVFilterContext *ctx)
  204. {
  205.     CoverContext *cover = ctx->priv;
  206.     int ret;
  207.  
  208.     if (cover->mode == MODE_COVER) {
  209.         if (!cover->cover_filename) {
  210.             av_log(ctx, AV_LOG_ERROR, "cover filename not set\n");
  211.             return AVERROR(EINVAL);
  212.         }
  213.  
  214.         cover->cover_frame = av_frame_alloc();
  215.         if (!cover->cover_frame)
  216.             return AVERROR(ENOMEM);
  217.  
  218.         if ((ret = ff_load_image(cover->cover_frame->data, cover->cover_frame->linesize,
  219.                                 &cover->cover_frame->width, &cover->cover_frame->height,
  220.                                 &cover->cover_frame->format, cover->cover_filename, ctx)) < 0)
  221.             return ret;
  222.  
  223.         if (cover->cover_frame->format != AV_PIX_FMT_YUV420P && cover->cover_frame->format != AV_PIX_FMT_YUVJ420P) {
  224.             av_log(ctx, AV_LOG_ERROR, "cover image is not a YUV420 image\n");
  225.             return AVERROR(EINVAL);
  226.         }
  227.     }
  228.  
  229.     return 0;
  230. }
  231.  
  232. static const AVFilterPad cover_rect_inputs[] = {
  233.     {
  234.         .name         = "default",
  235.         .type         = AVMEDIA_TYPE_VIDEO,
  236.         .config_props = config_input,
  237.         .filter_frame = filter_frame,
  238.     },
  239.     { NULL }
  240. };
  241.  
  242. static const AVFilterPad cover_rect_outputs[] = {
  243.     {
  244.         .name = "default",
  245.         .type = AVMEDIA_TYPE_VIDEO,
  246.     },
  247.     { NULL }
  248. };
  249.  
  250. AVFilter ff_vf_cover_rect = {
  251.     .name            = "cover_rect",
  252.     .description     = NULL_IF_CONFIG_SMALL("Find and cover a user specified object"),
  253.     .priv_size       = sizeof(CoverContext),
  254.     .init            = init,
  255.     .uninit          = uninit,
  256.     .query_formats   = query_formats,
  257.     .inputs          = cover_rect_inputs,
  258.     .outputs         = cover_rect_outputs,
  259.     .priv_class      = &cover_rect_class,
  260. };
  261.