vf_fade.c 15.4 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

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

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

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

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

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

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

B
Brandon Mintern 已提交
99 100
static int query_formats(AVFilterContext *ctx)
{
C
Clément Bœsch 已提交
101
    const FadeContext *s = ctx->priv;
102 103 104 105 106
    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,
107
        AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P,
108
        AV_PIX_FMT_RGB24,    AV_PIX_FMT_BGR24,
109 110
        AV_PIX_FMT_ARGB,     AV_PIX_FMT_ABGR,
        AV_PIX_FMT_RGBA,     AV_PIX_FMT_BGRA,
111
        AV_PIX_FMT_NONE
B
Brandon Mintern 已提交
112
    };
C
Clément Bœsch 已提交
113 114 115 116 117 118
    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 已提交
119

C
Clément Bœsch 已提交
120 121 122 123
    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 已提交
124 125 126
    return 0;
}

127 128 129 130 131
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
132 133
};

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

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

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

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

C
Clément Bœsch 已提交
155 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
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 已提交
196 197
static int filter_slice_luma(AVFilterContext *ctx, void *arg, int jobnr,
                             int nb_jobs)
B
Brandon Mintern 已提交
198
{
A
Anton Khirnov 已提交
199 200
    FadeContext *s = ctx->priv;
    AVFrame *frame = arg;
M
Michael Niedermayer 已提交
201 202
    int slice_start = (frame->height *  jobnr   ) / nb_jobs;
    int slice_end   = (frame->height * (jobnr+1)) / nb_jobs;
A
Anton Khirnov 已提交
203 204 205 206 207 208 209 210
    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. */
211
            *p = ((*p - s->black_level) * s->factor + s->black_level_scaled) >> 16;
A
Anton Khirnov 已提交
212 213 214 215 216 217 218 219 220 221 222 223
            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 已提交
224
    int i, j, plane;
225
    const int width = FF_CEIL_RSHIFT(frame->width, s->hsub);
M
Michael Niedermayer 已提交
226 227 228
    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 已提交
229

A
Anton Khirnov 已提交
230 231
    for (plane = 1; plane < 3; plane++) {
        for (i = slice_start; i < slice_end; i++) {
M
Michael Niedermayer 已提交
232
            uint8_t *p = frame->data[plane] + i * frame->linesize[plane];
233
            for (j = 0; j < width; j++) {
A
Anton Khirnov 已提交
234 235 236 237
                /* 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 已提交
238 239 240
                p++;
            }
        }
A
Anton Khirnov 已提交
241 242 243 244 245
    }

    return 0;
}

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
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 已提交
271 272 273 274
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
    AVFilterContext *ctx = inlink->dst;
    FadeContext *s       = ctx->priv;
275 276 277
    double frame_timestamp = frame->pts == AV_NOPTS_VALUE ? -1 : frame->pts * av_q2d(inlink->time_base);

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

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

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

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

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

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

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

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

345
    s->frame_index++;
346

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

350

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

static const AVOption fade_options[] = {
355
    { "type", "'in' or 'out' for fade-in/fade-out", OFFSET(type), AV_OPT_TYPE_INT, { .i64 = FADE_IN }, FADE_IN, FADE_OUT, FLAGS, "type" },
356
    { "t",    "'in' or 'out' for fade-in/fade-out", OFFSET(type), AV_OPT_TYPE_INT, { .i64 = FADE_IN }, FADE_IN, FADE_OUT, FLAGS, "type" },
357 358 359 360
        { "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 },
361 362
    { "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 },
363
    { "nb_frames",   "Number of frames to which the effect should be applied.",
364 365 366 367
                                                    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 },
368 369 370 371 372 373 374 375
    { "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 已提交
376 377
    { "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 已提交
378
    { NULL }
379 380
};

381
AVFILTER_DEFINE_CLASS(fade);
382

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

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

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