X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Fvf_tile.c;h=484a976de32df70066554893ab8175d008918101;hb=83ab46a57e4b8c647366bf81679891164e215a00;hp=52d53ebcc1d727459a6cc512a58b5be317e94edc;hpb=649384290089ff6ede80aceb050803707be72bb5;p=ffmpeg diff --git a/libavfilter/vf_tile.c b/libavfilter/vf_tile.c index 52d53ebcc1d..484a976de32 100644 --- a/libavfilter/vf_tile.c +++ b/libavfilter/vf_tile.c @@ -23,6 +23,7 @@ * tile video filter */ +#include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" @@ -31,30 +32,61 @@ #include "internal.h" typedef struct { + const AVClass *class; unsigned w, h; + unsigned margin; + unsigned padding; unsigned current; + unsigned nb_frames; FFDrawContext draw; FFDrawColor blank; } TileContext; #define REASONABLE_SIZE 1024 +#define OFFSET(x) offsetof(TileContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption tile_options[] = { + { "layout", "set grid size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, + {.str = "6x5"}, 0, 0, FLAGS }, + { "margin", "set outer border margin in pixels", OFFSET(margin), + AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1024, FLAGS }, + { "padding", "set inner border thickness in pixels", OFFSET(padding), + AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1024, FLAGS }, + { "nb_frames", "set maximum number of frame to render", OFFSET(nb_frames), + AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS }, + {NULL}, +}; + +AVFILTER_DEFINE_CLASS(tile); + static av_cold int init(AVFilterContext *ctx, const char *args) { TileContext *tile = ctx->priv; - int r; - char dummy; + static const char *shorthand[] = { "layout", "nb_frames", "margin", "padding", NULL }; + int ret; + + tile->class = &tile_class; + av_opt_set_defaults(tile); + + if ((ret = av_opt_set_from_string(tile, args, shorthand, "=", ":")) < 0) + return ret; - if (!args) - args = "6x5"; - r = sscanf(args, "%ux%u%c", &tile->w, &tile->h, &dummy); - if (r != 2 || !tile->w || !tile->h) - return AVERROR(EINVAL); if (tile->w > REASONABLE_SIZE || tile->h > REASONABLE_SIZE) { av_log(ctx, AV_LOG_ERROR, "Tile size %ux%u is insane.\n", tile->w, tile->h); return AVERROR(EINVAL); } + + if (tile->nb_frames == 0) { + tile->nb_frames = tile->w * tile->h; + } else if (tile->nb_frames > tile->w * tile->h) { + av_log(ctx, AV_LOG_ERROR, "nb_frames must be less than or equal to %dx%d=%d\n", + tile->w, tile->h, tile->w * tile->h); + return AVERROR(EINVAL); + } + return 0; } @@ -69,22 +101,24 @@ static int config_props(AVFilterLink *outlink) AVFilterContext *ctx = outlink->src; TileContext *tile = ctx->priv; AVFilterLink *inlink = ctx->inputs[0]; + const unsigned total_margin_w = (tile->w - 1) * tile->padding + 2*tile->margin; + const unsigned total_margin_h = (tile->h - 1) * tile->padding + 2*tile->margin; - if (inlink->w > INT_MAX / tile->w) { + if (inlink->w > (INT_MAX - total_margin_w) / tile->w) { av_log(ctx, AV_LOG_ERROR, "Total width %ux%u is too much.\n", tile->w, inlink->w); return AVERROR(EINVAL); } - if (inlink->h > INT_MAX / tile->h) { + if (inlink->h > (INT_MAX - total_margin_h) / tile->h) { av_log(ctx, AV_LOG_ERROR, "Total height %ux%u is too much.\n", tile->h, inlink->h); return AVERROR(EINVAL); } - outlink->w = tile->w * inlink->w; - outlink->h = tile->h * inlink->h; + outlink->w = tile->w * inlink->w + total_margin_w; + outlink->h = tile->h * inlink->h + total_margin_h; outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; outlink->frame_rate = av_mul_q(inlink->frame_rate, - (AVRational){ 1, tile->w * tile->h }); + (AVRational){ 1, tile->nb_frames }); ff_draw_init(&tile->draw, inlink->format, 0); /* TODO make the color an option, or find an unified way of choosing it */ ff_draw_color(&tile->draw, &tile->blank, (uint8_t[]){ 0, 0, 0, -1 }); @@ -109,17 +143,34 @@ static int start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref) avfilter_copy_buffer_ref_props(outlink->out_buf, picref); outlink->out_buf->video->w = outlink->w; outlink->out_buf->video->h = outlink->h; + + /* fill surface once for margin/padding */ + if (tile->margin || tile->padding) + ff_fill_rectangle(&tile->draw, &tile->blank, + outlink->out_buf->data, outlink->out_buf->linesize, + 0, 0, outlink->w, outlink->h); return 0; } +static void get_current_tile_pos(AVFilterContext *ctx, unsigned *x, unsigned *y) +{ + TileContext *tile = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + const unsigned tx = tile->current % tile->w; + const unsigned ty = tile->current / tile->w; + + *x = tile->margin + (inlink->w + tile->padding) * tx; + *y = tile->margin + (inlink->h + tile->padding) * ty; +} + static int draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { AVFilterContext *ctx = inlink->dst; TileContext *tile = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; - unsigned x0 = inlink->w * (tile->current % tile->w); - unsigned y0 = inlink->h * (tile->current / tile->w); + unsigned x0, y0; + get_current_tile_pos(ctx, &x0, &y0); ff_copy_rectangle2(&tile->draw, outlink->out_buf->data, outlink->out_buf->linesize, inlink ->cur_buf->data, inlink ->cur_buf->linesize, @@ -133,9 +184,9 @@ static void draw_blank_frame(AVFilterContext *ctx, AVFilterBufferRef *out_buf) { TileContext *tile = ctx->priv; AVFilterLink *inlink = ctx->inputs[0]; - unsigned x0 = inlink->w * (tile->current % tile->w); - unsigned y0 = inlink->h * (tile->current / tile->w); + unsigned x0, y0; + get_current_tile_pos(ctx, &x0, &y0); ff_fill_rectangle(&tile->draw, &tile->blank, out_buf->data, out_buf->linesize, x0, y0, inlink->w, inlink->h); @@ -149,7 +200,7 @@ static void end_last_frame(AVFilterContext *ctx) outlink->out_buf = NULL; ff_start_frame(outlink, out_buf); - while (tile->current < tile->w * tile->h) + while (tile->current < tile->nb_frames) draw_blank_frame(ctx, out_buf); ff_draw_slice(outlink, 0, out_buf->video->h, 1); ff_end_frame(outlink); @@ -162,7 +213,7 @@ static int end_frame(AVFilterLink *inlink) TileContext *tile = ctx->priv; avfilter_unref_bufferp(&inlink->cur_buf); - if (++tile->current == tile->w * tile->h) + if (++tile->current == tile->nb_frames) end_last_frame(ctx); return 0; } @@ -189,6 +240,27 @@ static int request_frame(AVFilterLink *outlink) return 0; } +static const AVFilterPad tile_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .start_frame = start_frame, + .draw_slice = draw_slice, + .end_frame = end_frame, + .min_perms = AV_PERM_READ, + }, + { NULL } +}; + +static const AVFilterPad tile_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_props, + .request_frame = request_frame, + }, + { NULL } +}; AVFilter avfilter_vf_tile = { .name = "tile", @@ -196,20 +268,7 @@ AVFilter avfilter_vf_tile = { .init = init, .query_formats = query_formats, .priv_size = sizeof(TileContext), - .inputs = (const AVFilterPad[]) { - { .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .start_frame = start_frame, - .draw_slice = draw_slice, - .end_frame = end_frame, - .min_perms = AV_PERM_READ, }, - { .name = NULL } - }, - .outputs = (const AVFilterPad[]) { - { .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_props, - .request_frame = request_frame }, - { .name = NULL } - }, + .inputs = tile_inputs, + .outputs = tile_outputs, + .priv_class = &tile_class, };