diff options
Diffstat (limited to 'libavfilter/vf_fade.c')
-rw-r--r-- | libavfilter/vf_fade.c | 228 |
1 files changed, 179 insertions, 49 deletions
diff --git a/libavfilter/vf_fade.c b/libavfilter/vf_fade.c index 9f748b8..7fe0dae 100644 --- a/libavfilter/vf_fade.c +++ b/libavfilter/vf_fade.c @@ -2,20 +2,20 @@ * Copyright (c) 2010 Brandon Mintern * Copyright (c) 2007 Bobby Bingham * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * 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. * - * Libav is distributed in the hope that it will be useful, + * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -25,46 +25,126 @@ * based heavily on vf_negate.c by Bobby Bingham */ +#include "libavutil/avstring.h" +#include "libavutil/eval.h" +#include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "drawutils.h" +#include "internal.h" + +#define R 0 +#define G 1 +#define B 2 +#define A 3 + +#define Y 0 +#define U 1 +#define V 2 typedef struct { + const AVClass *class; int factor, fade_per_frame; - unsigned int frame_index, start_frame, stop_frame; + unsigned int frame_index, start_frame, stop_frame, nb_frames; int hsub, vsub, bpp; + unsigned int black_level, black_level_scaled; + uint8_t is_packed_rgb; + uint8_t rgba_map[4]; + int alpha; + + char *type; } FadeContext; +#define OFFSET(x) offsetof(FadeContext, x) + +static const AVOption fade_options[] = { + { "type", "set the fade direction", OFFSET(type), AV_OPT_TYPE_STRING, {.str = "in" }, CHAR_MIN, CHAR_MAX }, + { "t", "set the fade direction", OFFSET(type), AV_OPT_TYPE_STRING, {.str = "in" }, CHAR_MIN, CHAR_MAX }, + { "start_frame", "set expression of frame to start fading", OFFSET(start_frame), AV_OPT_TYPE_INT, {.dbl = 0 }, 0, INT_MAX }, + { "s", "set expression of frame to start fading", OFFSET(start_frame), AV_OPT_TYPE_INT, {.dbl = 0 }, 0, INT_MAX }, + { "nb_frames", "set expression for fade duration in frames", OFFSET(nb_frames), AV_OPT_TYPE_INT, {.dbl = 25 }, 0, INT_MAX }, + { "n", "set expression for fade duration in frames", OFFSET(nb_frames), AV_OPT_TYPE_INT, {.dbl = 25 }, 0, INT_MAX }, + { "alpha", "fade alpha if it is available on the input", OFFSET(alpha), AV_OPT_TYPE_INT, {.dbl = 0 }, 0, 1 }, + {NULL}, +}; + +static const char *fade_get_name(void *ctx) +{ + return "fade"; +} + +static const AVClass fade_class = { + "FadeContext", + fade_get_name, + fade_options +}; + static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) { FadeContext *fade = ctx->priv; - unsigned int nb_frames; - char in_out[4]; + int ret = 0; + char *args1, *expr, *bufptr = NULL; - if (!args || - sscanf(args, " %3[^:]:%u:%u", in_out, &fade->start_frame, &nb_frames) != 3) { - av_log(ctx, AV_LOG_ERROR, - "Expected 3 arguments '(in|out):#:#':'%s'\n", args); - return AVERROR(EINVAL); + fade->class = &fade_class; + av_opt_set_defaults(fade); + + if (!(args1 = av_strdup(args))) { + ret = AVERROR(ENOMEM); + goto end; } - nb_frames = nb_frames ? nb_frames : 1; - fade->fade_per_frame = (1 << 16) / nb_frames; - if (!strcmp(in_out, "in")) + if (expr = av_strtok(args1, ":", &bufptr)) { + if (!(fade->type = av_strdup(expr))) { + ret = AVERROR(ENOMEM); + goto end; + } + } + if (expr = av_strtok(NULL, ":", &bufptr)) { + if ((ret = av_opt_set(fade, "start_frame", expr, 0)) < 0) { + av_log(ctx, AV_LOG_ERROR, + "Invalid value '%s' for start_frame option\n", expr); + return ret; + } + } + if (expr = av_strtok(NULL, ":", &bufptr)) { + if ((ret = av_opt_set(fade, "nb_frames", expr, 0)) < 0) { + av_log(ctx, AV_LOG_ERROR, + "Invalid value '%s' for nb_frames option\n", expr); + return ret; + } + } + + if (bufptr && (ret = av_set_options_string(fade, bufptr, "=", ":")) < 0) + goto end; + + fade->fade_per_frame = (1 << 16) / fade->nb_frames; + if (!strcmp(fade->type, "in")) fade->factor = 0; - else if (!strcmp(in_out, "out")) { + else if (!strcmp(fade->type, "out")) { fade->fade_per_frame = -fade->fade_per_frame; fade->factor = (1 << 16); } else { av_log(ctx, AV_LOG_ERROR, - "first argument must be 'in' or 'out':'%s'\n", in_out); - return AVERROR(EINVAL); + "Type argument must be 'in' or 'out' but '%s' was specified\n", fade->type); + ret = AVERROR(EINVAL); + goto end; } - fade->stop_frame = fade->start_frame + nb_frames; + fade->stop_frame = fade->start_frame + fade->nb_frames; av_log(ctx, AV_LOG_INFO, - "type:%s start_frame:%d nb_frames:%d\n", - in_out, fade->start_frame, nb_frames); - return 0; + "type:%s start_frame:%d nb_frames:%d alpha:%d\n", + fade->type, fade->start_frame, fade->nb_frames, fade->alpha); + +end: + av_free(args1); + return ret; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + FadeContext *fade = ctx->priv; + + av_freep(&fade->type); } static int query_formats(AVFilterContext *ctx) @@ -74,14 +154,31 @@ static int query_formats(AVFilterContext *ctx) PIX_FMT_YUV411P, PIX_FMT_YUV410P, PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ420P, PIX_FMT_YUV440P, PIX_FMT_YUVJ440P, + PIX_FMT_YUVA420P, PIX_FMT_RGB24, PIX_FMT_BGR24, + PIX_FMT_ARGB, PIX_FMT_ABGR, + PIX_FMT_RGBA, PIX_FMT_BGRA, PIX_FMT_NONE }; - avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); + avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts)); return 0; } +const static enum PixelFormat studio_level_pix_fmts[] = { + PIX_FMT_YUV444P, PIX_FMT_YUV422P, PIX_FMT_YUV420P, + PIX_FMT_YUV411P, PIX_FMT_YUV410P, + PIX_FMT_YUV440P, + PIX_FMT_NONE +}; + +static enum PixelFormat alpha_pix_fmts[] = { + PIX_FMT_YUVA420P, + PIX_FMT_ARGB, PIX_FMT_ABGR, + PIX_FMT_RGBA, PIX_FMT_BGRA, + PIX_FMT_NONE +}; + static int config_props(AVFilterLink *inlink) { FadeContext *fade = inlink->dst->priv; @@ -91,9 +188,37 @@ static int config_props(AVFilterLink *inlink) fade->vsub = pixdesc->log2_chroma_h; fade->bpp = av_get_bits_per_pixel(pixdesc) >> 3; + fade->alpha = fade->alpha ? ff_fmt_is_in(inlink->format, alpha_pix_fmts) : 0; + fade->is_packed_rgb = ff_fill_rgba_map(fade->rgba_map, inlink->format) >= 0; + + /* CCIR601/709 black level unless input is RGB or has alpha */ + fade->black_level = + ff_fmt_is_in(inlink->format, studio_level_pix_fmts) || fade->alpha ? 0 : 16; + /* 32768 = 1 << 15, it is an integer representation + * of 0.5 and is for rounding. */ + fade->black_level_scaled = (fade->black_level << 16) + 32768; return 0; } +static void fade_plane(int y, int h, int w, + int fade_factor, int black_level, int black_level_scaled, + uint8_t offset, uint8_t step, int bytes_per_plane, + uint8_t *data, int line_size) +{ + uint8_t *p; + int i, j; + + /* luma, alpha or rgb plane */ + for (i = 0; i < h; i++) { + p = data + offset + (y+i) * line_size; + for (j = 0; j < w * bytes_per_plane; j++) { + /* fade->factor is using 16 lower-order bits for decimal places. */ + *p = ((*p - black_level) * fade_factor + black_level_scaled) >> 16; + p+=step; + } + } +} + static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { FadeContext *fade = inlink->dst->priv; @@ -102,29 +227,33 @@ static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) int i, j, plane; if (fade->factor < UINT16_MAX) { - /* luma or rgb plane */ - for (i = 0; i < h; i++) { - p = outpic->data[0] + (y+i) * outpic->linesize[0]; - for (j = 0; j < inlink->w * fade->bpp; j++) { - /* fade->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 * fade->factor + 32768) >> 16; - p++; - } - } - - if (outpic->data[1] && outpic->data[2]) { - /* chroma planes */ - for (plane = 1; plane < 3; plane++) { - for (i = 0; i < h; i++) { - p = outpic->data[plane] + ((y+i) >> fade->vsub) * outpic->linesize[plane]; - for (j = 0; j < inlink->w >> fade->hsub; j++) { - /* 8421367 = ((128 << 1) + 1) << 15. It is an integer - * representation of 128.5. The .5 is for rounding - * purposes. */ - *p = ((*p - 128) * fade->factor + 8421367) >> 16; - p++; + if (fade->alpha) { + // alpha only + plane = fade->is_packed_rgb ? 0 : A; // alpha is on plane 0 for packed formats + // or plane 3 for planar formats + fade_plane(y, h, inlink->w, + fade->factor, fade->black_level, fade->black_level_scaled, + fade->is_packed_rgb ? fade->rgba_map[A] : 0, // alpha offset for packed formats + fade->is_packed_rgb ? 4 : 1, // pixstep for 8 bit packed formats + 1, outpic->data[plane], outpic->linesize[plane]); + } else { + /* luma or rgb plane */ + fade_plane(y, h, inlink->w, + fade->factor, fade->black_level, fade->black_level_scaled, + 0, 1, // offset & pixstep for Y plane or RGB packed format + fade->bpp, outpic->data[0], outpic->linesize[0]); + if (outpic->data[1] && outpic->data[2]) { + /* chroma planes */ + for (plane = 1; plane < 3; plane++) { + for (i = 0; i < h; i++) { + p = outpic->data[plane] + ((y+i) >> fade->vsub) * outpic->linesize[plane]; + for (j = 0; j < inlink->w >> fade->hsub; j++) { + /* 8421367 = ((128 << 1) + 1) << 15. It is an integer + * representation of 128.5. The .5 is for rounding + * purposes. */ + *p = ((*p - 128) * fade->factor + 8421367) >> 16; + p++; + } } } } @@ -149,12 +278,13 @@ static void end_frame(AVFilterLink *inlink) AVFilter avfilter_vf_fade = { .name = "fade", - .description = NULL_IF_CONFIG_SMALL("Fade in/out input video"), + .description = NULL_IF_CONFIG_SMALL("Fade in/out input video."), .init = init, + .uninit = uninit, .priv_size = sizeof(FadeContext), .query_formats = query_formats, - .inputs = (AVFilterPad[]) {{ .name = "default", + .inputs = (const AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_VIDEO, .config_props = config_props, .get_video_buffer = avfilter_null_get_video_buffer, @@ -164,7 +294,7 @@ AVFilter avfilter_vf_fade = { .min_perms = AV_PERM_READ | AV_PERM_WRITE, .rej_perms = AV_PERM_PRESERVE, }, { .name = NULL}}, - .outputs = (AVFilterPad[]) {{ .name = "default", + .outputs = (const AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_VIDEO, }, { .name = NULL}}, }; |