diff --git a/Changelog b/Changelog index 486978f2bb..5890aa79d2 100644 --- a/Changelog +++ b/Changelog @@ -60,6 +60,7 @@ version : - rename aspect filter to setdar, and pixelaspect to setsar - IEC 61937 demuxer - Mobotix .mxg demuxer +- frei0r source added version 0.6: diff --git a/doc/filters.texi b/doc/filters.texi index 1cba2d684d..c939b8766c 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -283,6 +283,7 @@ The following command: will convert the input video to the format "yuv420p". +@anchor{frei0r} @section frei0r Apply a frei0r effect to the input video. @@ -869,6 +870,34 @@ respectively 352 and 288 (corresponding to the CIF size format). timebase. The expression can contain the constants "PI", "E", "PHI", "AVTB" (the default timebase), and defaults to the value "AVTB". +@section frei0r_src + +Provide a frei0r source. + +To enable compilation of this filter you need to install the frei0r +header and configure FFmpeg with --enable-frei0r. + +The source supports the syntax: +@example +@var{size}:@var{rate}:@var{src_name}[@{=|:@}@var{param1}:@var{param2}:...:@var{paramN}] +@end example + +@var{size} is the size of the video to generate, may be a string of the +form @var{width}x@var{height} or a frame size abbreviation. +@var{rate} is the rate of the video to generate, may be a string of +the form @var{num}/@var{den} or a frame rate abbreviation. +@var{src_name} is the name to the frei0r source to load. For more +information regarding frei0r and how to set the parameters read the +section "frei0r" (@pxref{frei0r}) in the description of the video +filters. + +Some examples follow: +@example +# generate a frei0r partik0l source with size 200x200 and framerate 10 +# which is overlayed on the overlay filter main input +frei0r_src=200x200:10:partik0l=1234 [overlay]; [in][overlay] overlay +@end example + @c man end VIDEO SOURCES @chapter Video Sinks diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 210510f668..792e845838 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -46,6 +46,7 @@ OBJS-$(CONFIG_YADIF_FILTER) += vf_yadif.o OBJS-$(CONFIG_BUFFER_FILTER) += vsrc_buffer.o OBJS-$(CONFIG_COLOR_FILTER) += vf_pad.o +OBJS-$(CONFIG_FREI0R_SRC_FILTER) += vf_frei0r.o OBJS-$(CONFIG_NULLSRC_FILTER) += vsrc_nullsrc.o OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index c3067b85a6..4ecb00eb21 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -67,6 +67,7 @@ void avfilter_register_all(void) REGISTER_FILTER (BUFFER, buffer, vsrc); REGISTER_FILTER (COLOR, color, vsrc); + REGISTER_FILTER (FREI0R, frei0r_src, vsrc); REGISTER_FILTER (NULLSRC, nullsrc, vsrc); REGISTER_FILTER (NULLSINK, nullsink, vsink); diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index c5a52e8ce6..f7aaf7fb6d 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -25,7 +25,7 @@ #include "libavutil/avutil.h" #define LIBAVFILTER_VERSION_MAJOR 1 -#define LIBAVFILTER_VERSION_MINOR 65 +#define LIBAVFILTER_VERSION_MINOR 66 #define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ diff --git a/libavfilter/vf_frei0r.c b/libavfilter/vf_frei0r.c index a0e27bb62f..235b37a776 100644 --- a/libavfilter/vf_frei0r.c +++ b/libavfilter/vf_frei0r.c @@ -27,6 +27,7 @@ #include #include #include "libavutil/avstring.h" +#include "libavcore/imgutils.h" #include "libavcore/parseutils.h" #include "avfilter.h" @@ -54,6 +55,11 @@ typedef struct Frei0rContext { f0r_destruct_f destruct; f0r_deinit_f deinit; char params[256]; + + /* only used by the source */ + int w, h; + AVRational time_base; + uint64_t pts; } Frei0rContext; static void *load_sym(AVFilterContext *ctx, const char *sym_name) @@ -197,18 +203,14 @@ static void *load_path(AVFilterContext *ctx, const char *prefix, const char *nam return dlopen(path, RTLD_NOW|RTLD_LOCAL); } -static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) +static av_cold int frei0r_init(AVFilterContext *ctx, + const char *dl_name, int type) { Frei0rContext *frei0r = ctx->priv; f0r_init_f f0r_init; f0r_get_plugin_info_f f0r_get_plugin_info; f0r_plugin_info_t *pi; - char dl_name[1024], *path; - - *frei0r->params = 0; - - if (args) - sscanf(args, "%1023[^:]:%255c", dl_name, frei0r->params); + char *path; /* see: http://piksel.org/frei0r/1.2/spec/1.2/spec/group__pluglocations.html */ if ((path = av_strdup(getenv("FREI0R_PATH")))) { @@ -250,9 +252,10 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) f0r_get_plugin_info(&frei0r->plugin_info); pi = &frei0r->plugin_info; - if (pi->plugin_type != F0R_PLUGIN_TYPE_FILTER) { + if (pi->plugin_type != type) { av_log(ctx, AV_LOG_ERROR, - "Invalid type '%s' for the plugin, a filter plugin was expected\n", + "Invalid type '%s' for the plugin\n", + pi->plugin_type == F0R_PLUGIN_TYPE_FILTER ? "filter" : pi->plugin_type == F0R_PLUGIN_TYPE_SOURCE ? "source" : pi->plugin_type == F0R_PLUGIN_TYPE_MIXER2 ? "mixer2" : pi->plugin_type == F0R_PLUGIN_TYPE_MIXER3 ? "mixer3" : "unknown"); @@ -271,6 +274,18 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) return 0; } +static av_cold int filter_init(AVFilterContext *ctx, const char *args, void *opaque) +{ + Frei0rContext *frei0r = ctx->priv; + char dl_name[1024]; + *frei0r->params = 0; + + if (args) + sscanf(args, "%1023[^:]:%255c", dl_name, frei0r->params); + + return frei0r_init(ctx, dl_name, F0R_PLUGIN_TYPE_FILTER); +} + static av_cold void uninit(AVFilterContext *ctx) { Frei0rContext *frei0r = ctx->priv; @@ -344,7 +359,7 @@ AVFilter avfilter_vf_frei0r = { .description = NULL_IF_CONFIG_SMALL("Apply a frei0r effect."), .query_formats = query_formats, - .init = init, + .init = filter_init, .uninit = uninit, .priv_size = sizeof(Frei0rContext), @@ -361,3 +376,89 @@ AVFilter avfilter_vf_frei0r = { .type = AVMEDIA_TYPE_VIDEO, }, { .name = NULL}}, }; + +static av_cold int source_init(AVFilterContext *ctx, const char *args, void *opaque) +{ + Frei0rContext *frei0r = ctx->priv; + char dl_name[1024], c; + char frame_size[128] = ""; + char frame_rate[128] = ""; + AVRational frame_rate_q; + + memset(frei0r->params, 0, sizeof(frei0r->params)); + + if (args) + sscanf(args, "%127[^:]:%127[^:]:%1023[^:=]%c%255c", + frame_size, frame_rate, dl_name, &c, frei0r->params); + + if (av_parse_video_size(&frei0r->w, &frei0r->h, frame_size) < 0) { + av_log(ctx, AV_LOG_ERROR, "Invalid frame size: '%s'\n", frame_size); + return AVERROR(EINVAL); + } + + if (av_parse_video_rate(&frame_rate_q, frame_rate) < 0 || + frame_rate_q.den <= 0 || frame_rate_q.num <= 0) { + av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: '%s'\n", frame_rate); + return AVERROR(EINVAL); + } + frei0r->time_base.num = frame_rate_q.den; + frei0r->time_base.den = frame_rate_q.num; + + return frei0r_init(ctx, dl_name, F0R_PLUGIN_TYPE_SOURCE); +} + +static int source_config_props(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + Frei0rContext *frei0r = ctx->priv; + + if (av_image_check_size(frei0r->w, frei0r->h, 0, ctx) < 0) + return AVERROR(EINVAL); + outlink->w = frei0r->w; + outlink->h = frei0r->h; + outlink->time_base = frei0r->time_base; + + if (!(frei0r->instance = frei0r->construct(outlink->w, outlink->h))) { + av_log(ctx, AV_LOG_ERROR, "Impossible to load frei0r instance"); + return AVERROR(EINVAL); + } + + return set_params(ctx, frei0r->params); +} + +static int source_request_frame(AVFilterLink *outlink) +{ + Frei0rContext *frei0r = outlink->src->priv; + AVFilterBufferRef *picref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h); + picref->video->pixel_aspect = (AVRational) {1, 1}; + picref->pts = frei0r->pts++; + picref->pos = -1; + + avfilter_start_frame(outlink, avfilter_ref_buffer(picref, ~0)); + frei0r->update(frei0r->instance, av_rescale_q(picref->pts, frei0r->time_base, (AVRational){1,1000}), + NULL, (uint32_t *)picref->data[0]); + avfilter_draw_slice(outlink, 0, outlink->h, 1); + avfilter_end_frame(outlink); + avfilter_unref_buffer(picref); + + return 0; +} + +AVFilter avfilter_vsrc_frei0r_src = { + .name = "frei0r_src", + .description = NULL_IF_CONFIG_SMALL("Generate a frei0r source."), + + .priv_size = sizeof(Frei0rContext), + .init = source_init, + .uninit = uninit, + + .query_formats = query_formats, + + .inputs = (AVFilterPad[]) {{ .name = NULL}}, + + .outputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = source_request_frame, + .config_props = source_config_props }, + { .name = NULL}}, +};