123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- /*
- * Copyright (c) 2014-2015 Michael Niedermayer <michaelni@gmx.at>
- *
- * This file is part of FFmpeg.
- *
- * FFmpeg is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * FFmpeg is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- /**
- * @todo switch to dualinput
- */
- #include "libavutil/avassert.h"
- #include "libavutil/imgutils.h"
- #include "libavutil/opt.h"
- #include "internal.h"
- #include "lavfutils.h"
- enum mode {
- MODE_COVER,
- MODE_BLUR,
- NB_MODES
- };
- typedef struct CoverContext {
- AVClass *class;
- int mode;
- char *cover_filename;
- AVFrame *cover_frame;
- int width, height;
- } CoverContext;
- #define OFFSET(x) offsetof(CoverContext, x)
- #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
- static const AVOption cover_rect_options[] = {
- { "cover", "cover bitmap filename", OFFSET(cover_filename), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS },
- { "mode", "set removal mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_BLUR}, 0, NB_MODES - 1, FLAGS, "mode" },
- { "cover", "cover area with bitmap", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_COVER}, INT_MIN, INT_MAX, FLAGS, "mode" },
- { "blur", "blur area", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_BLUR}, INT_MIN, INT_MAX, FLAGS, "mode" },
- { NULL }
- };
- AVFILTER_DEFINE_CLASS(cover_rect);
- static int query_formats(AVFilterContext *ctx)
- {
- static const enum AVPixelFormat pix_fmts[] = {
- AV_PIX_FMT_YUV420P,
- AV_PIX_FMT_YUVJ420P,
- AV_PIX_FMT_NONE
- };
- return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
- }
- static int config_input(AVFilterLink *inlink)
- {
- return 0;
- }
- static void cover_rect(CoverContext *cover, AVFrame *in, int offx, int offy)
- {
- int x, y, p;
- for (p = 0; p < 3; p++) {
- uint8_t *data = in->data[p] + (offx>>!!p) + (offy>>!!p) * in->linesize[p];
- const uint8_t *src = cover->cover_frame->data[p];
- int w = AV_CEIL_RSHIFT(cover->cover_frame->width , !!p);
- int h = AV_CEIL_RSHIFT(cover->cover_frame->height, !!p);
- for (y = 0; y < h; y++) {
- for (x = 0; x < w; x++) {
- data[x] = src[x];
- }
- data += in->linesize[p];
- src += cover->cover_frame->linesize[p];
- }
- }
- }
- static void blur(CoverContext *cover, AVFrame *in, int offx, int offy)
- {
- int x, y, p;
- for (p=0; p<3; p++) {
- int ox = offx>>!!p;
- int oy = offy>>!!p;
- int stride = in->linesize[p];
- uint8_t *data = in->data[p] + ox + oy * stride;
- int w = AV_CEIL_RSHIFT(cover->width , !!p);
- int h = AV_CEIL_RSHIFT(cover->height, !!p);
- int iw = AV_CEIL_RSHIFT(in->width , !!p);
- int ih = AV_CEIL_RSHIFT(in->height, !!p);
- for (y = 0; y < h; y++) {
- for (x = 0; x < w; x++) {
- int c = 0;
- int s = 0;
- if (ox) {
- int scale = 65536 / (x + 1);
- s += data[-1 + y*stride] * scale;
- c += scale;
- }
- if (oy) {
- int scale = 65536 / (y + 1);
- s += data[x - stride] * scale;
- c += scale;
- }
- if (ox + w < iw) {
- int scale = 65536 / (w - x);
- s += data[w + y*stride] * scale;
- c += scale;
- }
- if (oy + h < ih) {
- int scale = 65536 / (h - y);
- s += data[x + h*stride] * scale;
- c += scale;
- }
- data[x + y*stride] = c ? (s + (c>>1)) / c : 0;
- }
- }
- }
- }
- static int filter_frame(AVFilterLink *inlink, AVFrame *in)
- {
- AVFilterContext *ctx = inlink->dst;
- CoverContext *cover = ctx->priv;
- AVDictionaryEntry *ex, *ey, *ew, *eh;
- int x = -1, y = -1, w = -1, h = -1;
- char *xendptr = NULL, *yendptr = NULL, *wendptr = NULL, *hendptr = NULL;
- ex = av_dict_get(in->metadata, "lavfi.rect.x", NULL, AV_DICT_MATCH_CASE);
- ey = av_dict_get(in->metadata, "lavfi.rect.y", NULL, AV_DICT_MATCH_CASE);
- ew = av_dict_get(in->metadata, "lavfi.rect.w", NULL, AV_DICT_MATCH_CASE);
- eh = av_dict_get(in->metadata, "lavfi.rect.h", NULL, AV_DICT_MATCH_CASE);
- if (ex && ey && ew && eh) {
- x = strtol(ex->value, &xendptr, 10);
- y = strtol(ey->value, ¥dptr, 10);
- w = strtol(ew->value, &wendptr, 10);
- h = strtol(eh->value, &hendptr, 10);
- }
- if (!xendptr || *xendptr || !yendptr || *yendptr ||
- !wendptr || *wendptr || !hendptr || *hendptr
- ) {
- return ff_filter_frame(ctx->outputs[0], in);
- }
- if (x < 0) {
- w += x;
- x = 0;
- }
- if (y < 0) {
- h += y;
- y = 0;
- }
- w = FFMIN(w, in->width - x);
- h = FFMIN(h, in->height - y);
- if (w > in->width || h > in->height || w <= 0 || h <= 0)
- return AVERROR(EINVAL);
- if (cover->cover_frame) {
- if (w != cover->cover_frame->width || h != cover->cover_frame->height)
- return AVERROR(EINVAL);
- }
- cover->width = w;
- cover->height = h;
- x = av_clip(x, 0, in->width - w);
- y = av_clip(y, 0, in->height - h);
- av_frame_make_writable(in);
- if (cover->mode == MODE_BLUR) {
- blur (cover, in, x, y);
- } else {
- cover_rect(cover, in, x, y);
- }
- return ff_filter_frame(ctx->outputs[0], in);
- }
- static av_cold void uninit(AVFilterContext *ctx)
- {
- CoverContext *cover = ctx->priv;
- if (cover->cover_frame)
- av_freep(&cover->cover_frame->data[0]);
- av_frame_free(&cover->cover_frame);
- }
- static av_cold int init(AVFilterContext *ctx)
- {
- CoverContext *cover = ctx->priv;
- int ret;
- if (cover->mode == MODE_COVER) {
- if (!cover->cover_filename) {
- av_log(ctx, AV_LOG_ERROR, "cover filename not set\n");
- return AVERROR(EINVAL);
- }
- cover->cover_frame = av_frame_alloc();
- if (!cover->cover_frame)
- return AVERROR(ENOMEM);
- if ((ret = ff_load_image(cover->cover_frame->data, cover->cover_frame->linesize,
- &cover->cover_frame->width, &cover->cover_frame->height,
- &cover->cover_frame->format, cover->cover_filename, ctx)) < 0)
- return ret;
- if (cover->cover_frame->format != AV_PIX_FMT_YUV420P && cover->cover_frame->format != AV_PIX_FMT_YUVJ420P) {
- av_log(ctx, AV_LOG_ERROR, "cover image is not a YUV420 image\n");
- return AVERROR(EINVAL);
- }
- }
- return 0;
- }
- static const AVFilterPad cover_rect_inputs[] = {
- {
- .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- .config_props = config_input,
- .filter_frame = filter_frame,
- },
- { NULL }
- };
- static const AVFilterPad cover_rect_outputs[] = {
- {
- .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- },
- { NULL }
- };
- AVFilter ff_vf_cover_rect = {
- .name = "cover_rect",
- .description = NULL_IF_CONFIG_SMALL("Find and cover a user specified object."),
- .priv_size = sizeof(CoverContext),
- .init = init,
- .uninit = uninit,
- .query_formats = query_formats,
- .inputs = cover_rect_inputs,
- .outputs = cover_rect_outputs,
- .priv_class = &cover_rect_class,
- };
|