vf_fade.c 15.5 KB
Newer Older
B
Brandon Mintern 已提交
1 2 3 4
/*
 * Copyright (c) 2010 Brandon Mintern
 * Copyright (c) 2007 Bobby Bingham
 *
5
 * This file is part of FFmpeg.
B
Brandon Mintern 已提交
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
B
Brandon Mintern 已提交
8 9 10 11
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
B
Brandon Mintern 已提交
13 14 15 16 17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
B
Brandon Mintern 已提交
19 20 21 22 23 24 25 26 27
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
 * @file
 * video fade filter
 * based heavily on vf_negate.c by Bobby Bingham
 */

C
Clément Bœsch 已提交
28
#include "libavutil/avassert.h"
29
#include "libavutil/avstring.h"
30
#include "libavutil/common.h"
31 32
#include "libavutil/eval.h"
#include "libavutil/opt.h"
B
Brandon Mintern 已提交
33 34
#include "libavutil/pixdesc.h"
#include "avfilter.h"
35
#include "drawutils.h"
36
#include "formats.h"
37
#include "internal.h"
38
#include "video.h"
B
Brandon Mintern 已提交
39

40 41 42 43 44 45 46 47 48
#define R 0
#define G 1
#define B 2
#define A 3

#define Y 0
#define U 1
#define V 2

49 50 51
#define FADE_IN  0
#define FADE_OUT 1

V
Vittorio Giovara 已提交
52
typedef struct FadeContext {
53
    const AVClass *class;
54
    int type;
B
Brandon Mintern 已提交
55
    int factor, fade_per_frame;
56
    int start_frame, nb_frames;
B
Brandon Mintern 已提交
57
    int hsub, vsub, bpp;
58
    unsigned int black_level, black_level_scaled;
59 60 61
    uint8_t is_packed_rgb;
    uint8_t rgba_map[4];
    int alpha;
62 63
    uint64_t start_time, duration;
    enum {VF_FADE_WAITING=0, VF_FADE_FADING, VF_FADE_DONE} fade_state;
C
Clément Bœsch 已提交
64 65
    uint8_t color_rgba[4];  ///< fade color
    int black_fade;         ///< if color_rgba is black
B
Brandon Mintern 已提交
66 67
} FadeContext;

68
static av_cold int init(AVFilterContext *ctx)
B
Brandon Mintern 已提交
69
{
70
    FadeContext *s = ctx->priv;
B
Brandon Mintern 已提交
71

72
    s->fade_per_frame = (1 << 16) / s->nb_frames;
73
    s->fade_state = VF_FADE_WAITING;
74

75
    if (s->duration != 0) {
76
        // If duration (seconds) is non-zero, assume that we are not fading based on frames
77
        s->nb_frames = 0; // Mostly to clean up logging
78 79 80
    }

    // Choose what to log. If both time-based and frame-based options, both lines will be in the log
81
    if (s->start_frame || s->nb_frames) {
82 83
        av_log(ctx, AV_LOG_VERBOSE,
               "type:%s start_frame:%d nb_frames:%d alpha:%d\n",
84 85
               s->type == FADE_IN ? "in" : "out", s->start_frame,
               s->nb_frames,s->alpha);
86
    }
87
    if (s->start_time || s->duration) {
88 89
        av_log(ctx, AV_LOG_VERBOSE,
               "type:%s start_time:%f duration:%f alpha:%d\n",
90 91
               s->type == FADE_IN ? "in" : "out", (s->start_time / (double)AV_TIME_BASE),
               (s->duration / (double)AV_TIME_BASE),s->alpha);
B
Brandon Mintern 已提交
92 93
    }

C
Clément Bœsch 已提交
94
    s->black_fade = !memcmp(s->color_rgba, "\x00\x00\x00\xff", 4);
95
    return 0;
96 97
}

