LCOV - code coverage report
Current view: top level - libavfilter - vf_mix.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 119 0.0 %
Date: 2017-12-11 04:34:20 Functions: 0 6 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2017 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             : 
      21             : #include "libavutil/avstring.h"
      22             : #include "libavutil/imgutils.h"
      23             : #include "libavutil/intreadwrite.h"
      24             : #include "libavutil/opt.h"
      25             : #include "libavutil/pixdesc.h"
      26             : 
      27             : #include "avfilter.h"
      28             : #include "formats.h"
      29             : #include "internal.h"
      30             : #include "framesync.h"
      31             : #include "video.h"
      32             : 
      33             : typedef struct MixContext {
      34             :     const AVClass *class;
      35             :     const AVPixFmtDescriptor *desc;
      36             :     char *weights_str;
      37             :     int nb_inputs;
      38             :     int duration;
      39             :     float *weights;
      40             :     float wfactor;
      41             : 
      42             :     int depth;
      43             :     int nb_planes;
      44             :     int linesize[4];
      45             :     int height[4];
      46             : 
      47             :     AVFrame **frames;
      48             :     FFFrameSync fs;
      49             : } MixContext;
      50             : 
      51           0 : static int query_formats(AVFilterContext *ctx)
      52             : {
      53           0 :     AVFilterFormats *pix_fmts = NULL;
      54             :     int fmt, ret;
      55             : 
      56           0 :     for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) {
      57           0 :         const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
      58           0 :         if (!(desc->flags & AV_PIX_FMT_FLAG_PAL ||
      59           0 :               desc->flags & AV_PIX_FMT_FLAG_HWACCEL ||
      60           0 :               desc->flags & AV_PIX_FMT_FLAG_BITSTREAM) &&
      61           0 :             (ret = ff_add_format(&pix_fmts, fmt)) < 0)
      62           0 :             return ret;
      63             :     }
      64             : 
      65           0 :     return ff_set_common_formats(ctx, pix_fmts);
      66             : }
      67             : 
      68           0 : static av_cold int init(AVFilterContext *ctx)
      69             : {
      70           0 :     MixContext *s = ctx->priv;
      71           0 :     char *p, *arg, *saveptr = NULL;
      72             :     int i, ret;
      73             : 
      74           0 :     s->frames = av_calloc(s->nb_inputs, sizeof(*s->frames));
      75           0 :     if (!s->frames)
      76           0 :         return AVERROR(ENOMEM);
      77             : 
      78           0 :     s->weights = av_calloc(s->nb_inputs, sizeof(*s->weights));
      79           0 :     if (!s->weights)
      80           0 :         return AVERROR(ENOMEM);
      81             : 
      82           0 :     for (i = 0; i < s->nb_inputs; i++) {
      83           0 :         AVFilterPad pad = { 0 };
      84             : 
      85           0 :         pad.type = AVMEDIA_TYPE_VIDEO;
      86           0 :         pad.name = av_asprintf("input%d", i);
      87           0 :         if (!pad.name)
      88           0 :             return AVERROR(ENOMEM);
      89             : 
      90           0 :         if ((ret = ff_insert_inpad(ctx, i, &pad)) < 0) {
      91           0 :             av_freep(&pad.name);
      92           0 :             return ret;
      93             :         }
      94             :     }
      95             : 
      96           0 :     p = s->weights_str;
      97           0 :     for (i = 0; i < s->nb_inputs; i++) {
      98           0 :         if (!(arg = av_strtok(p, " ", &saveptr)))
      99           0 :             break;
     100             : 
     101           0 :         p = NULL;
     102           0 :         sscanf(arg, "%f", &s->weights[i]);
     103           0 :         s->wfactor += s->weights[i];
     104             :     }
     105           0 :     s->wfactor = 1 / s->wfactor;
     106             : 
     107           0 :     return 0;
     108             : }
     109             : 
     110           0 : static int process_frame(FFFrameSync *fs)
     111             : {
     112           0 :     AVFilterContext *ctx = fs->parent;
     113           0 :     AVFilterLink *outlink = ctx->outputs[0];
     114           0 :     MixContext *s = fs->opaque;
     115           0 :     AVFrame **in = s->frames;
     116             :     AVFrame *out;
     117             :     int i, p, ret, x, y;
     118             : 
     119           0 :     for (i = 0; i < s->nb_inputs; i++) {
     120           0 :         if ((ret = ff_framesync_get_frame(&s->fs, i, &in[i], 0)) < 0)
     121           0 :             return ret;
     122             :     }
     123             : 
     124           0 :     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
     125           0 :     if (!out)
     126           0 :         return AVERROR(ENOMEM);
     127           0 :     out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base);
     128             : 
     129           0 :     if (s->depth <= 8) {
     130           0 :         for (p = 0; p < s->nb_planes; p++) {
     131           0 :             uint8_t *dst = out->data[p];
     132             : 
     133           0 :             for (y = 0; y < s->height[p]; y++) {
     134           0 :                 for (x = 0; x < s->linesize[p]; x++) {
     135           0 :                     int val = 0;
     136             : 
     137           0 :                     for (i = 0; i < s->nb_inputs; i++) {
     138           0 :                         uint8_t src = in[i]->data[p][y * s->linesize[p] + x];
     139             : 
     140           0 :                         val += src * s->weights[i];
     141             :                     }
     142             : 
     143           0 :                     dst[x] = val * s->wfactor;
     144             :                 }
     145             : 
     146           0 :                 dst += out->linesize[p];
     147             :             }
     148             :         }
     149             :     } else {
     150           0 :         for (p = 0; p < s->nb_planes; p++) {
     151           0 :             uint16_t *dst = (uint16_t *)out->data[p];
     152             : 
     153           0 :             for (y = 0; y < s->height[p]; y++) {
     154           0 :                 for (x = 0; x < s->linesize[p]; x++) {
     155           0 :                     int val = 0;
     156             : 
     157           0 :                     for (i = 0; i < s->nb_inputs; i++) {
     158           0 :                         uint16_t src = AV_RN16(in[i]->data[p] + y * s->linesize[p] + x * 2);
     159             : 
     160           0 :                         val += src * s->weights[i];
     161             :                     }
     162             : 
     163           0 :                     dst[x] = val * s->wfactor;
     164             :                 }
     165             : 
     166           0 :                 dst += out->linesize[p] / 2;
     167             :             }
     168             :         }
     169             :     }
     170             : 
     171           0 :     return ff_filter_frame(outlink, out);
     172             : }
     173             : 
     174           0 : static int config_output(AVFilterLink *outlink)
     175             : {
     176           0 :     AVFilterContext *ctx = outlink->src;
     177           0 :     MixContext *s = ctx->priv;
     178           0 :     AVRational time_base = ctx->inputs[0]->time_base;
     179           0 :     AVRational frame_rate = ctx->inputs[0]->frame_rate;
     180           0 :     AVFilterLink *inlink = ctx->inputs[0];
     181           0 :     int height = ctx->inputs[0]->h;
     182           0 :     int width = ctx->inputs[0]->w;
     183             :     FFFrameSyncIn *in;
     184             :     int i, ret;
     185             : 
     186           0 :     for (i = 1; i < s->nb_inputs; i++) {
     187           0 :         if (ctx->inputs[i]->h != height || ctx->inputs[i]->w != width) {
     188           0 :             av_log(ctx, AV_LOG_ERROR, "Input %d size (%dx%d) does not match input %d size (%dx%d).\n", i, ctx->inputs[i]->w, ctx->inputs[i]->h, 0, width, height);
     189           0 :             return AVERROR(EINVAL);
     190             :         }
     191             :     }
     192             : 
     193           0 :     s->desc = av_pix_fmt_desc_get(outlink->format);
     194           0 :     if (!s->desc)
     195           0 :         return AVERROR_BUG;
     196           0 :     s->nb_planes = av_pix_fmt_count_planes(outlink->format);
     197           0 :     s->depth = s->desc->comp[0].depth;
     198             : 
     199           0 :     outlink->w          = width;
     200           0 :     outlink->h          = height;
     201           0 :     outlink->time_base  = time_base;
     202           0 :     outlink->frame_rate = frame_rate;
     203             : 
     204           0 :     if ((ret = ff_framesync_init(&s->fs, ctx, s->nb_inputs)) < 0)
     205           0 :         return ret;
     206             : 
     207           0 :     in = s->fs.in;
     208           0 :     s->fs.opaque = s;
     209           0 :     s->fs.on_event = process_frame;
     210             : 
     211           0 :     if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0)
     212           0 :         return ret;
     213             : 
     214           0 :     s->height[1] = s->height[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h);
     215           0 :     s->height[0] = s->height[3] = inlink->h;
     216             : 
     217           0 :     for (i = 0; i < s->nb_inputs; i++) {
     218           0 :         AVFilterLink *inlink = ctx->inputs[i];
     219             : 
     220           0 :         in[i].time_base = inlink->time_base;
     221           0 :         in[i].sync   = 1;
     222           0 :         in[i].before = EXT_STOP;
     223           0 :         in[i].after  = (s->duration == 1 || (s->duration == 2 && i == 0)) ? EXT_STOP : EXT_INFINITY;
     224             :     }
     225             : 
     226           0 :     return ff_framesync_configure(&s->fs);
     227             : }
     228             : 
     229           0 : static av_cold void uninit(AVFilterContext *ctx)
     230             : {
     231           0 :     MixContext *s = ctx->priv;
     232             :     int i;
     233             : 
     234           0 :     ff_framesync_uninit(&s->fs);
     235           0 :     av_freep(&s->frames);
     236           0 :     av_freep(&s->weights);
     237             : 
     238           0 :     for (i = 0; i < ctx->nb_inputs; i++)
     239           0 :         av_freep(&ctx->input_pads[i].name);
     240           0 : }
     241             : 
     242           0 : static int activate(AVFilterContext *ctx)
     243             : {
     244           0 :     MixContext *s = ctx->priv;
     245           0 :     return ff_framesync_activate(&s->fs);
     246             : }
     247             : 
     248             : #define OFFSET(x) offsetof(MixContext, x)
     249             : #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
     250             : 
     251             : static const AVOption mix_options[] = {
     252             :     { "inputs", "set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64=2}, 2, INT_MAX, .flags = FLAGS },
     253             :     { "weights", "set weight for each input", OFFSET(weights_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, .flags = FLAGS },
     254             :     { "duration", "how to determine end of stream", OFFSET(duration), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, .flags = FLAGS, "duration" },
     255             :         { "longest",  "Duration of longest input",  0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "duration" },
     256             :         { "shortest", "Duration of shortest input", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "duration" },
     257             :         { "first",    "Duration of first input",    0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "duration" },
     258             :     { NULL },
     259             : };
     260             : 
     261             : static const AVFilterPad outputs[] = {
     262             :     {
     263             :         .name          = "default",
     264             :         .type          = AVMEDIA_TYPE_VIDEO,
     265             :         .config_props  = config_output,
     266             :     },
     267             :     { NULL }
     268             : };
     269             : 
     270             : AVFILTER_DEFINE_CLASS(mix);
     271             : 
     272             : AVFilter ff_vf_mix = {
     273             :     .name          = "mix",
     274             :     .description   = NULL_IF_CONFIG_SMALL("Mix video inputs."),
     275             :     .priv_size     = sizeof(MixContext),
     276             :     .priv_class    = &mix_class,
     277             :     .query_formats = query_formats,
     278             :     .outputs       = outputs,
     279             :     .init          = init,
     280             :     .uninit        = uninit,
     281             :     .activate      = activate,
     282             :     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
     283             : };

Generated by: LCOV version 1.13