diff options
Diffstat (limited to 'libavfilter/vsrc_testsrc.c')
-rw-r--r-- | libavfilter/vsrc_testsrc.c | 766 |
1 files changed, 666 insertions, 100 deletions
diff --git a/libavfilter/vsrc_testsrc.c b/libavfilter/vsrc_testsrc.c index e41625e..8814440 100644 --- a/libavfilter/vsrc_testsrc.c +++ b/libavfilter/vsrc_testsrc.c @@ -1,21 +1,22 @@ /* * Copyright (c) 2007 Nicolas George <nicolas.george@normalesup.org> * Copyright (c) 2011 Stefano Sabatini + * Copyright (c) 2012 Paul B Mahol * - * 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 */ @@ -28,87 +29,96 @@ * * rgbtestsrc is ported from MPlayer libmpcodecs/vf_rgbtest.c by * Michael Niedermayer. + * + * smptebars and smptehdbars are by Paul B Mahol. */ #include <float.h> +#include "libavutil/avassert.h" #include "libavutil/common.h" -#include "libavutil/mathematics.h" #include "libavutil/opt.h" +#include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" #include "libavutil/parseutils.h" #include "avfilter.h" +#include "drawutils.h" #include "formats.h" #include "internal.h" #include "video.h" typedef struct TestSourceContext { const AVClass *class; - int h, w; + int w, h; unsigned int nb_frame; - AVRational time_base; - int64_t pts, max_pts; - char *size; ///< video frame size - char *rate; ///< video frame rate - char *duration; ///< total duration of the generated video + AVRational time_base, frame_rate; + int64_t pts; + int64_t duration; ///< duration expressed in microseconds AVRational sar; ///< sample aspect ratio + int draw_once; ///< draw only the first frame, always put out the same picture + int draw_once_reset; ///< draw only the first frame or in case of reset + AVFrame *picref; ///< cached reference containing the painted picture void (* fill_picture_fn)(AVFilterContext *ctx, AVFrame *frame); + /* only used by testsrc */ + int nb_decimals; + + /* only used by color */ + FFDrawContext draw; + FFDrawColor color; + uint8_t color_rgba[4]; + /* only used by rgbtest */ - int rgba_map[4]; + uint8_t rgba_map[4]; + + /* only used by haldclut */ + int level; } TestSourceContext; #define OFFSET(x) offsetof(TestSourceContext, x) -#define FLAGS AV_OPT_FLAG_VIDEO_PARAM +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM -static const AVOption testsrc_options[] = { - { "size", "set video size", OFFSET(size), AV_OPT_TYPE_STRING, {.str = "320x240"}, .flags = FLAGS }, - { "s", "set video size", OFFSET(size), AV_OPT_TYPE_STRING, {.str = "320x240"}, .flags = FLAGS }, - { "rate", "set video rate", OFFSET(rate), AV_OPT_TYPE_STRING, {.str = "25"}, .flags = FLAGS }, - { "r", "set video rate", OFFSET(rate), AV_OPT_TYPE_STRING, {.str = "25"}, .flags = FLAGS }, - { "duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS }, - { "sar", "set video sample aspect ratio", OFFSET(sar), AV_OPT_TYPE_RATIONAL, {.dbl = 1}, 0, INT_MAX, FLAGS }, - { NULL }, -}; +#define SIZE_OPTIONS \ + { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "320x240"}, 0, 0, FLAGS },\ + { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "320x240"}, 0, 0, FLAGS },\ -static av_cold int init_common(AVFilterContext *ctx) -{ - TestSourceContext *test = ctx->priv; - AVRational frame_rate_q; - int64_t duration = -1; - int ret = 0; +#define COMMON_OPTIONS_NOSIZE \ + { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS },\ + { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS },\ + { "duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS },\ + { "d", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS },\ + { "sar", "set video sample aspect ratio", OFFSET(sar), AV_OPT_TYPE_RATIONAL, {.dbl= 1}, 0, INT_MAX, FLAGS }, - if ((ret = av_parse_video_size(&test->w, &test->h, test->size)) < 0) { - av_log(ctx, AV_LOG_ERROR, "Invalid frame size: '%s'\n", test->size); - return ret; - } +#define COMMON_OPTIONS SIZE_OPTIONS COMMON_OPTIONS_NOSIZE - if ((ret = av_parse_video_rate(&frame_rate_q, test->rate)) < 0 || - frame_rate_q.den <= 0 || frame_rate_q.num <= 0) { - av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: '%s'\n", test->rate); - return ret; - } +static const AVOption options[] = { + COMMON_OPTIONS + { NULL } +}; - if ((test->duration) && (ret = av_parse_time(&duration, test->duration, 1)) < 0) { - av_log(ctx, AV_LOG_ERROR, "Invalid duration: '%s'\n", test->duration); - return ret; - } +static av_cold int init(AVFilterContext *ctx) +{ + TestSourceContext *test = ctx->priv; - test->time_base.num = frame_rate_q.den; - test->time_base.den = frame_rate_q.num; - test->max_pts = duration >= 0 ? - av_rescale_q(duration, AV_TIME_BASE_Q, test->time_base) : -1; + test->time_base = av_inv_q(test->frame_rate); test->nb_frame = 0; test->pts = 0; - av_log(ctx, AV_LOG_DEBUG, "size:%dx%d rate:%d/%d duration:%f sar:%d/%d\n", - test->w, test->h, frame_rate_q.num, frame_rate_q.den, - duration < 0 ? -1 : test->max_pts * av_q2d(test->time_base), + av_log(ctx, AV_LOG_VERBOSE, "size:%dx%d rate:%d/%d duration:%f sar:%d/%d\n", + test->w, test->h, test->frame_rate.num, test->frame_rate.den, + test->duration < 0 ? -1 : (double)test->duration/1000000, test->sar.num, test->sar.den); return 0; } +static av_cold void uninit(AVFilterContext *ctx) +{ + TestSourceContext *test = ctx->priv; + + av_frame_free(&test->picref); +} + static int config_props(AVFilterLink *outlink) { TestSourceContext *test = outlink->src->priv; @@ -116,7 +126,8 @@ static int config_props(AVFilterLink *outlink) outlink->w = test->w; outlink->h = test->h; outlink->sample_aspect_ratio = test->sar; - outlink->time_base = test->time_base; + outlink->frame_rate = test->frame_rate; + outlink->time_base = test->time_base; return 0; } @@ -126,36 +137,320 @@ static int request_frame(AVFilterLink *outlink) TestSourceContext *test = outlink->src->priv; AVFrame *frame; - if (test->max_pts >= 0 && test->pts > test->max_pts) + if (test->duration >= 0 && + av_rescale_q(test->pts, test->time_base, AV_TIME_BASE_Q) >= test->duration) return AVERROR_EOF; - frame = ff_get_video_buffer(outlink, test->w, test->h); + + if (test->draw_once) { + if (test->draw_once_reset) { + av_frame_free(&test->picref); + test->draw_once_reset = 0; + } + if (!test->picref) { + test->picref = + ff_get_video_buffer(outlink, test->w, test->h); + if (!test->picref) + return AVERROR(ENOMEM); + test->fill_picture_fn(outlink->src, test->picref); + } + frame = av_frame_clone(test->picref); + } else + frame = ff_get_video_buffer(outlink, test->w, test->h); + if (!frame) return AVERROR(ENOMEM); - - frame->pts = test->pts++; + frame->pts = test->pts; frame->key_frame = 1; frame->interlaced_frame = 0; frame->pict_type = AV_PICTURE_TYPE_I; frame->sample_aspect_ratio = test->sar; + if (!test->draw_once) + test->fill_picture_fn(outlink->src, frame); + + test->pts++; test->nb_frame++; - test->fill_picture_fn(outlink->src, frame); return ff_filter_frame(outlink, frame); } -#if CONFIG_TESTSRC_FILTER +#if CONFIG_COLOR_FILTER + +static const AVOption color_options[] = { + { "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 }, + COMMON_OPTIONS + { NULL } +}; + +AVFILTER_DEFINE_CLASS(color); + +static void color_fill_picture(AVFilterContext *ctx, AVFrame *picref) +{ + TestSourceContext *test = ctx->priv; + ff_fill_rectangle(&test->draw, &test->color, + picref->data, picref->linesize, + 0, 0, test->w, test->h); +} + +static av_cold int color_init(AVFilterContext *ctx) +{ + TestSourceContext *test = ctx->priv; + test->fill_picture_fn = color_fill_picture; + test->draw_once = 1; + return init(ctx); +} + +static int color_query_formats(AVFilterContext *ctx) +{ + ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); + return 0; +} + +static int color_config_props(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->src; + TestSourceContext *test = ctx->priv; + int ret; + + ff_draw_init(&test->draw, inlink->format, 0); + ff_draw_color(&test->draw, &test->color, test->color_rgba); + + test->w = ff_draw_round_to_sub(&test->draw, 0, -1, test->w); + test->h = ff_draw_round_to_sub(&test->draw, 1, -1, test->h); + if (av_image_check_size(test->w, test->h, 0, ctx) < 0) + return AVERROR(EINVAL); + + if ((ret = config_props(inlink)) < 0) + return ret; + + return 0; +} + +static int color_process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + TestSourceContext *test = ctx->priv; + int ret; + + if (!strcmp(cmd, "color") || !strcmp(cmd, "c")) { + uint8_t color_rgba[4]; + + ret = av_parse_color(color_rgba, args, -1, ctx); + if (ret < 0) + return ret; + + memcpy(test->color_rgba, color_rgba, sizeof(color_rgba)); + ff_draw_color(&test->draw, &test->color, test->color_rgba); + test->draw_once_reset = 1; + return 0; + } + + return AVERROR(ENOSYS); +} + +static const AVFilterPad color_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = color_config_props, + }, + { NULL } +}; + +AVFilter ff_vsrc_color = { + .name = "color", + .description = NULL_IF_CONFIG_SMALL("Provide an uniformly colored input."), + .priv_class = &color_class, + .priv_size = sizeof(TestSourceContext), + .init = color_init, + .uninit = uninit, + .query_formats = color_query_formats, + .inputs = NULL, + .outputs = color_outputs, + .process_command = color_process_command, +}; + +#endif /* CONFIG_COLOR_FILTER */ + +#if CONFIG_HALDCLUTSRC_FILTER + +static const AVOption haldclutsrc_options[] = { + { "level", "set level", OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 6}, 2, 8, FLAGS }, + COMMON_OPTIONS_NOSIZE + { NULL } +}; + +AVFILTER_DEFINE_CLASS(haldclutsrc); + +static void haldclutsrc_fill_picture(AVFilterContext *ctx, AVFrame *frame) +{ + int i, j, k, x = 0, y = 0, is16bit = 0, step; + uint32_t alpha = 0; + const TestSourceContext *hc = ctx->priv; + int level = hc->level; + float scale; + const int w = frame->width; + const int h = frame->height; + const uint8_t *data = frame->data[0]; + const int linesize = frame->linesize[0]; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); + uint8_t rgba_map[4]; + + av_assert0(w == h && w == level*level*level); + + ff_fill_rgba_map(rgba_map, frame->format); + + switch (frame->format) { + case AV_PIX_FMT_RGB48: + case AV_PIX_FMT_BGR48: + case AV_PIX_FMT_RGBA64: + case AV_PIX_FMT_BGRA64: + is16bit = 1; + alpha = 0xffff; + break; + case AV_PIX_FMT_RGBA: + case AV_PIX_FMT_BGRA: + case AV_PIX_FMT_ARGB: + case AV_PIX_FMT_ABGR: + alpha = 0xff; + break; + } + + step = av_get_padded_bits_per_pixel(desc) >> (3 + is16bit); + scale = ((float)(1 << (8*(is16bit+1))) - 1) / (level*level - 1); + +#define LOAD_CLUT(nbits) do { \ + uint##nbits##_t *dst = ((uint##nbits##_t *)(data + y*linesize)) + x*step; \ + dst[rgba_map[0]] = av_clip_uint##nbits(i * scale); \ + dst[rgba_map[1]] = av_clip_uint##nbits(j * scale); \ + dst[rgba_map[2]] = av_clip_uint##nbits(k * scale); \ + if (step == 4) \ + dst[rgba_map[3]] = alpha; \ +} while (0) + + level *= level; + for (k = 0; k < level; k++) { + for (j = 0; j < level; j++) { + for (i = 0; i < level; i++) { + if (!is16bit) + LOAD_CLUT(8); + else + LOAD_CLUT(16); + if (++x == w) { + x = 0; + y++; + } + } + } + } +} + +static av_cold int haldclutsrc_init(AVFilterContext *ctx) +{ + TestSourceContext *hc = ctx->priv; + hc->fill_picture_fn = haldclutsrc_fill_picture; + hc->draw_once = 1; + return init(ctx); +} + +static int haldclutsrc_query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, + AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, + AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, + AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR, + AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0, + AV_PIX_FMT_RGB48, AV_PIX_FMT_BGR48, + AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64, + AV_PIX_FMT_NONE, + }; + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); + return 0; +} + +static int haldclutsrc_config_props(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + TestSourceContext *hc = ctx->priv; + + hc->w = hc->h = hc->level * hc->level * hc->level; + return config_props(outlink); +} + +static const AVFilterPad haldclutsrc_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = haldclutsrc_config_props, + }, + { NULL } +}; + +AVFilter ff_vsrc_haldclutsrc = { + .name = "haldclutsrc", + .description = NULL_IF_CONFIG_SMALL("Provide an identity Hald CLUT."), + .priv_class = &haldclutsrc_class, + .priv_size = sizeof(TestSourceContext), + .init = haldclutsrc_init, + .uninit = uninit, + .query_formats = haldclutsrc_query_formats, + .inputs = NULL, + .outputs = haldclutsrc_outputs, +}; +#endif /* CONFIG_HALDCLUTSRC_FILTER */ + +#if CONFIG_NULLSRC_FILTER + +#define nullsrc_options options +AVFILTER_DEFINE_CLASS(nullsrc); + +static void nullsrc_fill_picture(AVFilterContext *ctx, AVFrame *picref) { } -static const char *testsrc_get_name(void *ctx) +static av_cold int nullsrc_init(AVFilterContext *ctx) { - return "testsrc"; + TestSourceContext *test = ctx->priv; + + test->fill_picture_fn = nullsrc_fill_picture; + return init(ctx); } -static const AVClass testsrc_class = { - .class_name = "TestSourceContext", - .item_name = testsrc_get_name, - .option = testsrc_options, +static const AVFilterPad nullsrc_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = config_props, + }, + { NULL }, +}; + +AVFilter ff_vsrc_nullsrc = { + .name = "nullsrc", + .description = NULL_IF_CONFIG_SMALL("Null video source, return unprocessed video frames."), + .init = nullsrc_init, + .uninit = uninit, + .priv_size = sizeof(TestSourceContext), + .priv_class = &nullsrc_class, + .inputs = NULL, + .outputs = nullsrc_outputs, +}; + +#endif /* CONFIG_NULLSRC_FILTER */ + +#if CONFIG_TESTSRC_FILTER + +static const AVOption testsrc_options[] = { + COMMON_OPTIONS + { "decimals", "set number of decimals to show", OFFSET(nb_decimals), AV_OPT_TYPE_INT, {.i64=0}, 0, 17, FLAGS }, + { "n", "set number of decimals to show", OFFSET(nb_decimals), AV_OPT_TYPE_INT, {.i64=0}, 0, 17, FLAGS }, + { NULL } }; +AVFILTER_DEFINE_CLASS(testsrc); + /** * Fill a rectangle with value val. * @@ -168,8 +463,8 @@ static const AVClass testsrc_class = { * @param w width of the rectangle to draw, expressed as a number of segment_width units * @param h height of the rectangle to draw, expressed as a number of segment_width units */ -static void draw_rectangle(unsigned val, uint8_t *dst, int dst_linesize, unsigned segment_width, - unsigned x, unsigned y, unsigned w, unsigned h) +static void draw_rectangle(unsigned val, uint8_t *dst, int dst_linesize, int segment_width, + int x, int y, int w, int h) { int i; int step = 3; @@ -183,8 +478,8 @@ static void draw_rectangle(unsigned val, uint8_t *dst, int dst_linesize, unsigne } } -static void draw_digit(int digit, uint8_t *dst, unsigned dst_linesize, - unsigned segment_width) +static void draw_digit(int digit, uint8_t *dst, int dst_linesize, + int segment_width) { #define TOP_HBAR 1 #define MID_HBAR 2 @@ -278,7 +573,7 @@ static void test_fill_picture(AVFilterContext *ctx, AVFrame *frame) } /* draw sliding color line */ - p = data + frame->linesize[0] * height * 3/4; + p0 = p = data + frame->linesize[0] * (height * 3/4); grad = (256 * test->nb_frame * test->time_base.num / test->time_base.den) % GRADIENT_SIZE; rgrad = 0; @@ -306,15 +601,25 @@ static void test_fill_picture(AVFilterContext *ctx, AVFrame *frame) if (grad >= GRADIENT_SIZE) grad -= GRADIENT_SIZE; } + p = p0; for (y = height / 8; y > 0; y--) { - memcpy(p, p - frame->linesize[0], 3 * width); + memcpy(p+frame->linesize[0], p, 3 * width); p += frame->linesize[0]; } /* draw digits */ seg_size = width / 80; if (seg_size >= 1 && height >= 13 * seg_size) { - second = test->nb_frame * test->time_base.num / test->time_base.den; + int64_t p10decimals = 1; + double time = av_q2d(test->time_base) * test->nb_frame * + pow(10, test->nb_decimals); + if (time >= INT_MAX) + return; + + for (x = 0; x < test->nb_decimals; x++) + p10decimals *= 10; + + second = av_rescale_rnd(test->nb_frame * test->time_base.num, p10decimals, test->time_base.den, AV_ROUND_ZERO); x = width - (width - seg_size * 64) / 2; y = (height - seg_size * 13) / 2; p = data + (x*3 + y * frame->linesize[0]); @@ -333,7 +638,7 @@ static av_cold int test_init(AVFilterContext *ctx) TestSourceContext *test = ctx->priv; test->fill_picture_fn = test_fill_picture; - return init_common(ctx); + return init(ctx); } static int test_query_formats(AVFilterContext *ctx) @@ -361,28 +666,18 @@ AVFilter ff_vsrc_testsrc = { .priv_size = sizeof(TestSourceContext), .priv_class = &testsrc_class, .init = test_init, - + .uninit = uninit, .query_formats = test_query_formats, - - .inputs = NULL, - - .outputs = avfilter_vsrc_testsrc_outputs, + .inputs = NULL, + .outputs = avfilter_vsrc_testsrc_outputs, }; #endif /* CONFIG_TESTSRC_FILTER */ #if CONFIG_RGBTESTSRC_FILTER -static const char *rgbtestsrc_get_name(void *ctx) -{ - return "rgbtestsrc"; -} - -static const AVClass rgbtestsrc_class = { - .class_name = "RGBTestSourceContext", - .item_name = rgbtestsrc_get_name, - .option = testsrc_options, -}; +#define rgbtestsrc_options options +AVFILTER_DEFINE_CLASS(rgbtestsrc); #define R 0 #define G 1 @@ -391,7 +686,7 @@ static const AVClass rgbtestsrc_class = { static void rgbtest_put_pixel(uint8_t *dst, int dst_linesize, int x, int y, int r, int g, int b, enum AVPixelFormat fmt, - int rgba_map[4]) + uint8_t rgba_map[4]) { int32_t v; uint8_t *p; @@ -413,7 +708,7 @@ static void rgbtest_put_pixel(uint8_t *dst, int dst_linesize, case AV_PIX_FMT_BGRA: case AV_PIX_FMT_ARGB: case AV_PIX_FMT_ABGR: - v = (r << (rgba_map[R]*8)) + (g << (rgba_map[G]*8)) + (b << (rgba_map[B]*8)); + v = (r << (rgba_map[R]*8)) + (g << (rgba_map[G]*8)) + (b << (rgba_map[B]*8)) + (255 << (rgba_map[A]*8)); p = dst + 4*x + y*dst_linesize; AV_WL32(p, v); break; @@ -444,8 +739,9 @@ static av_cold int rgbtest_init(AVFilterContext *ctx) { TestSourceContext *test = ctx->priv; + test->draw_once = 1; test->fill_picture_fn = rgbtest_fill_picture; - return init_common(ctx); + return init(ctx); } static int rgbtest_query_formats(AVFilterContext *ctx) @@ -466,15 +762,7 @@ static int rgbtest_config_props(AVFilterLink *outlink) { TestSourceContext *test = outlink->src->priv; - switch (outlink->format) { - case AV_PIX_FMT_ARGB: test->rgba_map[A] = 0; test->rgba_map[R] = 1; test->rgba_map[G] = 2; test->rgba_map[B] = 3; break; - case AV_PIX_FMT_ABGR: test->rgba_map[A] = 0; test->rgba_map[B] = 1; test->rgba_map[G] = 2; test->rgba_map[R] = 3; break; - case AV_PIX_FMT_RGBA: - case AV_PIX_FMT_RGB24: test->rgba_map[R] = 0; test->rgba_map[G] = 1; test->rgba_map[B] = 2; test->rgba_map[A] = 3; break; - case AV_PIX_FMT_BGRA: - case AV_PIX_FMT_BGR24: test->rgba_map[B] = 0; test->rgba_map[G] = 1; test->rgba_map[R] = 2; test->rgba_map[A] = 3; break; - } - + ff_fill_rgba_map(test->rgba_map, outlink->format); return config_props(outlink); } @@ -494,12 +782,290 @@ AVFilter ff_vsrc_rgbtestsrc = { .priv_size = sizeof(TestSourceContext), .priv_class = &rgbtestsrc_class, .init = rgbtest_init, - + .uninit = uninit, .query_formats = rgbtest_query_formats, + .inputs = NULL, + .outputs = avfilter_vsrc_rgbtestsrc_outputs, +}; - .inputs = NULL, +#endif /* CONFIG_RGBTESTSRC_FILTER */ + +#if CONFIG_SMPTEBARS_FILTER || CONFIG_SMPTEHDBARS_FILTER - .outputs = avfilter_vsrc_rgbtestsrc_outputs, +static const uint8_t rainbow[7][4] = { + { 180, 128, 128, 255 }, /* gray */ + { 168, 44, 136, 255 }, /* yellow */ + { 145, 147, 44, 255 }, /* cyan */ + { 133, 63, 52, 255 }, /* green */ + { 63, 193, 204, 255 }, /* magenta */ + { 51, 109, 212, 255 }, /* red */ + { 28, 212, 120, 255 }, /* blue */ }; -#endif /* CONFIG_RGBTESTSRC_FILTER */ +static const uint8_t wobnair[7][4] = { + { 32, 240, 118, 255 }, /* blue */ + { 19, 128, 128, 255 }, /* 7.5% intensity black */ + { 54, 184, 198, 255 }, /* magenta */ + { 19, 128, 128, 255 }, /* 7.5% intensity black */ + { 188, 154, 16, 255 }, /* cyan */ + { 19, 128, 128, 255 }, /* 7.5% intensity black */ + { 191, 128, 128, 255 }, /* gray */ +}; + +static const uint8_t white[4] = { 235, 128, 128, 255 }; +static const uint8_t black[4] = { 19, 128, 128, 255 }; /* 7.5% intensity black */ + +/* pluge pulses */ +static const uint8_t neg4ire[4] = { 9, 128, 128, 255 }; /* 3.5% intensity black */ +static const uint8_t pos4ire[4] = { 29, 128, 128, 255 }; /* 11.5% intensity black */ + +/* fudged Q/-I */ +static const uint8_t i_pixel[4] = { 61, 153, 99, 255 }; +static const uint8_t q_pixel[4] = { 35, 174, 152, 255 }; + +static const uint8_t gray40[4] = { 104, 128, 128, 255 }; +static const uint8_t gray15[4] = { 49, 128, 128, 255 }; +static const uint8_t cyan[4] = { 188, 154, 16, 255 }; +static const uint8_t yellow[4] = { 219, 16, 138, 255 }; +static const uint8_t blue[4] = { 32, 240, 118, 255 }; +static const uint8_t red[4] = { 63, 102, 240, 255 }; +static const uint8_t black0[4] = { 16, 128, 128, 255 }; +static const uint8_t black2[4] = { 20, 128, 128, 255 }; +static const uint8_t black4[4] = { 25, 128, 128, 255 }; +static const uint8_t neg2[4] = { 12, 128, 128, 255 }; + +static void draw_bar(TestSourceContext *test, const uint8_t color[4], + unsigned x, unsigned y, unsigned w, unsigned h, + AVFrame *frame) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); + uint8_t *p, *p0; + int plane; + + x = FFMIN(x, test->w - 1); + y = FFMIN(y, test->h - 1); + w = FFMIN(w, test->w - x); + h = FFMIN(h, test->h - y); + + av_assert0(x + w <= test->w); + av_assert0(y + h <= test->h); + + for (plane = 0; frame->data[plane]; plane++) { + const int c = color[plane]; + const int linesize = frame->linesize[plane]; + int i, px, py, pw, ph; + + if (plane == 1 || plane == 2) { + px = x >> desc->log2_chroma_w; + pw = w >> desc->log2_chroma_w; + py = y >> desc->log2_chroma_h; + ph = h >> desc->log2_chroma_h; + } else { + px = x; + pw = w; + py = y; + ph = h; + } + + p0 = p = frame->data[plane] + py * linesize + px; + memset(p, c, pw); + p += linesize; + for (i = 1; i < ph; i++, p += linesize) + memcpy(p, p0, pw); + } +} + +static int smptebars_query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, + AV_PIX_FMT_NONE, + }; + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); + return 0; +} + +static const AVFilterPad smptebars_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = config_props, + }, + { NULL } +}; + +#if CONFIG_SMPTEBARS_FILTER + +#define smptebars_options options +AVFILTER_DEFINE_CLASS(smptebars); + +static void smptebars_fill_picture(AVFilterContext *ctx, AVFrame *picref) +{ + TestSourceContext *test = ctx->priv; + int r_w, r_h, w_h, p_w, p_h, i, tmp, x = 0; + const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(picref->format); + + av_frame_set_colorspace(picref, AVCOL_SPC_BT470BG); + + r_w = FFALIGN((test->w + 6) / 7, 1 << pixdesc->log2_chroma_w); + r_h = FFALIGN(test->h * 2 / 3, 1 << pixdesc->log2_chroma_h); + w_h = FFALIGN(test->h * 3 / 4 - r_h, 1 << pixdesc->log2_chroma_h); + p_w = FFALIGN(r_w * 5 / 4, 1 << pixdesc->log2_chroma_w); + p_h = test->h - w_h - r_h; + + for (i = 0; i < 7; i++) { + draw_bar(test, rainbow[i], x, 0, r_w, r_h, picref); + draw_bar(test, wobnair[i], x, r_h, r_w, w_h, picref); + x += r_w; + } + x = 0; + draw_bar(test, i_pixel, x, r_h + w_h, p_w, p_h, picref); + x += p_w; + draw_bar(test, white, x, r_h + w_h, p_w, p_h, picref); + x += p_w; + draw_bar(test, q_pixel, x, r_h + w_h, p_w, p_h, picref); + x += p_w; + tmp = FFALIGN(5 * r_w - x, 1 << pixdesc->log2_chroma_w); + draw_bar(test, black, x, r_h + w_h, tmp, p_h, picref); + x += tmp; + tmp = FFALIGN(r_w / 3, 1 << pixdesc->log2_chroma_w); + draw_bar(test, neg4ire, x, r_h + w_h, tmp, p_h, picref); + x += tmp; + draw_bar(test, black, x, r_h + w_h, tmp, p_h, picref); + x += tmp; + draw_bar(test, pos4ire, x, r_h + w_h, tmp, p_h, picref); + x += tmp; + draw_bar(test, black, x, r_h + w_h, test->w - x, p_h, picref); +} + +static av_cold int smptebars_init(AVFilterContext *ctx) +{ + TestSourceContext *test = ctx->priv; + + test->fill_picture_fn = smptebars_fill_picture; + test->draw_once = 1; + return init(ctx); +} + +AVFilter ff_vsrc_smptebars = { + .name = "smptebars", + .description = NULL_IF_CONFIG_SMALL("Generate SMPTE color bars."), + .priv_size = sizeof(TestSourceContext), + .priv_class = &smptebars_class, + .init = smptebars_init, + .uninit = uninit, + .query_formats = smptebars_query_formats, + .inputs = NULL, + .outputs = smptebars_outputs, +}; + +#endif /* CONFIG_SMPTEBARS_FILTER */ + +#if CONFIG_SMPTEHDBARS_FILTER + +#define smptehdbars_options options +AVFILTER_DEFINE_CLASS(smptehdbars); + +static void smptehdbars_fill_picture(AVFilterContext *ctx, AVFrame *picref) +{ + TestSourceContext *test = ctx->priv; + int d_w, r_w, r_h, l_w, i, tmp, x = 0, y = 0; + const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(picref->format); + + av_frame_set_colorspace(picref, AVCOL_SPC_BT709); + + d_w = FFALIGN(test->w / 8, 1 << pixdesc->log2_chroma_w); + r_h = FFALIGN(test->h * 7 / 12, 1 << pixdesc->log2_chroma_h); + draw_bar(test, gray40, x, 0, d_w, r_h, picref); + x += d_w; + + r_w = FFALIGN((((test->w + 3) / 4) * 3) / 7, 1 << pixdesc->log2_chroma_w); + for (i = 0; i < 7; i++) { + draw_bar(test, rainbow[i], x, 0, r_w, r_h, picref); + x += r_w; + } + draw_bar(test, gray40, x, 0, test->w - x, r_h, picref); + y = r_h; + r_h = FFALIGN(test->h / 12, 1 << pixdesc->log2_chroma_h); + draw_bar(test, cyan, 0, y, d_w, r_h, picref); + x = d_w; + draw_bar(test, i_pixel, x, y, r_w, r_h, picref); + x += r_w; + tmp = r_w * 6; + draw_bar(test, rainbow[0], x, y, tmp, r_h, picref); + x += tmp; + l_w = x; + draw_bar(test, blue, x, y, test->w - x, r_h, picref); + y += r_h; + draw_bar(test, yellow, 0, y, d_w, r_h, picref); + x = d_w; + draw_bar(test, q_pixel, x, y, r_w, r_h, picref); + x += r_w; + + for (i = 0; i < tmp; i += 1 << pixdesc->log2_chroma_w) { + uint8_t yramp[4] = {0}; + + yramp[0] = i * 255 / tmp; + yramp[1] = 128; + yramp[2] = 128; + yramp[3] = 255; + + draw_bar(test, yramp, x, y, 1 << pixdesc->log2_chroma_w, r_h, picref); + x += 1 << pixdesc->log2_chroma_w; + } + draw_bar(test, red, x, y, test->w - x, r_h, picref); + y += r_h; + draw_bar(test, gray15, 0, y, d_w, test->h - y, picref); + x = d_w; + tmp = FFALIGN(r_w * 3 / 2, 1 << pixdesc->log2_chroma_w); + draw_bar(test, black0, x, y, tmp, test->h - y, picref); + x += tmp; + tmp = FFALIGN(r_w * 2, 1 << pixdesc->log2_chroma_w); + draw_bar(test, white, x, y, tmp, test->h - y, picref); + x += tmp; + tmp = FFALIGN(r_w * 5 / 6, 1 << pixdesc->log2_chroma_w); + draw_bar(test, black0, x, y, tmp, test->h - y, picref); + x += tmp; + tmp = FFALIGN(r_w / 3, 1 << pixdesc->log2_chroma_w); + draw_bar(test, neg2, x, y, tmp, test->h - y, picref); + x += tmp; + draw_bar(test, black0, x, y, tmp, test->h - y, picref); + x += tmp; + draw_bar(test, black2, x, y, tmp, test->h - y, picref); + x += tmp; + draw_bar(test, black0, x, y, tmp, test->h - y, picref); + x += tmp; + draw_bar(test, black4, x, y, tmp, test->h - y, picref); + x += tmp; + r_w = l_w - x; + draw_bar(test, black0, x, y, r_w, test->h - y, picref); + x += r_w; + draw_bar(test, gray15, x, y, test->w - x, test->h - y, picref); +} + +static av_cold int smptehdbars_init(AVFilterContext *ctx) +{ + TestSourceContext *test = ctx->priv; + + test->fill_picture_fn = smptehdbars_fill_picture; + test->draw_once = 1; + return init(ctx); +} + +AVFilter ff_vsrc_smptehdbars = { + .name = "smptehdbars", + .description = NULL_IF_CONFIG_SMALL("Generate SMPTE HD color bars."), + .priv_size = sizeof(TestSourceContext), + .priv_class = &smptehdbars_class, + .init = smptehdbars_init, + .uninit = uninit, + .query_formats = smptebars_query_formats, + .inputs = NULL, + .outputs = smptebars_outputs, +}; + +#endif /* CONFIG_SMPTEHDBARS_FILTER */ +#endif /* CONFIG_SMPTEBARS_FILTER || CONFIG_SMPTEHDBARS_FILTER */ |