B
Brandon Mintern 已提交
98 99
static int query_formats(AVFilterContext *ctx)
{
C
Clément Bœsch 已提交
100
    const FadeContext *s = ctx->priv;
101 102 103 104 105
    static const enum AVPixelFormat pix_fmts[] = {
        AV_PIX_FMT_YUV444P,  AV_PIX_FMT_YUV422P,  AV_PIX_FMT_YUV420P,
        AV_PIX_FMT_YUV411P,  AV_PIX_FMT_YUV410P,
        AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
        AV_PIX_FMT_YUV440P,  AV_PIX_FMT_YUVJ440P,
106
        AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P,
107
        AV_PIX_FMT_RGB24,    AV_PIX_FMT_BGR24,
108 109
        AV_PIX_FMT_ARGB,     AV_PIX_FMT_ABGR,
        AV_PIX_FMT_RGBA,     AV_PIX_FMT_BGRA,
110
        AV_PIX_FMT_NONE
B
Brandon Mintern 已提交
111
    };
C
Clément Bœsch 已提交
112 113 114 115 116 117
    static const enum AVPixelFormat pix_fmts_rgb[] = {
        AV_PIX_FMT_RGB24,    AV_PIX_FMT_BGR24,
        AV_PIX_FMT_ARGB,     AV_PIX_FMT_ABGR,
        AV_PIX_FMT_RGBA,     AV_PIX_FMT_BGRA,
        AV_PIX_FMT_NONE
    };
B
Brandon Mintern 已提交
118

C
Clément Bœsch 已提交
119 120 121 122
    if (s->black_fade)
        ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
    else
        ff_set_common_formats(ctx, ff_make_format_list(pix_fmts_rgb));
B
Brandon Mintern 已提交
123 124 125
    return 0;
}

126 127 128 129 130
const static enum AVPixelFormat studio_level_pix_fmts[] = {
    AV_PIX_FMT_YUV444P,  AV_PIX_FMT_YUV422P,  AV_PIX_FMT_YUV420P,
    AV_PIX_FMT_YUV411P,  AV_PIX_FMT_YUV410P,
    AV_PIX_FMT_YUV440P,
    AV_PIX_FMT_NONE
131 132
};

B
Brandon Mintern 已提交
133 134
static int config_props(AVFilterLink *inlink)
{
135
    FadeContext *s = inlink->dst->priv;
136
    const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(inlink->format);
B
Brandon Mintern 已提交
137

138 139
    s->hsub = pixdesc->log2_chroma_w;
    s->vsub = pixdesc->log2_chroma_h;
B
Brandon Mintern 已提交
140

141 142 143
    s->bpp = pixdesc->flags & AV_PIX_FMT_FLAG_PLANAR ?
             1 :
             av_get_bits_per_pixel(pixdesc) >> 3;
144
    s->alpha &= !!(pixdesc->flags & AV_PIX_FMT_FLAG_ALPHA);
145
    s->is_packed_rgb = ff_fill_rgba_map(s->rgba_map, inlink->format) >= 0;
146

147
    /* use CCIR601/709 black level for studio-level pixel non-alpha components */
148 149
    s->black_level =
            ff_fmt_is_in(inlink->format, studio_level_pix_fmts) && !s->alpha ? 16 : 0;
150 151
    /* 32768 = 1 << 15, it is an integer representation
     * of 0.5 and is for rounding. */
152
    s->black_level_scaled = (s->black_level << 16) + 32768;
B
Brandon Mintern 已提交
153 154 155
    return 0;
}

C
Clément Bœsch 已提交
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
static av_always_inline void filter_rgb(FadeContext *s, const AVFrame *frame,
                                        int slice_start, int slice_end,
                                        int do_alpha, int step)
{
    int i, j;
    const uint8_t r_idx  = s->rgba_map[R];
    const uint8_t g_idx  = s->rgba_map[G];
    const uint8_t b_idx  = s->rgba_map[B];
    const uint8_t a_idx  = s->rgba_map[A];
    const uint8_t *c = s->color_rgba;

    for (i = slice_start; i < slice_end; i++) {
        uint8_t *p = frame->data[0] + i * frame->linesize[0];
        for (j = 0; j < frame->width; j++) {
#define INTERP(c_name, c_idx) av_clip_uint8(((c[c_idx]<<16) + ((int)p[c_name] - (int)c[c_idx]) * s->factor + (1<<15)) >> 16)
            p[r_idx] = INTERP(r_idx, 0);
            p[g_idx] = INTERP(g_idx, 1);
            p[b_idx] = INTERP(b_idx, 2);
            if (do_alpha)
                p[a_idx] = INTERP(a_idx, 3);
            p += step;
        }
    }
}

