Commit 3e1097b9 authored by Clément Bœsch's avatar Clément Bœsch

avfilter/fade: add color option.

Fixes Ticket #1822.
parent 78e150c5
...@@ -3809,7 +3809,8 @@ effect. Default is 0. ...@@ -3809,7 +3809,8 @@ effect. Default is 0.
@item nb_frames, n @item nb_frames, n
The number of frames for which the fade effect has to last. At the end of the The number of frames for which the fade effect has to last. At the end of the
fade-in effect the output video will have the same intensity as the input video, fade-in effect the output video will have the same intensity as the input video,
at the end of the fade-out transition the output video will be completely black. at the end of the fade-out transition the output video will be filled with the
selected @option{color}.
Default is 25. Default is 25.
@item alpha @item alpha
...@@ -3824,8 +3825,12 @@ whichever comes last. Default is 0. ...@@ -3824,8 +3825,12 @@ whichever comes last. Default is 0.
@item duration, d @item duration, d
The number of seconds for which the fade effect has to last. At the end of the The number of seconds for which the fade effect has to last. At the end of the
fade-in effect the output video will have the same intensity as the input video, fade-in effect the output video will have the same intensity as the input video,
at the end of the fade-out transition the output video will be completely black. at the end of the fade-out transition the output video will be filled with the
selected @option{color}.
If both duration and nb_frames are specified, duration is used. Default is 0. If both duration and nb_frames are specified, duration is used. Default is 0.
@item color, c
Specify the color of the fade. Default is "black".
@end table @end table
@subsection Examples @subsection Examples
...@@ -3856,9 +3861,9 @@ fade=in:0:25, fade=out:975:25 ...@@ -3856,9 +3861,9 @@ fade=in:0:25, fade=out:975:25
@end example @end example
@item @item
Make first 5 frames black, then fade in from frame 5-24: Make first 5 frames yellow, then fade in from frame 5-24:
@example @example
fade=in:5:20 fade=in:5:20:color=yellow
@end example @end example
@item @item
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
* based heavily on vf_negate.c by Bobby Bingham * based heavily on vf_negate.c by Bobby Bingham
*/ */
#include "libavutil/avassert.h"
#include "libavutil/avstring.h" #include "libavutil/avstring.h"
#include "libavutil/common.h" #include "libavutil/common.h"
#include "libavutil/eval.h" #include "libavutil/eval.h"
...@@ -61,6 +62,8 @@ typedef struct { ...@@ -61,6 +62,8 @@ typedef struct {
int alpha; int alpha;
uint64_t start_time, duration; uint64_t start_time, duration;
enum {VF_FADE_WAITING=0, VF_FADE_FADING, VF_FADE_DONE} fade_state; enum {VF_FADE_WAITING=0, VF_FADE_FADING, VF_FADE_DONE} fade_state;
uint8_t color_rgba[4]; ///< fade color
int black_fade; ///< if color_rgba is black
} FadeContext; } FadeContext;
static av_cold int init(AVFilterContext *ctx) static av_cold int init(AVFilterContext *ctx)
...@@ -89,11 +92,13 @@ static av_cold int init(AVFilterContext *ctx) ...@@ -89,11 +92,13 @@ static av_cold int init(AVFilterContext *ctx)
(s->duration / (double)AV_TIME_BASE),s->alpha); (s->duration / (double)AV_TIME_BASE),s->alpha);
} }
s->black_fade = !memcmp(s->color_rgba, "\x00\x00\x00\xff", 4);
return 0; return 0;
} }
static int query_formats(AVFilterContext *ctx) static int query_formats(AVFilterContext *ctx)
{ {
const FadeContext *s = ctx->priv;
static const enum AVPixelFormat pix_fmts[] = { static const enum AVPixelFormat pix_fmts[] = {
AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P,
AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
...@@ -105,8 +110,17 @@ static int query_formats(AVFilterContext *ctx) ...@@ -105,8 +110,17 @@ static int query_formats(AVFilterContext *ctx)
AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA,
AV_PIX_FMT_NONE AV_PIX_FMT_NONE
}; };
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
};
if (s->black_fade)
ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
else
ff_set_common_formats(ctx, ff_make_format_list(pix_fmts_rgb));
return 0; return 0;
} }
...@@ -138,6 +152,47 @@ static int config_props(AVFilterLink *inlink) ...@@ -138,6 +152,47 @@ static int config_props(AVFilterLink *inlink)
return 0; return 0;
} }
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;
}
static int filter_slice_luma(AVFilterContext *ctx, void *arg, int jobnr, static int filter_slice_luma(AVFilterContext *ctx, void *arg, int jobnr,
int nb_jobs) int nb_jobs)
{ {
...@@ -271,8 +326,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) ...@@ -271,8 +326,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
if (s->alpha) { if (s->alpha) {
ctx->internal->execute(ctx, filter_slice_alpha, frame, NULL, ctx->internal->execute(ctx, filter_slice_alpha, frame, NULL,
FFMIN(frame->height, ctx->graph->nb_threads)); FFMIN(frame->height, ctx->graph->nb_threads));
} 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));
} else { } else {
/* luma or rgb plane */ /* luma, or rgb plane in case of black */
ctx->internal->execute(ctx, filter_slice_luma, frame, NULL, ctx->internal->execute(ctx, filter_slice_luma, frame, NULL,
FFMIN(frame->height, ctx->graph->nb_threads)); FFMIN(frame->height, ctx->graph->nb_threads));
...@@ -315,6 +373,8 @@ static const AVOption fade_options[] = { ...@@ -315,6 +373,8 @@ static const AVOption fade_options[] = {
OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS }, OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "d", "Duration of the effect in seconds.", { "d", "Duration of the effect in seconds.",
OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS }, OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "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 },
{ NULL } { NULL }
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment