forked from FFmpeg/FFmpeg
avfilter: add zoneplate video test source
This commit is contained in:
parent
086a0f3e5e
commit
3475c8342c
6 changed files with 300 additions and 2 deletions
|
@ -8,6 +8,7 @@ version <next>:
|
|||
- afireqsrc audio source filter
|
||||
- arls filter
|
||||
- ffmpeg CLI new option: -readrate_initial_burst
|
||||
- zoneplate video source filter
|
||||
|
||||
version 6.0:
|
||||
- Radiance HDR image support
|
||||
|
|
|
@ -28083,6 +28083,104 @@ Set max jump for single pan destination. Allowed range is from 1 to 10000.
|
|||
Set fractal type, can be default @code{carpet} or @code{triangle}.
|
||||
@end table
|
||||
|
||||
@section zoneplate
|
||||
Generate a zoneplate test video pattern.
|
||||
|
||||
This source accepts the following options:
|
||||
|
||||
@table @option
|
||||
@item size, s
|
||||
Set frame size. For the syntax of this option, check the @ref{video size syntax,,"Video
|
||||
size" section in the ffmpeg-utils manual,ffmpeg-utils}. Default value is "320x240".
|
||||
|
||||
@item rate, r
|
||||
Set frame rate, expressed as number of frames per second. Default
|
||||
value is "25".
|
||||
|
||||
@item duration, d
|
||||
Set the duration of the sourced video. See
|
||||
@ref{time duration syntax,,the Time duration section in the ffmpeg-utils(1) manual,ffmpeg-utils}
|
||||
for the accepted syntax.
|
||||
|
||||
If not specified, or the expressed duration is negative, the video is
|
||||
supposed to be generated forever.
|
||||
|
||||
@item sar
|
||||
Set the sample aspect ratio of the sourced video.
|
||||
|
||||
@item precision
|
||||
Set precision in bits for look-up table for sine calculations. Default value is 10.
|
||||
Allowed range is from 4 to 16.
|
||||
|
||||
@item xo
|
||||
Set horizontal axis offset for output signal. Default value is 0.
|
||||
|
||||
@item yo
|
||||
Set vertical axis offset for output signal. Default value is 0.
|
||||
|
||||
@item to
|
||||
Set time axis offset for output signal. Default value is 0.
|
||||
|
||||
@item k0
|
||||
Set 0-order, constant added to signal phase. Default value is 0.
|
||||
|
||||
@item kx
|
||||
Set 1-order, phase factor multiplier for horizontal axis. Default value is 0.
|
||||
|
||||
@item ky
|
||||
Set 1-order, phase factor multiplier for vertical axis. Default value is 0.
|
||||
|
||||
@item kt
|
||||
Set 1-order, phase factor multiplier for time axis. Default value is 0.
|
||||
|
||||
@item kxt, kyt, kxy
|
||||
Set phase factor multipliers for combination of spatial and temporal axis.
|
||||
Default value is 0.
|
||||
|
||||
@item kx2
|
||||
Set 2-order, phase factor multiplier for horizontal axis. Default value is 0.
|
||||
|
||||
@item ky2
|
||||
Set 2-order, phase factor multiplier for vertical axis. Default value is 0.
|
||||
|
||||
@item kt2
|
||||
Set 2-order, phase factor multiplier for time axis. Default value is 0.
|
||||
|
||||
@item ku
|
||||
Set the constant added to final phase to produce chroma-blue component of signal.
|
||||
Default value is 0.
|
||||
|
||||
@item kv
|
||||
Set the constant added to final phase to produce chroma-red component of signal.
|
||||
Default value is 0.
|
||||
@end table
|
||||
|
||||
@subsection Commands
|
||||
|
||||
This source supports the some above options as @ref{commands}.
|
||||
|
||||
@subsection Examples
|
||||
|
||||
@itemize
|
||||
@item
|
||||
Generate horizontal color sine sweep:
|
||||
@example
|
||||
zoneplate=ku=512:kv=0:kt2=0:kx2=256:s=wvga:xo=-426:kt=11
|
||||
@end example
|
||||
|
||||
@item
|
||||
Generate vertical color sine sweep:
|
||||
@example
|
||||
zoneplate=ku=512:kv=0:kt2=0:ky2=156:s=wvga:yo=-240:kt=11
|
||||
@end example
|
||||
|
||||
@item
|
||||
Generate circular zone-plate:
|
||||
@example
|
||||
zoneplate=ku=512:kv=100:kt2=0:ky2=256:kx2=556:s=wvga:yo=0:kt=11
|
||||
@end example
|
||||
@end itemize
|
||||
|
||||
@c man end VIDEO SOURCES
|
||||
|
||||
@chapter Video Sinks
|
||||
|
|
|
@ -592,6 +592,7 @@ OBJS-$(CONFIG_SMPTEHDBARS_FILTER) += vsrc_testsrc.o
|
|||
OBJS-$(CONFIG_TESTSRC_FILTER) += vsrc_testsrc.o
|
||||
OBJS-$(CONFIG_TESTSRC2_FILTER) += vsrc_testsrc.o
|
||||
OBJS-$(CONFIG_YUVTESTSRC_FILTER) += vsrc_testsrc.o
|
||||
OBJS-$(CONFIG_ZONEPLATE_FILTER) += vsrc_testsrc.o
|
||||
|
||||
OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o
|
||||
|
||||
|
|
|
@ -557,6 +557,7 @@ extern const AVFilter ff_vsrc_smptehdbars;
|
|||
extern const AVFilter ff_vsrc_testsrc;
|
||||
extern const AVFilter ff_vsrc_testsrc2;
|
||||
extern const AVFilter ff_vsrc_yuvtestsrc;
|
||||
extern const AVFilter ff_vsrc_zoneplate;
|
||||
|
||||
extern const AVFilter ff_vsink_nullsink;
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
|
||||
#include "version_major.h"
|
||||
|
||||
#define LIBAVFILTER_VERSION_MINOR 7
|
||||
#define LIBAVFILTER_VERSION_MICRO 102
|
||||
#define LIBAVFILTER_VERSION_MINOR 8
|
||||
#define LIBAVFILTER_VERSION_MICRO 100
|
||||
|
||||
|
||||
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
|
||||
|
|
|
@ -88,6 +88,15 @@ typedef struct TestSourceContext {
|
|||
|
||||
/* only used by haldclut */
|
||||
int level;
|
||||
|
||||
/* only used by zoneplate */
|
||||
int k0, kx, ky, kt;
|
||||
int kxt, kyt, kxy;
|
||||
int kx2, ky2, kt2;
|
||||
int xo, yo, to, kU, kV;
|
||||
int lut_precision;
|
||||
uint8_t *lut;
|
||||
int (*fill_slice_fn)(AVFilterContext *ctx, void *arg, int job, int nb_jobs);
|
||||
} TestSourceContext;
|
||||
|
||||
#define OFFSET(x) offsetof(TestSourceContext, x)
|
||||
|
@ -135,6 +144,7 @@ static av_cold void uninit(AVFilterContext *ctx)
|
|||
TestSourceContext *test = ctx->priv;
|
||||
|
||||
av_frame_free(&test->picref);
|
||||
av_freep(&test->lut);
|
||||
}
|
||||
|
||||
static int config_props(AVFilterLink *outlink)
|
||||
|
@ -2049,3 +2059,190 @@ const AVFilter ff_vsrc_colorchart = {
|
|||
};
|
||||
|
||||
#endif /* CONFIG_COLORCHART_FILTER */
|
||||
|
||||
#if CONFIG_ZONEPLATE_FILTER
|
||||
|
||||
static const AVOption zoneplate_options[] = {
|
||||
COMMON_OPTIONS
|
||||
{ "precision", "set LUT precision", OFFSET(lut_precision), AV_OPT_TYPE_INT, {.i64=10}, 4, 16, FLAGS },
|
||||
{ "xo", "set X-axis offset", OFFSET(xo), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "yo", "set Y-axis offset", OFFSET(yo), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "to", "set T-axis offset", OFFSET(to), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "k0", "set 0-order phase", OFFSET(k0), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "kx", "set 1-order X-axis phase", OFFSET(kx), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "ky", "set 1-order Y-axis phase", OFFSET(ky), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "kt", "set 1-order T-axis phase", OFFSET(kt), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "kxt", "set X-axis*T-axis product phase", OFFSET(kxt), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "kyt", "set Y-axis*T-axis product phase", OFFSET(kyt), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "kxy", "set X-axis*Y-axis product phase", OFFSET(kxy), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "kx2", "set 2-order X-axis phase", OFFSET(kx2), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "ky2", "set 2-order Y-axis phase", OFFSET(ky2), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "kt2", "set 2-order T-axis phase", OFFSET(kt2), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "ku", "set 0-order U-color phase", OFFSET(kU), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ "kv", "set 0-order V-color phase", OFFSET(kV), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
AVFILTER_DEFINE_CLASS(zoneplate);
|
||||
|
||||
#define ZONEPLATE_SLICE(name, type) \
|
||||
static int zoneplate_fill_slice_##name(AVFilterContext *ctx, \
|
||||
void *arg, int job, \
|
||||
int nb_jobs) \
|
||||
{ \
|
||||
TestSourceContext *test = ctx->priv; \
|
||||
AVFrame *frame = arg; \
|
||||
const int w = frame->width; \
|
||||
const int h = frame->height; \
|
||||
const int kxt = test->kxt, kyt = test->kyt, kx2 = test->kx2; \
|
||||
const int t = test->pts + test->to, k0 = test->k0; \
|
||||
const int kt = test->kt, kt2 = test->kt2, ky2 = test->ky2; \
|
||||
const int ky = test->ky, kx = test->kx, kxy = test->kxy; \
|
||||
const int lut_mask = (1 << test->lut_precision) - 1; \
|
||||
const int nkt2t = kt2 * t * t, nktt = kt * t; \
|
||||
const int start = (h * job ) / nb_jobs; \
|
||||
const int end = (h * (job+1)) / nb_jobs; \
|
||||
const int ylinesize = frame->linesize[0] / sizeof(type); \
|
||||
const int ulinesize = frame->linesize[1] / sizeof(type); \
|
||||
const int vlinesize = frame->linesize[2] / sizeof(type); \
|
||||
const int xreset = -(w / 2) - test->xo; \
|
||||
const int yreset = -(h / 2) - test->yo + start; \
|
||||
const int kU = test->kU, kV = test->kV; \
|
||||
const int skxy = 0xffff / (w / 2); \
|
||||
const int skx2 = 0xffff / w; \
|
||||
const int dkxt = kxt * t; \
|
||||
type *ydst = ((type *)frame->data[0]) + start * ylinesize; \
|
||||
type *udst = ((type *)frame->data[1]) + start * ulinesize; \
|
||||
type *vdst = ((type *)frame->data[2]) + start * vlinesize; \
|
||||
const type *lut = (const type *)test->lut; \
|
||||
int akx, akxt, aky, akyt; \
|
||||
\
|
||||
aky = start * ky; \
|
||||
akyt = start * kyt * t; \
|
||||
\
|
||||
for (int j = start, y = yreset; j < end; j++, y++) { \
|
||||
const int dkxy = kxy * y * skxy; \
|
||||
const int nky2kt2 = (ky2 * y * y) / h + (nkt2t >> 1); \
|
||||
int akxy = dkxy * xreset; \
|
||||
\
|
||||
akx = 0; \
|
||||
akxt = 0; \
|
||||
aky += ky; \
|
||||
akyt += kyt * t; \
|
||||
\
|
||||
for (int i = 0, x = xreset; i < w; i++, x++) { \
|
||||
int phase = k0, uphase = kU, vphase = kV; \
|
||||
\
|
||||
akx += kx; \
|
||||
phase += akx + aky + nktt; \
|
||||
\
|
||||
akxt += dkxt; \
|
||||
akxy += dkxy; \
|
||||
phase += akxt + akyt; \
|
||||
phase += akxy >> 16; \
|
||||
phase += ((kx2 * x * x * skx2) >> 16) + nky2kt2; \
|
||||
uphase += phase; \
|
||||
vphase += phase; \
|
||||
\
|
||||
ydst[i] = lut[phase & lut_mask]; \
|
||||
udst[i] = lut[uphase & lut_mask]; \
|
||||
vdst[i] = lut[vphase & lut_mask]; \
|
||||
} \
|
||||
\
|
||||
ydst += ylinesize; \
|
||||
udst += ulinesize; \
|
||||
vdst += vlinesize; \
|
||||
} \
|
||||
\
|
||||
return 0; \
|
||||
}
|
||||
|
||||
ZONEPLATE_SLICE( 8, uint8_t)
|
||||
ZONEPLATE_SLICE( 9, uint16_t)
|
||||
ZONEPLATE_SLICE(10, uint16_t)
|
||||
ZONEPLATE_SLICE(12, uint16_t)
|
||||
ZONEPLATE_SLICE(14, uint16_t)
|
||||
ZONEPLATE_SLICE(16, uint16_t)
|
||||
|
||||
static void zoneplate_fill_picture(AVFilterContext *ctx, AVFrame *frame)
|
||||
{
|
||||
TestSourceContext *test = ctx->priv;
|
||||
ff_filter_execute(ctx, test->fill_slice_fn, frame, NULL,
|
||||
FFMIN(frame->height, ff_filter_get_nb_threads(ctx)));
|
||||
}
|
||||
|
||||
static int zoneplate_config_props(AVFilterLink *outlink)
|
||||
{
|
||||
AVFilterContext *ctx = outlink->src;
|
||||
TestSourceContext *test = ctx->priv;
|
||||
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
|
||||
const int lut_size = 1 << test->lut_precision;
|
||||
const int depth = desc->comp[0].depth;
|
||||
uint16_t *lut16;
|
||||
uint8_t *lut8;
|
||||
|
||||
if (av_image_check_size(test->w, test->h, 0, ctx) < 0)
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
test->lut = av_calloc(lut_size, sizeof(*test->lut) * ((depth + 7) / 8));
|
||||
if (!test->lut)
|
||||
return AVERROR(ENOMEM);
|
||||
|
||||
lut8 = test->lut;
|
||||
lut16 = (uint16_t *)test->lut;
|
||||
switch (depth) {
|
||||
case 8:
|
||||
for (int i = 0; i < lut_size; i++)
|
||||
lut8[i] = lrintf(255.f * (0.5f + 0.5f * sinf((2.f * M_PI * i) / lut_size)));
|
||||
break;
|
||||
default:
|
||||
for (int i = 0; i < lut_size; i++)
|
||||
lut16[i] = lrintf(((1 << depth) - 1) * (0.5f + 0.5f * sinf((2.f * M_PI * i) / lut_size)));
|
||||
break;
|
||||
}
|
||||
|
||||
test->draw_once = 0;
|
||||
test->fill_picture_fn = zoneplate_fill_picture;
|
||||
|
||||
switch (depth) {
|
||||
case 8: test->fill_slice_fn = zoneplate_fill_slice_8; break;
|
||||
case 9: test->fill_slice_fn = zoneplate_fill_slice_9; break;
|
||||
case 10: test->fill_slice_fn = zoneplate_fill_slice_10; break;
|
||||
case 12: test->fill_slice_fn = zoneplate_fill_slice_12; break;
|
||||
case 14: test->fill_slice_fn = zoneplate_fill_slice_14; break;
|
||||
case 16: test->fill_slice_fn = zoneplate_fill_slice_16; break;
|
||||
}
|
||||
return config_props(outlink);
|
||||
}
|
||||
|
||||
static const enum AVPixelFormat zoneplate_pix_fmts[] = {
|
||||
AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P9,
|
||||
AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12,
|
||||
AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV444P16,
|
||||
AV_PIX_FMT_NONE,
|
||||
};
|
||||
|
||||
static const AVFilterPad avfilter_vsrc_zoneplate_outputs[] = {
|
||||
{
|
||||
.name = "default",
|
||||
.type = AVMEDIA_TYPE_VIDEO,
|
||||
.config_props = zoneplate_config_props,
|
||||
},
|
||||
};
|
||||
|
||||
const AVFilter ff_vsrc_zoneplate = {
|
||||
.name = "zoneplate",
|
||||
.description = NULL_IF_CONFIG_SMALL("Generate zone-plate."),
|
||||
.priv_size = sizeof(TestSourceContext),
|
||||
.priv_class = &zoneplate_class,
|
||||
.init = init,
|
||||
.uninit = uninit,
|
||||
.activate = activate,
|
||||
.inputs = NULL,
|
||||
FILTER_OUTPUTS(avfilter_vsrc_zoneplate_outputs),
|
||||
FILTER_PIXFMTS_ARRAY(zoneplate_pix_fmts),
|
||||
.flags = AVFILTER_FLAG_SLICE_THREADS,
|
||||
.process_command = ff_filter_process_command,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_ZONEPLATE_FILTER */
|
||||
|
|
Loading…
Add table
Reference in a new issue