*/
#include "libavutil/common.h"
+#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "avfilter.h"
#include "formats.h"
#include "internal.h"
#include "video.h"
-typedef struct {
+#define FADE_IN 0
+#define FADE_OUT 1
+
+typedef struct FadeContext {
+ const AVClass *class;
+ int type;
int factor, fade_per_frame;
- unsigned int frame_index, start_frame, stop_frame;
+ int start_frame, nb_frames;
+ unsigned int frame_index, stop_frame;
int hsub, vsub, bpp;
} FadeContext;
-static av_cold int init(AVFilterContext *ctx, const char *args)
+static av_cold int init(AVFilterContext *ctx)
{
- FadeContext *fade = ctx->priv;
- unsigned int nb_frames;
- char in_out[4];
-
- 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);
- }
-
- nb_frames = nb_frames ? nb_frames : 1;
- fade->fade_per_frame = (1 << 16) / nb_frames;
- if (!strcmp(in_out, "in"))
- fade->factor = 0;
- else if (!strcmp(in_out, "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);
+ FadeContext *s = ctx->priv;
+
+ s->fade_per_frame = (1 << 16) / s->nb_frames;
+ if (s->type == FADE_IN) {
+ s->factor = 0;
+ } else if (s->type == FADE_OUT) {
+ s->fade_per_frame = -s->fade_per_frame;
+ s->factor = (1 << 16);
}
- fade->stop_frame = fade->start_frame + nb_frames;
+ s->stop_frame = s->start_frame + s->nb_frames;
av_log(ctx, AV_LOG_VERBOSE,
"type:%s start_frame:%d nb_frames:%d\n",
- in_out, fade->start_frame, nb_frames);
+ s->type == FADE_IN ? "in" : "out", s->start_frame,
+ s->nb_frames);
return 0;
}
static int config_props(AVFilterLink *inlink)
{
- FadeContext *fade = inlink->dst->priv;
- const AVPixFmtDescriptor *pixdesc = &av_pix_fmt_descriptors[inlink->format];
+ FadeContext *s = inlink->dst->priv;
+ const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(inlink->format);
- fade->hsub = pixdesc->log2_chroma_w;
- fade->vsub = pixdesc->log2_chroma_h;
+ s->hsub = pixdesc->log2_chroma_w;
+ s->vsub = pixdesc->log2_chroma_h;
- fade->bpp = av_get_bits_per_pixel(pixdesc) >> 3;
+ s->bpp = av_get_bits_per_pixel(pixdesc) >> 3;
return 0;
}
-static int draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
+static int filter_slice_luma(AVFilterContext *ctx, void *arg, int jobnr,
+ int nb_jobs)
{
- FadeContext *fade = inlink->dst->priv;
- AVFilterBufferRef *outpic = inlink->cur_buf;
- uint8_t *p;
+ FadeContext *s = ctx->priv;
+ AVFrame *frame = arg;
+ int slice_h = frame->height / nb_jobs;
+ int slice_start = jobnr * slice_h;
+ int slice_end = (jobnr == nb_jobs - 1) ? frame->height : (jobnr + 1) * slice_h;
+ 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. */
+ *p = (*p * s->factor + 32768) >> 16;
+ p++;
+ }
+ }
+
+ return 0;
+}
+
+static int filter_slice_chroma(AVFilterContext *ctx, void *arg, int jobnr,
+ int nb_jobs)
+{
+ FadeContext *s = ctx->priv;
+ AVFrame *frame = arg;
+ int slice_h = FFALIGN(frame->height / nb_jobs, 1 << s->vsub);
+ int slice_start = jobnr * slice_h;
+ int slice_end = (jobnr == nb_jobs - 1) ? frame->height : (jobnr + 1) * slice_h;
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;
+ for (plane = 1; plane < 3; plane++) {
+ for (i = slice_start; i < slice_end; i++) {
+ uint8_t *p = frame->data[plane] + (i >> s->vsub) * frame->linesize[plane];
+ for (j = 0; j < frame->width >> s->hsub; j++) {
+ /* 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;
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++;
- }
- }
- }
- }
}
- return ff_draw_slice(inlink->dst->outputs[0], y, h, slice_dir);
+ return 0;
}
-static int end_frame(AVFilterLink *inlink)
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
- FadeContext *fade = inlink->dst->priv;
- int ret;
+ AVFilterContext *ctx = inlink->dst;
+ FadeContext *s = ctx->priv;
- ret = ff_end_frame(inlink->dst->outputs[0]);
+ if (s->factor < UINT16_MAX) {
+ /* luma or rgb plane */
+ ctx->internal->execute(ctx, filter_slice_luma, frame, NULL,
+ FFMIN(frame->height, ctx->graph->nb_threads));
+
+ if (frame->data[1] && frame->data[2]) {
+ /* chroma planes */
+ ctx->internal->execute(ctx, filter_slice_chroma, frame, NULL,
+ FFMIN(frame->height, ctx->graph->nb_threads));
+ }
+ }
- if (fade->frame_index >= fade->start_frame &&
- fade->frame_index <= fade->stop_frame)
- fade->factor += fade->fade_per_frame;
- fade->factor = av_clip_uint16(fade->factor);
- fade->frame_index++;
+ if (s->frame_index >= s->start_frame &&
+ s->frame_index <= s->stop_frame)
+ s->factor += s->fade_per_frame;
+ s->factor = av_clip_uint16(s->factor);
+ s->frame_index++;
- return ret;
+ return ff_filter_frame(inlink->dst->outputs[0], frame);
}
+#define OFFSET(x) offsetof(FadeContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM
+static const AVOption options[] = {
+ { "type", "'in' or 'out' for fade-in/fade-out", OFFSET(type), AV_OPT_TYPE_INT, { .i64 = FADE_IN }, FADE_IN, FADE_OUT, FLAGS, "type" },
+ { "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 },
+ { "nb_frames", "Number of frames to which the effect should be applied.",
+ OFFSET(nb_frames), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, FLAGS },
+ { NULL },
+};
+
+static const AVClass fade_class = {
+ .class_name = "fade",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
static const AVFilterPad avfilter_vf_fade_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.config_props = config_props,
.get_video_buffer = ff_null_get_video_buffer,
- .start_frame = ff_null_start_frame,
- .draw_slice = draw_slice,
- .end_frame = end_frame,
- .min_perms = AV_PERM_READ | AV_PERM_WRITE,
- .rej_perms = AV_PERM_PRESERVE,
+ .filter_frame = filter_frame,
+ .needs_writable = 1,
},
{ NULL }
};
{ NULL }
};
-AVFilter avfilter_vf_fade = {
+AVFilter ff_vf_fade = {
.name = "fade",
.description = NULL_IF_CONFIG_SMALL("Fade in/out input video"),
.init = init,
.priv_size = sizeof(FadeContext),
+ .priv_class = &fade_class,
.query_formats = query_formats,
.inputs = avfilter_vf_fade_inputs,
.outputs = avfilter_vf_fade_outputs,
+ .flags = AVFILTER_FLAG_SLICE_THREADS,
};