static int filter_slice_rgb(AVFilterContext *ctx, void *arg, int jobnr,
                            int nb_jobs)
{
    FadeContext *s = ctx->priv;
    AVFrame *frame = arg;
    int slice_start = (frame->height *  jobnr   ) / nb_jobs;
    int slice_end   = (frame->height * (jobnr+1)) / nb_jobs;

    if      (s->alpha)    filter_rgb(s, frame, slice_start, slice_end, 1, 4);
    else if (s->bpp == 3) filter_rgb(s, frame, slice_start, slice_end, 0, 3);
    else if (s->bpp == 4) filter_rgb(s, frame, slice_start, slice_end, 0, 4);
    else                  av_assert0(0);

    return 0;
}

A
Anton Khirnov 已提交
197 198
static int filter_slice_luma(AVFilterContext *ctx, void *arg, int jobnr,
                             int nb_jobs)
B
Brandon Mintern 已提交
199
{
A
Anton Khirnov 已提交
200 201
    FadeContext *s = ctx->priv;
    AVFrame *frame = arg;
M
Michael Niedermayer 已提交
202 203
    int slice_start = (frame->height *  jobnr   ) / nb_jobs;
    int slice_end   = (frame->height * (jobnr+1)) / nb_jobs;
A
Anton Khirnov 已提交
204 205 206 207 208 209 210 211
    int i, j;

    for (i = slice_start; i < slice_end; i++) {
        uint8_t *p = frame->data[0] + i * frame->linesize[0];
        for (j = 0; j < frame->width * s->bpp; j++) {
            /* s->factor is using 16 lower-order bits for decimal
             * places. 32768 = 1 << 15, it is an integer representation
             * of 0.5 and is for rounding. */
212
            *p = ((*p - s->black_level) * s->factor + s->black_level_scaled) >> 16;
A
Anton Khirnov 已提交
213 214 215 216 217 218 219 220 221 222 223 224
            p++;
        }
    }

    return 0;
}

static int filter_slice_chroma(AVFilterContext *ctx, void *arg, int jobnr,
                               int nb_jobs)
{
    FadeContext *s = ctx->priv;
    AVFrame *frame = arg;
B
Brandon Mintern 已提交
225
    int i, j, plane;
226
    const int width = FF_CEIL_RSHIFT(frame->width, s->hsub);
M
Michael Niedermayer 已提交
227 228 229
    const int height= FF_CEIL_RSHIFT(frame->height, s->vsub);
    int slice_start = (height *  jobnr   ) / nb_jobs;
    int slice_end   = (height * (jobnr+1)) / nb_jobs;
B
Brandon Mintern 已提交
230

A
Anton Khirnov 已提交
231 232
    for (plane = 1; plane < 3; plane++) {
        for (i = slice_start; i < slice_end; i++) {
M
Michael Niedermayer 已提交
233
            uint8_t *p = frame->data[plane] + i * frame->linesize[plane];
234
            for (j = 0; j < width; j++) {
A
Anton Khirnov 已提交
235 236 237 238
                /* 8421367 = ((128 << 1) + 1) << 15. It is an integer
                 * representation of 128.5. The .5 is for rounding
                 * purposes. */
                *p = ((*p - 128) * s->factor + 8421367) >> 16;
B
Brandon Mintern 已提交
239 240 241
                p++;
            }
        }
A
Anton Khirnov 已提交
242 243 244 245 246
    }

    return 0;
}

247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
static int filter_slice_alpha(AVFilterContext *ctx, void *arg, int jobnr,
                              int nb_jobs)
{
    FadeContext *s = ctx->priv;
    AVFrame *frame = arg;
    int plane = s->is_packed_rgb ? 0 : A;
    int slice_start = (frame->height *  jobnr   ) / nb_jobs;
    int slice_end   = (frame->height * (jobnr+1)) / nb_jobs;
    int i, j;

    for (i = slice_start; i < slice_end; i++) {
        uint8_t *p = frame->data[plane] + i * frame->linesize[plane] + s->is_packed_rgb*s->rgba_map[A];
        int step = s->is_packed_rgb ? 4 : 1;
        for (j = 0; j < frame->width; j++) {
            /* s->factor is using 16 lower-order bits for decimal
             * places. 32768 = 1 << 15, it is an integer representation
             * of 0.5 and is for rounding. */
            *p = ((*p - s->black_level) * s->factor + s->black_level_scaled) >> 16;
            p += step;
        }
    }

    return 0;
}

A
Anton Khirnov 已提交
272 273 274 275
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
    AVFilterContext *ctx = inlink->dst;
    FadeContext *s       = ctx->priv;
276 277 278
    double frame_timestamp = frame->pts == AV_NOPTS_VALUE ? -1 : frame->pts * av_q2d(inlink->time_base);

    // Calculate Fade assuming this is a Fade In
279 280
    if (s->fade_state == VF_FADE_WAITING) {
        s->factor=0;
281 282
        if (frame_timestamp >= s->start_time/(double)AV_TIME_BASE
            && inlink->frame_count >= s->start_frame) {
283
            // Time to start fading
284
            s->fade_state = VF_FADE_FADING;
285 286

            // Save start time in case we are starting based on frames and fading based on time
287
            if (s->start_time == 0 && s->start_frame != 0) {
288
                s->start_time = frame_timestamp*(double)AV_TIME_BASE;
289 290 291
            }

            // Save start frame in case we are starting based on time and fading based on frames
292
            if (s->start_time != 0 && s->start_frame == 0) {
293
                s->start_frame = inlink->frame_count;
294 295 296
            }
        }
    }
297 298
    if (s->fade_state == VF_FADE_FADING) {
        if (s->duration == 0) {
299
            // Fading based on frame count
300
            s->factor = (inlink->frame_count - s->start_frame) * s->fade_per_frame;
301
            if (inlink->frame_count > s->start_frame + s->nb_frames) {
302
                s->fade_state = VF_FADE_DONE;
303 304 305 306
            }

        } else {
            // Fading based on duration
307
            s->factor = (frame_timestamp - s->start_time/(double)AV_TIME_BASE)
308
                            * (float) UINT16_MAX / (s->duration/(double)AV_TIME_BASE);
309 310
            if (frame_timestamp > s->start_time/(double)AV_TIME_BASE
                                  + s->duration/(double)AV_TIME_BASE) {
311
                s->fade_state = VF_FADE_DONE;
312 313 314
            }
        }
    }
315 316
    if (s->fade_state == VF_FADE_DONE) {
        s->factor=UINT16_MAX;
317 318
    }

319
    s->factor = av_clip_uint16(s->factor);
320 321

    // Invert fade_factor if Fading Out
322
    if (s->type == FADE_OUT) {
323
        s->factor=UINT16_MAX-s->factor;
324
    }
B
Brandon Mintern 已提交
325

326 327
    if (s->factor < UINT16_MAX) {
        if (s->alpha) {
328 329
            ctx->internal->execute(ctx, filter_slice_alpha, frame, NULL,
                                FFMIN(frame->height, ctx->graph->nb_threads));
C
Clément Bœsch 已提交
330 331 332
        } else if (s->is_packed_rgb && !s->black_fade) {
            ctx->internal->execute(ctx, filter_slice_rgb, frame, NULL,
                                   FFMIN(frame->height, ctx->graph->nb_threads));
333
        } else {
C
Clément Bœsch 已提交
334
            /* luma, or rgb plane in case of black */
335 336 337
            ctx->internal->execute(ctx, filter_slice_luma, frame, NULL,
                                FFMIN(frame->height, ctx->graph->nb_threads));

A
Anton Khirnov 已提交
338
            if (frame->data[1] && frame->data[2]) {
339
                /* chroma planes */
340 341
                ctx->internal->execute(ctx, filter_slice_chroma, frame, NULL,
                                    FFMIN(frame->height, ctx->graph->nb_threads));
B
Brandon Mintern 已提交
342 343 344 345
            }
        }
    }

A
Anton Khirnov 已提交
346
    return ff_filter_frame(inlink->dst->outputs[0], frame);
B
Brandon Mintern 已提交
347 348
}

349

350
#define OFFSET(x) offsetof(FadeContext, x)
351 352 353
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM

static const AVOption fade_options[] = {
354
    { "type", "'in' or 'out' for fade-in/fade-out", OFFSET(type), AV_OPT_TYPE_INT, { .i64 = FADE_IN }, FADE_IN, FADE_OUT, FLAGS, "type" },
355
    { "t",    "'in' or 'out' for fade-in/fade-out", OFFSET(type), AV_OPT_TYPE_INT, { .i64 = FADE_IN }, FADE_IN, FADE_OUT, FLAGS, "type" },
356 357 358 359
        { "in",  "fade-in",  0, AV_OPT_TYPE_CONST, { .i64 = FADE_IN },  .unit = "type" },
        { "out", "fade-out", 0, AV_OPT_TYPE_CONST, { .i64 = FADE_OUT }, .unit = "type" },
    { "start_frame", "Number of the first frame to which to apply the effect.",
                                                    OFFSET(start_frame), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
360 361
    { "s",           "Number of the first frame to which to apply the effect.",
                                                    OFFSET(start_frame), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
362
    { "nb_frames",   "Number of frames to which the effect should be applied.",
363 364 365 366
                                                    OFFSET(nb_frames),   AV_OPT_TYPE_INT, { .i64 = 25 }, 0, INT_MAX, FLAGS },
    { "n",           "Number of frames to which the effect should be applied.",
                                                    OFFSET(nb_frames),   AV_OPT_TYPE_INT, { .i64 = 25 }, 0, INT_MAX, FLAGS },
    { "alpha",       "fade alpha if it is available on the input", OFFSET(alpha),       AV_OPT_TYPE_INT, {.i64 = 0    }, 0,       1, FLAGS },
367 368 369 370 371 372 373 374
    { "start_time",  "Number of seconds of the beginning of the effect.",
                                                    OFFSET(start_time),  AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
    { "st",          "Number of seconds of the beginning of the effect.",
                                                    OFFSET(start_time),  AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
    { "duration",    "Duration of the effect in seconds.",
                                                    OFFSET(duration),    AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
    { "d",           "Duration of the effect in seconds.",
                                                    OFFSET(duration),    AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
C
Clément Bœsch 已提交
375 376
    { "color",       "set color",                   OFFSET(color_rgba),  AV_OPT_TYPE_COLOR,    {.str = "black"}, CHAR_MIN, CHAR_MAX, FLAGS },
    { "c",           "set color",                   OFFSET(color_rgba),  AV_OPT_TYPE_COLOR,    {.str = "black"}, CHAR_MIN, CHAR_MAX, FLAGS },
P
Paul B Mahol 已提交
377
    { NULL }
378 379
};

380
AVFILTER_DEFINE_CLASS(fade);
381

382 383
static const AVFilterPad avfilter_vf_fade_inputs[] = {
    {
P
Paul B Mahol 已提交
384 385 386 387 388
        .name           = "default",
        .type           = AVMEDIA_TYPE_VIDEO,
        .config_props   = config_props,
        .filter_frame   = filter_frame,
        .needs_writable = 1,
389 390 391 392 393 394 395 396 397 398 399 400
    },
    { NULL }
};

static const AVFilterPad avfilter_vf_fade_outputs[] = {
    {
        .name = "default",
        .type = AVMEDIA_TYPE_VIDEO,
    },
    { NULL }
};

401
AVFilter ff_vf_fade = {
B
Brandon Mintern 已提交
402
    .name          = "fade",
403
    .description   = NULL_IF_CONFIG_SMALL("Fade in/out input video."),
B
Brandon Mintern 已提交
404 405
    .init          = init,
    .priv_size     = sizeof(FadeContext),
406
    .priv_class    = &fade_class,
B
Brandon Mintern 已提交
407
    .query_formats = query_formats,
P
Paul B Mahol 已提交
408 409 410
    .inputs        = avfilter_vf_fade_inputs,
    .outputs       = avfilter_vf_fade_outputs,
    .flags         = AVFILTER_FLAG_SLICE_THREADS,
B
Brandon Mintern 已提交
411
};