Message ID | 20230207035907.975340-1-haihao.xiang@intel.com |
---|---|
State | New |
Headers | show |
Series | [FFmpeg-devel,1/2] lavfi/vf_stack_vaapi: factor out the common code for stack setting | expand |
Context | Check | Description |
---|---|---|
yinshiyou/make_loongarch64 | success | Make finished |
yinshiyou/make_fate_loongarch64 | success | Make fate finished |
andriy/make_x86 | success | Make finished |
andriy/make_fate_x86 | success | Make fate finished |
On Di, 2023-02-07 at 11:59 +0800, Xiang, Haihao wrote: > From: Haihao Xiang <haihao.xiang@intel.com> > > The common code will be used in QSV based stack filters. > > Signed-off-by: Haihao Xiang <haihao.xiang@intel.com> > --- > libavfilter/stack_internal.c | 355 +++++++++++++++++++++++++++++++ > libavfilter/stack_internal.h | 60 ++++++ > libavfilter/vf_stack_vaapi.c | 395 ++++------------------------------- > 3 files changed, 452 insertions(+), 358 deletions(-) > create mode 100644 libavfilter/stack_internal.c > create mode 100644 libavfilter/stack_internal.h > > diff --git a/libavfilter/stack_internal.c b/libavfilter/stack_internal.c > new file mode 100644 > index 0000000000..57661e1c97 > --- /dev/null > +++ b/libavfilter/stack_internal.c > @@ -0,0 +1,355 @@ > +/* > + * This file is part of FFmpeg. > + * > + * 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. > + * > + * 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 FFmpeg; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 > USA > + */ > + > +#define OFFSET(x) offsetof(StackHWContext, x) > +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM) > + > +#define SET_OUTPUT_REGION(region, rx, ry, rw, rh) do { \ > + region->x = rx; \ > + region->y = ry; \ > + region->width = rw; \ > + region->height = rh; \ > + } while (0) > + > +static int init_framesync(AVFilterContext *avctx) > +{ > + StackBaseContext *sctx = avctx->priv; > + int ret; > + > + ret = ff_framesync_init(&sctx->fs, avctx, avctx->nb_inputs); > + if (ret < 0) > + return ret; > + > + sctx->fs.on_event = process_frame; > + sctx->fs.opaque = sctx; > + > + for (int i = 0; i < sctx->nb_inputs; i++) { > + FFFrameSyncIn *in = &sctx->fs.in[i]; > + > + in->before = EXT_STOP; > + in->after = sctx->shortest ? EXT_STOP : EXT_INFINITY; > + in->sync = 1; > + in->time_base = avctx->inputs[i]->time_base; > + } > + > + return ff_framesync_configure(&sctx->fs); > +} > + > +static int config_comm_output(AVFilterLink *outlink) > +{ > + AVFilterContext *avctx = outlink->src; > + StackBaseContext *sctx = avctx->priv; > + AVFilterLink *inlink0 = avctx->inputs[0]; > + int width, height, ret; > + > + if (sctx->mode == STACK_H) { > + height = sctx->tile_height; > + width = 0; > + > + if (!height) > + height = inlink0->h; > + > + for (int i = 0; i < sctx->nb_inputs; i++) { > + AVFilterLink *inlink = avctx->inputs[i]; > + StackItemRegion *region = &sctx->regions[i]; > + > + SET_OUTPUT_REGION(region, width, 0, av_rescale(height, inlink->w, > inlink->h), height); > + width += av_rescale(height, inlink->w, inlink->h); > + } > + } else if (sctx->mode == STACK_V) { > + height = 0; > + width = sctx->tile_width; > + > + if (!width) > + width = inlink0->w; > + > + for (int i = 0; i < sctx->nb_inputs; i++) { > + AVFilterLink *inlink = avctx->inputs[i]; > + StackItemRegion *region = &sctx->regions[i]; > + > + SET_OUTPUT_REGION(region, 0, height, width, av_rescale(width, > inlink->h, inlink->w)); > + height += av_rescale(width, inlink->h, inlink->w); > + } > + } else if (sctx->nb_grid_rows && sctx->nb_grid_columns) { > + int xpos = 0, ypos = 0; > + int ow, oh, k = 0; > + > + ow = sctx->tile_width; > + oh = sctx->tile_height; > + > + if (!ow || !oh) { > + ow = avctx->inputs[0]->w; > + oh = avctx->inputs[0]->h; > + } > + > + for (int i = 0; i < sctx->nb_grid_columns; i++) { > + ypos = 0; > + > + for (int j = 0; j < sctx->nb_grid_rows; j++) { > + StackItemRegion *region = &sctx->regions[k++]; > + > + SET_OUTPUT_REGION(region, xpos, ypos, ow, oh); > + ypos += oh; > + } > + > + xpos += ow; > + } > + > + width = ow * sctx->nb_grid_columns; > + height = oh * sctx->nb_grid_rows; > + } else { > + char *arg, *p = sctx->layout, *saveptr = NULL; > + char *arg2, *p2, *saveptr2 = NULL; > + char *arg3, *p3, *saveptr3 = NULL; > + int xpos, ypos, size; > + int ow, oh; > + > + width = avctx->inputs[0]->w; > + height = avctx->inputs[0]->h; > + > + for (int i = 0; i < sctx->nb_inputs; i++) { > + AVFilterLink *inlink = avctx->inputs[i]; > + StackItemRegion *region = &sctx->regions[i]; > + > + ow = inlink->w; > + oh = inlink->h; > + > + if (!(arg = av_strtok(p, "|", &saveptr))) > + return AVERROR(EINVAL); > + > + p = NULL; > + p2 = arg; > + xpos = ypos = 0; > + > + for (int j = 0; j < 3; j++) { > + if (!(arg2 = av_strtok(p2, "_", &saveptr2))) { > + if (j == 2) > + break; > + else > + return AVERROR(EINVAL); > + } > + > + p2 = NULL; > + p3 = arg2; > + > + if (j == 2) { > + if ((ret = av_parse_video_size(&ow, &oh, p3)) < 0) { > + av_log(avctx, AV_LOG_ERROR, "Invalid size '%s'\n", > p3); > + return ret; > + } > + > + break; > + } > + > + while ((arg3 = av_strtok(p3, "+", &saveptr3))) { > + p3 = NULL; > + if (sscanf(arg3, "w%d", &size) == 1) { > + if (size == i || size < 0 || size >= sctx->nb_inputs) > + return AVERROR(EINVAL); > + > + if (!j) > + xpos += sctx->regions[size].width; > + else > + ypos += sctx->regions[size].width; > + } else if (sscanf(arg3, "h%d", &size) == 1) { > + if (size == i || size < 0 || size >= sctx->nb_inputs) > + return AVERROR(EINVAL); > + > + if (!j) > + xpos += sctx->regions[size].height; > + else > + ypos += sctx->regions[size].height; > + } else if (sscanf(arg3, "%d", &size) == 1) { > + if (size < 0) > + return AVERROR(EINVAL); > + > + if (!j) > + xpos += size; > + else > + ypos += size; > + } else { > + return AVERROR(EINVAL); > + } > + } > + } > + > + SET_OUTPUT_REGION(region, xpos, ypos, ow, oh); > + width = FFMAX(width, xpos + ow); > + height = FFMAX(height, ypos + oh); > + } > + > + } > + > + outlink->w = width; > + outlink->h = height; > + outlink->frame_rate = inlink0->frame_rate; > + outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio; > + > + for (int i = 1; i < sctx->nb_inputs; i++) { > + AVFilterLink *inlink = avctx->inputs[i]; > + if (outlink->frame_rate.num != inlink->frame_rate.num || > + outlink->frame_rate.den != inlink->frame_rate.den) { > + av_log(avctx, AV_LOG_VERBOSE, > + "Video inputs have different frame rates, output will be > VFR\n"); > + outlink->frame_rate = av_make_q(1, 0); > + break; > + } > + } > + > + ret = init_framesync(avctx); > + if (ret < 0) > + return ret; > + > + outlink->time_base = sctx->fs.time_base; > + > + return 0; > +} > + > +static int stack_init(AVFilterContext *avctx) > +{ > + StackBaseContext *sctx = avctx->priv; > + int ret; > + > + if (!strcmp(avctx->filter->name, HSTACK_NAME)) > + sctx->mode = STACK_H; > + else if (!strcmp(avctx->filter->name, VSTACK_NAME)) > + sctx->mode = STACK_V; > + else { > + int is_grid; > + > + av_assert0(strcmp(avctx->filter->name, XSTACK_NAME) == 0); > + sctx->mode = STACK_X; > + is_grid = sctx->nb_grid_rows && sctx->nb_grid_columns; > + > + if (sctx->layout && is_grid) { > + av_log(avctx, AV_LOG_ERROR, "Both layout and grid were specified. > Only one is allowed.\n"); > + return AVERROR(EINVAL); > + } > + > + if (!sctx->layout && !is_grid) { > + if (sctx->nb_inputs == 2) { > + sctx->nb_grid_rows = 1; > + sctx->nb_grid_columns = 2; > + is_grid = 1; > + } else { > + av_log(avctx, AV_LOG_ERROR, "No layout or grid > specified.\n"); > + return AVERROR(EINVAL); > + } > + } > + > + if (is_grid) > + sctx->nb_inputs = sctx->nb_grid_rows * sctx->nb_grid_columns; > + > + if (strcmp(sctx->fillcolor_str, "none") && > + av_parse_color(sctx->fillcolor, sctx->fillcolor_str, -1, avctx) > >= 0) { > + sctx->fillcolor_enable = 1; > + } else { > + sctx->fillcolor_enable = 0; > + } > + } > + > + for (int i = 0; i < sctx->nb_inputs; i++) { > + AVFilterPad pad = { 0 }; > + > + pad.type = AVMEDIA_TYPE_VIDEO; > + pad.name = av_asprintf("input%d", i); > + > + if (!pad.name) > + return AVERROR(ENOMEM); > + > + if ((ret = ff_append_inpad_free_name(avctx, &pad)) < 0) > + return ret; > + } > + > + sctx->regions = av_calloc(sctx->nb_inputs, sizeof(*sctx->regions)); > + if (!sctx->regions) > + return AVERROR(ENOMEM); > + > + return 0; > +} > + > +static av_cold void stack_uninit(AVFilterContext *avctx) > +{ > + StackBaseContext *sctx = avctx->priv; > + > + av_freep(&sctx->regions); > + ff_framesync_uninit(&sctx->fs); > +} > + > +static int stack_activate(AVFilterContext *avctx) > +{ > + StackBaseContext *sctx = avctx->priv; > + return ff_framesync_activate(&sctx->fs); > +} > + > +static const AVFilterPad stack_outputs[] = { > + { > + .name = "default", > + .type = AVMEDIA_TYPE_VIDEO, > + .config_props = config_output, > + }, > +}; > + > +#define STACK_COMMON_OPTS \ > + { "inputs", "Set number of inputs", OFFSET(base.nb_inputs), > AV_OPT_TYPE_INT, { .i64 = 2 }, 2, UINT16_MAX, .flags = FLAGS }, \ > + { "shortest", "Force termination when the shortest input terminates", > OFFSET(base.shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, > + > +#define DEFINE_HSTACK_OPTIONS(api) \ > + static const AVOption hstack_##api##_options[] = { \ > + STACK_COMMON_OPTS \ > + { "height", "Set output height (0 to use the height of input 0)", > OFFSET(base.tile_height), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS > }, \ > + { NULL } \ > + } > + > +#define DEFINE_VSTACK_OPTIONS(api) \ > + static const AVOption vstack_##api##_options[] = { \ > + STACK_COMMON_OPTS \ > + { "width", "Set output width (0 to use the width of input 0)", > OFFSET(base.tile_width), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS > }, \ > + { NULL } \ > + } > + > +#define DEFINE_XSTACK_OPTIONS(api) \ > + static const AVOption xstack_##api##_options[] = { \ > + STACK_COMMON_OPTS \ > + { "layout", "Set custom layout", OFFSET(base.layout), > AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, .flags = FLAGS }, \ > + { "grid", "set fixed size grid layout", > OFFSET(base.nb_grid_columns), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, > .flags = FLAGS }, \ > + { "grid_tile_size", "set tile size in grid layout", > OFFSET(base.tile_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = > FLAGS }, \ > + { "fill", "Set the color for unused pixels", > OFFSET(base.fillcolor_str), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = > FLAGS }, \ > + { NULL } \ > + } > + > +#define DEFINE_STACK_FILTER(category, api, capi) \ > + static const AVClass category##_##api##_class = { \ > + .class_name = #category "_" #api, \ > + .item_name = av_default_item_name, \ > + .option = category##_##api##_options, \ > + .version = LIBAVUTIL_VERSION_INT, \ > + }; \ > + const AVFilter ff_vf_##category##_##api = { \ > + .name = #category "_" #api, \ > + .description = NULL_IF_CONFIG_SMALL(#capi " " #category), \ > + .priv_size = sizeof(StackHWContext), \ > + .priv_class = &category##_##api##_class, \ > + .init = api##_stack_init, \ > + .uninit = api##_stack_uninit, \ > + .activate = stack_activate, \ > + FILTER_QUERY_FUNC(api##_stack_query_formats), \ > + FILTER_OUTPUTS(stack_outputs), \ > + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, \ > + .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, \ > + } > diff --git a/libavfilter/stack_internal.h b/libavfilter/stack_internal.h > new file mode 100644 > index 0000000000..2847fc620b > --- /dev/null > +++ b/libavfilter/stack_internal.h > @@ -0,0 +1,60 @@ > +/* > + * This file is part of FFmpeg. > + * > + * 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. > + * > + * 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 FFmpeg; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 > USA > + */ > + > +#ifndef AVFILTER_STACK_INTERNAL_H > +#define AVFILTER_STACK_INTERNAL_H > + > +enum { > + STACK_H = 0, > + STACK_V = 1, > + STACK_X = 2 > +}; > + > +typedef struct StackItemRegion { > + int x; > + int y; > + int width; > + int height; > +} StackItemRegion; > + > +typedef struct StackBaseContext { > + HWContext hwctx; /**< must be the first field */ > + > + FFFrameSync fs; > + int mode; > + uint8_t fillcolor[4]; > + int fillcolor_enable; > + StackItemRegion *regions; > + > + /* Options */ > + int nb_inputs; > + int shortest; > + int tile_width; > + int tile_height; > + int nb_grid_columns; > + int nb_grid_rows; > + char *layout; > + char *fillcolor_str; > +} StackBaseContext; > + > +static int config_comm_output(AVFilterLink *outlink); > +static int stack_init(AVFilterContext *avctx); > +static av_cold void stack_uninit(AVFilterContext *avctx); > +static int stack_activate(AVFilterContext *avctx); > + > +#endif /* AVFILTER_STACK_INTERNAL_H */ > \ No newline at end of file > diff --git a/libavfilter/vf_stack_vaapi.c b/libavfilter/vf_stack_vaapi.c > index 403fbb19eb..26dbe3f7aa 100644 > --- a/libavfilter/vf_stack_vaapi.c > +++ b/libavfilter/vf_stack_vaapi.c > @@ -42,33 +42,17 @@ > #include "framesync.h" > #include "vaapi_vpp.h" > > -#define OFFSET(x) offsetof(StackVAAPIContext, x) > -#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM) > - > -enum { > - STACK_VAAPI_H = 0, > - STACK_VAAPI_V = 1, > - STACK_VAAPI_X = 2 > -}; > +#define HSTACK_NAME "hstack_vaapi" > +#define VSTACK_NAME "vstack_vaapi" > +#define XSTACK_NAME "xstack_vaapi" > +#define HWContext VAAPIVPPContext > +#define StackHWContext StackVAAPIContext > +#include "stack_internal.h" > > typedef struct StackVAAPIContext { > - VAAPIVPPContext vppctx; /**< must be the first field */ > + StackBaseContext base; > > - FFFrameSync fs; > - int mode; > VARectangle *rects; > - uint8_t fillcolor[4]; > - int fillcolor_enable; > - > - /* Options */ > - int nb_inputs; > - int shortest; > - int tile_width; > - int tile_height; > - int nb_grid_columns; > - int nb_grid_rows; > - char *layout; > - char *fillcolor_str; > } StackVAAPIContext; > > static int process_frame(FFFrameSync *fs) > @@ -122,14 +106,14 @@ static int process_frame(FFFrameSync *fs) > params[i].surface = (VASurfaceID)(uintptr_t)iframe->data[3]; > params[i].output_region = &sctx->rects[i]; > > - if (sctx->fillcolor_enable) > - params[i].output_background_color = (sctx->fillcolor[3] << 24 | > - sctx->fillcolor[0] << 16 | > - sctx->fillcolor[1] << 8 | > - sctx->fillcolor[2]); > + if (sctx->base.fillcolor_enable) > + params[i].output_background_color = (sctx->base.fillcolor[3] << > 24 | > + sctx->base.fillcolor[0] << > 16 | > + sctx->base.fillcolor[1] << 8 > | > + sctx->base.fillcolor[2]); > } > > - oframe->pts = av_rescale_q(sctx->fs.pts, sctx->fs.time_base, outlink- > >time_base); > + oframe->pts = av_rescale_q(sctx->base.fs.pts, sctx->base.fs.time_base, > outlink->time_base); > oframe->sample_aspect_ratio = outlink->sample_aspect_ratio; > > ret = ff_vaapi_vpp_render_pictures(avctx, params, avctx->nb_inputs, > oframe); > @@ -147,37 +131,6 @@ fail: > return ret; > } > > -static int init_framesync(AVFilterContext *avctx) > -{ > - StackVAAPIContext *sctx = avctx->priv; > - int ret; > - > - ret = ff_framesync_init(&sctx->fs, avctx, avctx->nb_inputs); > - if (ret < 0) > - return ret; > - > - sctx->fs.on_event = process_frame; > - sctx->fs.opaque = sctx; > - > - for (int i = 0; i < sctx->nb_inputs; i++) { > - FFFrameSyncIn *in = &sctx->fs.in[i]; > - > - in->before = EXT_STOP; > - in->after = sctx->shortest ? EXT_STOP : EXT_INFINITY; > - in->sync = 1; > - in->time_base = avctx->inputs[i]->time_base; > - } > - > - return ff_framesync_configure(&sctx->fs); > -} > - > -#define SET_INPUT_REGION(rect, rx, ry, rw, rh) do { \ > - rect->x = rx; \ > - rect->y = ry; \ > - rect->width = rw; \ > - rect->height = rh; \ > - } while (0) > - > static int config_output(AVFilterLink *outlink) > { > AVFilterContext *avctx = outlink->src; > @@ -185,7 +138,7 @@ static int config_output(AVFilterLink *outlink) > VAAPIVPPContext *vppctx = avctx->priv; > AVFilterLink *inlink0 = avctx->inputs[0]; > AVHWFramesContext *hwfc0 = NULL; > - int width, height, ret; > + int ret; > > if (inlink0->format != AV_PIX_FMT_VAAPI || !inlink0->hw_frames_ctx || > !inlink0->hw_frames_ctx->data) { > av_log(avctx, AV_LOG_ERROR, "Software pixel format is not > supported.\n"); > @@ -194,7 +147,7 @@ static int config_output(AVFilterLink *outlink) > > hwfc0 = (AVHWFramesContext *)inlink0->hw_frames_ctx->data; > > - for (int i = 1; i < sctx->nb_inputs; i++) { > + for (int i = 1; i < sctx->base.nb_inputs; i++) { > AVFilterLink *inlink = avctx->inputs[i]; > AVHWFramesContext *hwfc = NULL; > > @@ -219,168 +172,19 @@ static int config_output(AVFilterLink *outlink) > ff_vaapi_vpp_config_input(inlink0); > vppctx->output_format = hwfc0->sw_format; > > - if (sctx->mode == STACK_VAAPI_H) { > - height = sctx->tile_height; > - width = 0; > - > - if (!height) > - height = inlink0->h; > - > - for (int i = 0; i < sctx->nb_inputs; i++) { > - AVFilterLink *inlink = avctx->inputs[i]; > - VARectangle *rect = &sctx->rects[i]; > - > - SET_INPUT_REGION(rect, width, 0, av_rescale(height, inlink->w, > inlink->h), height); > - width += av_rescale(height, inlink->w, inlink->h); > - } > - } else if (sctx->mode == STACK_VAAPI_V) { > - height = 0; > - width = sctx->tile_width; > - > - if (!width) > - width = inlink0->w; > - > - for (int i = 0; i < sctx->nb_inputs; i++) { > - AVFilterLink *inlink = avctx->inputs[i]; > - VARectangle *rect = &sctx->rects[i]; > - > - SET_INPUT_REGION(rect, 0, height, width, av_rescale(width, > inlink->h, inlink->w)); > - height += av_rescale(width, inlink->h, inlink->w); > - } > - } else if (sctx->nb_grid_rows && sctx->nb_grid_columns) { > - int xpos = 0, ypos = 0; > - int ow, oh, k = 0; > - > - ow = sctx->tile_width; > - oh = sctx->tile_height; > - > - if (!ow || !oh) { > - ow = avctx->inputs[0]->w; > - oh = avctx->inputs[0]->h; > - } > - > - for (int i = 0; i < sctx->nb_grid_columns; i++) { > - ypos = 0; > - > - for (int j = 0; j < sctx->nb_grid_rows; j++) { > - VARectangle *rect = &sctx->rects[k++]; > - > - SET_INPUT_REGION(rect, xpos, ypos, ow, oh); > - ypos += oh; > - } > - > - xpos += ow; > - } > - > - width = ow * sctx->nb_grid_columns; > - height = oh * sctx->nb_grid_rows; > - } else { > - char *arg, *p = sctx->layout, *saveptr = NULL; > - char *arg2, *p2, *saveptr2 = NULL; > - char *arg3, *p3, *saveptr3 = NULL; > - int xpos, ypos, size; > - int ow, oh; > - > - width = avctx->inputs[0]->w; > - height = avctx->inputs[0]->h; > - > - for (int i = 0; i < sctx->nb_inputs; i++) { > - AVFilterLink *inlink = avctx->inputs[i]; > - VARectangle *rect = &sctx->rects[i]; > - > - ow = inlink->w; > - oh = inlink->h; > - > - if (!(arg = av_strtok(p, "|", &saveptr))) > - return AVERROR(EINVAL); > - > - p = NULL; > - p2 = arg; > - xpos = ypos = 0; > - > - for (int j = 0; j < 3; j++) { > - if (!(arg2 = av_strtok(p2, "_", &saveptr2))) { > - if (j == 2) > - break; > - else > - return AVERROR(EINVAL); > - } > - > - p2 = NULL; > - p3 = arg2; > - > - if (j == 2) { > - if ((ret = av_parse_video_size(&ow, &oh, p3)) < 0) { > - av_log(avctx, AV_LOG_ERROR, "Invalid size '%s'\n", > p3); > - return ret; > - } > - > - break; > - } > - > - while ((arg3 = av_strtok(p3, "+", &saveptr3))) { > - p3 = NULL; > - if (sscanf(arg3, "w%d", &size) == 1) { > - if (size == i || size < 0 || size >= sctx->nb_inputs) > - return AVERROR(EINVAL); > - > - if (!j) > - xpos += sctx->rects[size].width; > - else > - ypos += sctx->rects[size].width; > - } else if (sscanf(arg3, "h%d", &size) == 1) { > - if (size == i || size < 0 || size >= sctx->nb_inputs) > - return AVERROR(EINVAL); > - > - if (!j) > - xpos += sctx->rects[size].height; > - else > - ypos += sctx->rects[size].height; > - } else if (sscanf(arg3, "%d", &size) == 1) { > - if (size < 0) > - return AVERROR(EINVAL); > - > - if (!j) > - xpos += size; > - else > - ypos += size; > - } else { > - return AVERROR(EINVAL); > - } > - } > - } > - > - SET_INPUT_REGION(rect, xpos, ypos, ow, oh); > - width = FFMAX(width, xpos + ow); > - height = FFMAX(height, ypos + oh); > - } > - > - } > - > - outlink->w = width; > - outlink->h = height; > - outlink->frame_rate = inlink0->frame_rate; > - outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio; > - > - for (int i = 1; i < sctx->nb_inputs; i++) { > - AVFilterLink *inlink = avctx->inputs[i]; > - if (outlink->frame_rate.num != inlink->frame_rate.num || > - outlink->frame_rate.den != inlink->frame_rate.den) { > - av_log(avctx, AV_LOG_VERBOSE, > - "Video inputs have different frame rates, output will be > VFR\n"); > - outlink->frame_rate = av_make_q(1, 0); > - break; > - } > - } > - > - ret = init_framesync(avctx); > + ret = config_comm_output(outlink); > if (ret < 0) > return ret; > > - outlink->time_base = sctx->fs.time_base; > + for (int i = 0; i < sctx->base.nb_inputs; i++) { > + sctx->rects[i].x = sctx->base.regions[i].x; > + sctx->rects[i].y = sctx->base.regions[i].y; > + sctx->rects[i].width = sctx->base.regions[i].width; > + sctx->rects[i].height = sctx->base.regions[i].height; > + } > > - vppctx->output_width = width; > - vppctx->output_height = height; > + vppctx->output_width = outlink->w; > + vppctx->output_height = outlink->h; > > return ff_vaapi_vpp_config_output(outlink); > } > @@ -391,59 +195,12 @@ static int vaapi_stack_init(AVFilterContext *avctx) > VAAPIVPPContext *vppctx = avctx->priv; > int ret; > > - if (!strcmp(avctx->filter->name, "hstack_vaapi")) > - sctx->mode = STACK_VAAPI_H; > - else if (!strcmp(avctx->filter->name, "vstack_vaapi")) > - sctx->mode = STACK_VAAPI_V; > - else { > - int is_grid; > - > - av_assert0(strcmp(avctx->filter->name, "xstack_vaapi") == 0); > - sctx->mode = STACK_VAAPI_X; > - is_grid = sctx->nb_grid_rows && sctx->nb_grid_columns; > - > - if (sctx->layout && is_grid) { > - av_log(avctx, AV_LOG_ERROR, "Both layout and grid were specified. > Only one is allowed.\n"); > - return AVERROR(EINVAL); > - } > - > - if (!sctx->layout && !is_grid) { > - if (sctx->nb_inputs == 2) { > - sctx->nb_grid_rows = 1; > - sctx->nb_grid_columns = 2; > - is_grid = 1; > - } else { > - av_log(avctx, AV_LOG_ERROR, "No layout or grid > specified.\n"); > - return AVERROR(EINVAL); > - } > - } > - > - if (is_grid) > - sctx->nb_inputs = sctx->nb_grid_rows * sctx->nb_grid_columns; > - > - if (strcmp(sctx->fillcolor_str, "none") && > - av_parse_color(sctx->fillcolor, sctx->fillcolor_str, -1, avctx) > >= 0) { > - sctx->fillcolor_enable = 1; > - } else { > - sctx->fillcolor_enable = 0; > - } > - } > - > - for (int i = 0; i < sctx->nb_inputs; i++) { > - AVFilterPad pad = { 0 }; > - > - pad.type = AVMEDIA_TYPE_VIDEO; > - pad.name = av_asprintf("input%d", i); > - > - if (!pad.name) > - return AVERROR(ENOMEM); > - > - if ((ret = ff_append_inpad_free_name(avctx, &pad)) < 0) > - return ret; > - } > + ret = stack_init(avctx); > + if (ret) > + return ret; > > /* stack region */ > - sctx->rects = av_calloc(sctx->nb_inputs, sizeof(*sctx->rects)); > + sctx->rects = av_calloc(sctx->base.nb_inputs, sizeof(*sctx->rects)); > if (!sctx->rects) > return AVERROR(ENOMEM); > > @@ -457,14 +214,9 @@ static av_cold void vaapi_stack_uninit(AVFilterContext > *avctx) > { > StackVAAPIContext *sctx = avctx->priv; > > - ff_framesync_uninit(&sctx->fs); > - av_freep(&sctx->rects); > -} > + stack_uninit(avctx); > > -static int vaapi_stack_activate(AVFilterContext *avctx) > -{ > - StackVAAPIContext *sctx = avctx->priv; > - return ff_framesync_activate(&sctx->fs); > + av_freep(&sctx->rects); > } > > static int vaapi_stack_query_formats(AVFilterContext *avctx) > @@ -477,98 +229,25 @@ static int vaapi_stack_query_formats(AVFilterContext > *avctx) > return ff_set_common_formats_from_list(avctx, pixel_formats); > } > > -static const AVFilterPad vaapi_stack_outputs[] = { > - { > - .name = "default", > - .type = AVMEDIA_TYPE_VIDEO, > - .config_props = config_output, > - }, > -}; > - > -#define STACK_COMMON_OPTS \ > - { "inputs", "Set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, { > .i64 = 2 }, 2, UINT16_MAX, .flags = FLAGS }, \ > - { "shortest", "Force termination when the shortest input terminates", > OFFSET(shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, > +#include "stack_internal.c" > > #if CONFIG_HSTACK_VAAPI_FILTER > > -static const AVOption hstack_vaapi_options[] = { > - STACK_COMMON_OPTS > - > - { "height", "Set output height (0 to use the height of input 0)", > OFFSET(tile_height), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS }, > - { NULL } > -}; > - > -AVFILTER_DEFINE_CLASS(hstack_vaapi); > - > -const AVFilter ff_vf_hstack_vaapi = { > - .name = "hstack_vaapi", > - .description = NULL_IF_CONFIG_SMALL("VA-API hstack."), > - .priv_size = sizeof(StackVAAPIContext), > - .priv_class = &hstack_vaapi_class, > - .init = vaapi_stack_init, > - .uninit = vaapi_stack_uninit, > - .activate = vaapi_stack_activate, > - FILTER_QUERY_FUNC(vaapi_stack_query_formats), > - FILTER_OUTPUTS(vaapi_stack_outputs), > - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, > - .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, > -}; > +DEFINE_HSTACK_OPTIONS(vaapi); > +DEFINE_STACK_FILTER(hstack, vaapi, "VA-API"); > > #endif > > #if CONFIG_VSTACK_VAAPI_FILTER > > -static const AVOption vstack_vaapi_options[] = { > - STACK_COMMON_OPTS > - > - { "width", "Set output width (0 to use the width of input 0)", > OFFSET(tile_width), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS }, > - { NULL } > -}; > - > -AVFILTER_DEFINE_CLASS(vstack_vaapi); > - > -const AVFilter ff_vf_vstack_vaapi = { > - .name = "vstack_vaapi", > - .description = NULL_IF_CONFIG_SMALL("VA-API vstack."), > - .priv_size = sizeof(StackVAAPIContext), > - .priv_class = &vstack_vaapi_class, > - .init = vaapi_stack_init, > - .uninit = vaapi_stack_uninit, > - .activate = vaapi_stack_activate, > - FILTER_QUERY_FUNC(vaapi_stack_query_formats), > - FILTER_OUTPUTS(vaapi_stack_outputs), > - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, > - .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, > -}; > +DEFINE_VSTACK_OPTIONS(vaapi); > +DEFINE_STACK_FILTER(vstack, vaapi, "VA-API"); > > #endif > > #if CONFIG_XSTACK_VAAPI_FILTER > > -static const AVOption xstack_vaapi_options[] = { > - STACK_COMMON_OPTS > - > - { "layout", "Set custom layout", OFFSET(layout), AV_OPT_TYPE_STRING, > {.str = NULL}, 0, 0, .flags = FLAGS }, > - { "grid", "set fixed size grid layout", OFFSET(nb_grid_columns), > AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, > - { "grid_tile_size", "set tile size in grid layout", OFFSET(tile_width), > AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, > - { "fill", "Set the color for unused pixels", OFFSET(fillcolor_str), > AV_OPT_TYPE_STRING, {.str = "none"}, .flags = FLAGS }, > - { NULL } > -}; > - > -AVFILTER_DEFINE_CLASS(xstack_vaapi); > - > -const AVFilter ff_vf_xstack_vaapi = { > - .name = "xstack_vaapi", > - .description = NULL_IF_CONFIG_SMALL("VA-API xstack."), > - .priv_size = sizeof(StackVAAPIContext), > - .priv_class = &xstack_vaapi_class, > - .init = vaapi_stack_init, > - .uninit = vaapi_stack_uninit, > - .activate = vaapi_stack_activate, > - FILTER_OUTPUTS(vaapi_stack_outputs), > - FILTER_QUERY_FUNC(vaapi_stack_query_formats), > - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, > - .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, > -}; > +DEFINE_XSTACK_OPTIONS(vaapi); > +DEFINE_STACK_FILTER(xstack, vaapi, "VA-API"); > > #endif Will apply the patchset. -Haihao
diff --git a/libavfilter/stack_internal.c b/libavfilter/stack_internal.c new file mode 100644 index 0000000000..57661e1c97 --- /dev/null +++ b/libavfilter/stack_internal.c @@ -0,0 +1,355 @@ +/* + * This file is part of FFmpeg. + * + * 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. + * + * 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define OFFSET(x) offsetof(StackHWContext, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM) + +#define SET_OUTPUT_REGION(region, rx, ry, rw, rh) do { \ + region->x = rx; \ + region->y = ry; \ + region->width = rw; \ + region->height = rh; \ + } while (0) + +static int init_framesync(AVFilterContext *avctx) +{ + StackBaseContext *sctx = avctx->priv; + int ret; + + ret = ff_framesync_init(&sctx->fs, avctx, avctx->nb_inputs); + if (ret < 0) + return ret; + + sctx->fs.on_event = process_frame; + sctx->fs.opaque = sctx; + + for (int i = 0; i < sctx->nb_inputs; i++) { + FFFrameSyncIn *in = &sctx->fs.in[i]; + + in->before = EXT_STOP; + in->after = sctx->shortest ? EXT_STOP : EXT_INFINITY; + in->sync = 1; + in->time_base = avctx->inputs[i]->time_base; + } + + return ff_framesync_configure(&sctx->fs); +} + +static int config_comm_output(AVFilterLink *outlink) +{ + AVFilterContext *avctx = outlink->src; + StackBaseContext *sctx = avctx->priv; + AVFilterLink *inlink0 = avctx->inputs[0]; + int width, height, ret; + + if (sctx->mode == STACK_H) { + height = sctx->tile_height; + width = 0; + + if (!height) + height = inlink0->h; + + for (int i = 0; i < sctx->nb_inputs; i++) { + AVFilterLink *inlink = avctx->inputs[i]; + StackItemRegion *region = &sctx->regions[i]; + + SET_OUTPUT_REGION(region, width, 0, av_rescale(height, inlink->w, inlink->h), height); + width += av_rescale(height, inlink->w, inlink->h); + } + } else if (sctx->mode == STACK_V) { + height = 0; + width = sctx->tile_width; + + if (!width) + width = inlink0->w; + + for (int i = 0; i < sctx->nb_inputs; i++) { + AVFilterLink *inlink = avctx->inputs[i]; + StackItemRegion *region = &sctx->regions[i]; + + SET_OUTPUT_REGION(region, 0, height, width, av_rescale(width, inlink->h, inlink->w)); + height += av_rescale(width, inlink->h, inlink->w); + } + } else if (sctx->nb_grid_rows && sctx->nb_grid_columns) { + int xpos = 0, ypos = 0; + int ow, oh, k = 0; + + ow = sctx->tile_width; + oh = sctx->tile_height; + + if (!ow || !oh) { + ow = avctx->inputs[0]->w; + oh = avctx->inputs[0]->h; + } + + for (int i = 0; i < sctx->nb_grid_columns; i++) { + ypos = 0; + + for (int j = 0; j < sctx->nb_grid_rows; j++) { + StackItemRegion *region = &sctx->regions[k++]; + + SET_OUTPUT_REGION(region, xpos, ypos, ow, oh); + ypos += oh; + } + + xpos += ow; + } + + width = ow * sctx->nb_grid_columns; + height = oh * sctx->nb_grid_rows; + } else { + char *arg, *p = sctx->layout, *saveptr = NULL; + char *arg2, *p2, *saveptr2 = NULL; + char *arg3, *p3, *saveptr3 = NULL; + int xpos, ypos, size; + int ow, oh; + + width = avctx->inputs[0]->w; + height = avctx->inputs[0]->h; + + for (int i = 0; i < sctx->nb_inputs; i++) { + AVFilterLink *inlink = avctx->inputs[i]; + StackItemRegion *region = &sctx->regions[i]; + + ow = inlink->w; + oh = inlink->h; + + if (!(arg = av_strtok(p, "|", &saveptr))) + return AVERROR(EINVAL); + + p = NULL; + p2 = arg; + xpos = ypos = 0; + + for (int j = 0; j < 3; j++) { + if (!(arg2 = av_strtok(p2, "_", &saveptr2))) { + if (j == 2) + break; + else + return AVERROR(EINVAL); + } + + p2 = NULL; + p3 = arg2; + + if (j == 2) { + if ((ret = av_parse_video_size(&ow, &oh, p3)) < 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid size '%s'\n", p3); + return ret; + } + + break; + } + + while ((arg3 = av_strtok(p3, "+", &saveptr3))) { + p3 = NULL; + if (sscanf(arg3, "w%d", &size) == 1) { + if (size == i || size < 0 || size >= sctx->nb_inputs) + return AVERROR(EINVAL); + + if (!j) + xpos += sctx->regions[size].width; + else + ypos += sctx->regions[size].width; + } else if (sscanf(arg3, "h%d", &size) == 1) { + if (size == i || size < 0 || size >= sctx->nb_inputs) + return AVERROR(EINVAL); + + if (!j) + xpos += sctx->regions[size].height; + else + ypos += sctx->regions[size].height; + } else if (sscanf(arg3, "%d", &size) == 1) { + if (size < 0) + return AVERROR(EINVAL); + + if (!j) + xpos += size; + else + ypos += size; + } else { + return AVERROR(EINVAL); + } + } + } + + SET_OUTPUT_REGION(region, xpos, ypos, ow, oh); + width = FFMAX(width, xpos + ow); + height = FFMAX(height, ypos + oh); + } + + } + + outlink->w = width; + outlink->h = height; + outlink->frame_rate = inlink0->frame_rate; + outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio; + + for (int i = 1; i < sctx->nb_inputs; i++) { + AVFilterLink *inlink = avctx->inputs[i]; + if (outlink->frame_rate.num != inlink->frame_rate.num || + outlink->frame_rate.den != inlink->frame_rate.den) { + av_log(avctx, AV_LOG_VERBOSE, + "Video inputs have different frame rates, output will be VFR\n"); + outlink->frame_rate = av_make_q(1, 0); + break; + } + } + + ret = init_framesync(avctx); + if (ret < 0) + return ret; + + outlink->time_base = sctx->fs.time_base; + + return 0; +} + +static int stack_init(AVFilterContext *avctx) +{ + StackBaseContext *sctx = avctx->priv; + int ret; + + if (!strcmp(avctx->filter->name, HSTACK_NAME)) + sctx->mode = STACK_H; + else if (!strcmp(avctx->filter->name, VSTACK_NAME)) + sctx->mode = STACK_V; + else { + int is_grid; + + av_assert0(strcmp(avctx->filter->name, XSTACK_NAME) == 0); + sctx->mode = STACK_X; + is_grid = sctx->nb_grid_rows && sctx->nb_grid_columns; + + if (sctx->layout && is_grid) { + av_log(avctx, AV_LOG_ERROR, "Both layout and grid were specified. Only one is allowed.\n"); + return AVERROR(EINVAL); + } + + if (!sctx->layout && !is_grid) { + if (sctx->nb_inputs == 2) { + sctx->nb_grid_rows = 1; + sctx->nb_grid_columns = 2; + is_grid = 1; + } else { + av_log(avctx, AV_LOG_ERROR, "No layout or grid specified.\n"); + return AVERROR(EINVAL); + } + } + + if (is_grid) + sctx->nb_inputs = sctx->nb_grid_rows * sctx->nb_grid_columns; + + if (strcmp(sctx->fillcolor_str, "none") && + av_parse_color(sctx->fillcolor, sctx->fillcolor_str, -1, avctx) >= 0) { + sctx->fillcolor_enable = 1; + } else { + sctx->fillcolor_enable = 0; + } + } + + for (int i = 0; i < sctx->nb_inputs; i++) { + AVFilterPad pad = { 0 }; + + pad.type = AVMEDIA_TYPE_VIDEO; + pad.name = av_asprintf("input%d", i); + + if (!pad.name) + return AVERROR(ENOMEM); + + if ((ret = ff_append_inpad_free_name(avctx, &pad)) < 0) + return ret; + } + + sctx->regions = av_calloc(sctx->nb_inputs, sizeof(*sctx->regions)); + if (!sctx->regions) + return AVERROR(ENOMEM); + + return 0; +} + +static av_cold void stack_uninit(AVFilterContext *avctx) +{ + StackBaseContext *sctx = avctx->priv; + + av_freep(&sctx->regions); + ff_framesync_uninit(&sctx->fs); +} + +static int stack_activate(AVFilterContext *avctx) +{ + StackBaseContext *sctx = avctx->priv; + return ff_framesync_activate(&sctx->fs); +} + +static const AVFilterPad stack_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, +}; + +#define STACK_COMMON_OPTS \ + { "inputs", "Set number of inputs", OFFSET(base.nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 2, UINT16_MAX, .flags = FLAGS }, \ + { "shortest", "Force termination when the shortest input terminates", OFFSET(base.shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, + +#define DEFINE_HSTACK_OPTIONS(api) \ + static const AVOption hstack_##api##_options[] = { \ + STACK_COMMON_OPTS \ + { "height", "Set output height (0 to use the height of input 0)", OFFSET(base.tile_height), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS }, \ + { NULL } \ + } + +#define DEFINE_VSTACK_OPTIONS(api) \ + static const AVOption vstack_##api##_options[] = { \ + STACK_COMMON_OPTS \ + { "width", "Set output width (0 to use the width of input 0)", OFFSET(base.tile_width), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS }, \ + { NULL } \ + } + +#define DEFINE_XSTACK_OPTIONS(api) \ + static const AVOption xstack_##api##_options[] = { \ + STACK_COMMON_OPTS \ + { "layout", "Set custom layout", OFFSET(base.layout), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, .flags = FLAGS }, \ + { "grid", "set fixed size grid layout", OFFSET(base.nb_grid_columns), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, \ + { "grid_tile_size", "set tile size in grid layout", OFFSET(base.tile_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, \ + { "fill", "Set the color for unused pixels", OFFSET(base.fillcolor_str), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = FLAGS }, \ + { NULL } \ + } + +#define DEFINE_STACK_FILTER(category, api, capi) \ + static const AVClass category##_##api##_class = { \ + .class_name = #category "_" #api, \ + .item_name = av_default_item_name, \ + .option = category##_##api##_options, \ + .version = LIBAVUTIL_VERSION_INT, \ + }; \ + const AVFilter ff_vf_##category##_##api = { \ + .name = #category "_" #api, \ + .description = NULL_IF_CONFIG_SMALL(#capi " " #category), \ + .priv_size = sizeof(StackHWContext), \ + .priv_class = &category##_##api##_class, \ + .init = api##_stack_init, \ + .uninit = api##_stack_uninit, \ + .activate = stack_activate, \ + FILTER_QUERY_FUNC(api##_stack_query_formats), \ + FILTER_OUTPUTS(stack_outputs), \ + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, \ + .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, \ + } diff --git a/libavfilter/stack_internal.h b/libavfilter/stack_internal.h new file mode 100644 index 0000000000..2847fc620b --- /dev/null +++ b/libavfilter/stack_internal.h @@ -0,0 +1,60 @@ +/* + * This file is part of FFmpeg. + * + * 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. + * + * 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFILTER_STACK_INTERNAL_H +#define AVFILTER_STACK_INTERNAL_H + +enum { + STACK_H = 0, + STACK_V = 1, + STACK_X = 2 +}; + +typedef struct StackItemRegion { + int x; + int y; + int width; + int height; +} StackItemRegion; + +typedef struct StackBaseContext { + HWContext hwctx; /**< must be the first field */ + + FFFrameSync fs; + int mode; + uint8_t fillcolor[4]; + int fillcolor_enable; + StackItemRegion *regions; + + /* Options */ + int nb_inputs; + int shortest; + int tile_width; + int tile_height; + int nb_grid_columns; + int nb_grid_rows; + char *layout; + char *fillcolor_str; +} StackBaseContext; + +static int config_comm_output(AVFilterLink *outlink); +static int stack_init(AVFilterContext *avctx); +static av_cold void stack_uninit(AVFilterContext *avctx); +static int stack_activate(AVFilterContext *avctx); + +#endif /* AVFILTER_STACK_INTERNAL_H */ \ No newline at end of file diff --git a/libavfilter/vf_stack_vaapi.c b/libavfilter/vf_stack_vaapi.c index 403fbb19eb..26dbe3f7aa 100644 --- a/libavfilter/vf_stack_vaapi.c +++ b/libavfilter/vf_stack_vaapi.c @@ -42,33 +42,17 @@ #include "framesync.h" #include "vaapi_vpp.h" -#define OFFSET(x) offsetof(StackVAAPIContext, x) -#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM) - -enum { - STACK_VAAPI_H = 0, - STACK_VAAPI_V = 1, - STACK_VAAPI_X = 2 -}; +#define HSTACK_NAME "hstack_vaapi" +#define VSTACK_NAME "vstack_vaapi" +#define XSTACK_NAME "xstack_vaapi" +#define HWContext VAAPIVPPContext +#define StackHWContext StackVAAPIContext +#include "stack_internal.h" typedef struct StackVAAPIContext { - VAAPIVPPContext vppctx; /**< must be the first field */ + StackBaseContext base; - FFFrameSync fs; - int mode; VARectangle *rects; - uint8_t fillcolor[4]; - int fillcolor_enable; - - /* Options */ - int nb_inputs; - int shortest; - int tile_width; - int tile_height; - int nb_grid_columns; - int nb_grid_rows; - char *layout; - char *fillcolor_str; } StackVAAPIContext; static int process_frame(FFFrameSync *fs) @@ -122,14 +106,14 @@ static int process_frame(FFFrameSync *fs) params[i].surface = (VASurfaceID)(uintptr_t)iframe->data[3]; params[i].output_region = &sctx->rects[i]; - if (sctx->fillcolor_enable) - params[i].output_background_color = (sctx->fillcolor[3] << 24 | - sctx->fillcolor[0] << 16 | - sctx->fillcolor[1] << 8 | - sctx->fillcolor[2]); + if (sctx->base.fillcolor_enable) + params[i].output_background_color = (sctx->base.fillcolor[3] << 24 | + sctx->base.fillcolor[0] << 16 | + sctx->base.fillcolor[1] << 8 | + sctx->base.fillcolor[2]); } - oframe->pts = av_rescale_q(sctx->fs.pts, sctx->fs.time_base, outlink->time_base); + oframe->pts = av_rescale_q(sctx->base.fs.pts, sctx->base.fs.time_base, outlink->time_base); oframe->sample_aspect_ratio = outlink->sample_aspect_ratio; ret = ff_vaapi_vpp_render_pictures(avctx, params, avctx->nb_inputs, oframe); @@ -147,37 +131,6 @@ fail: return ret; } -static int init_framesync(AVFilterContext *avctx) -{ - StackVAAPIContext *sctx = avctx->priv; - int ret; - - ret = ff_framesync_init(&sctx->fs, avctx, avctx->nb_inputs); - if (ret < 0) - return ret; - - sctx->fs.on_event = process_frame; - sctx->fs.opaque = sctx; - - for (int i = 0; i < sctx->nb_inputs; i++) { - FFFrameSyncIn *in = &sctx->fs.in[i]; - - in->before = EXT_STOP; - in->after = sctx->shortest ? EXT_STOP : EXT_INFINITY; - in->sync = 1; - in->time_base = avctx->inputs[i]->time_base; - } - - return ff_framesync_configure(&sctx->fs); -} - -#define SET_INPUT_REGION(rect, rx, ry, rw, rh) do { \ - rect->x = rx; \ - rect->y = ry; \ - rect->width = rw; \ - rect->height = rh; \ - } while (0) - static int config_output(AVFilterLink *outlink) { AVFilterContext *avctx = outlink->src; @@ -185,7 +138,7 @@ static int config_output(AVFilterLink *outlink) VAAPIVPPContext *vppctx = avctx->priv; AVFilterLink *inlink0 = avctx->inputs[0]; AVHWFramesContext *hwfc0 = NULL; - int width, height, ret; + int ret; if (inlink0->format != AV_PIX_FMT_VAAPI || !inlink0->hw_frames_ctx || !inlink0->hw_frames_ctx->data) { av_log(avctx, AV_LOG_ERROR, "Software pixel format is not supported.\n"); @@ -194,7 +147,7 @@ static int config_output(AVFilterLink *outlink) hwfc0 = (AVHWFramesContext *)inlink0->hw_frames_ctx->data; - for (int i = 1; i < sctx->nb_inputs; i++) { + for (int i = 1; i < sctx->base.nb_inputs; i++) { AVFilterLink *inlink = avctx->inputs[i]; AVHWFramesContext *hwfc = NULL; @@ -219,168 +172,19 @@ static int config_output(AVFilterLink *outlink) ff_vaapi_vpp_config_input(inlink0); vppctx->output_format = hwfc0->sw_format; - if (sctx->mode == STACK_VAAPI_H) { - height = sctx->tile_height; - width = 0; - - if (!height) - height = inlink0->h; - - for (int i = 0; i < sctx->nb_inputs; i++) { - AVFilterLink *inlink = avctx->inputs[i]; - VARectangle *rect = &sctx->rects[i]; - - SET_INPUT_REGION(rect, width, 0, av_rescale(height, inlink->w, inlink->h), height); - width += av_rescale(height, inlink->w, inlink->h); - } - } else if (sctx->mode == STACK_VAAPI_V) { - height = 0; - width = sctx->tile_width; - - if (!width) - width = inlink0->w; - - for (int i = 0; i < sctx->nb_inputs; i++) { - AVFilterLink *inlink = avctx->inputs[i]; - VARectangle *rect = &sctx->rects[i]; - - SET_INPUT_REGION(rect, 0, height, width, av_rescale(width, inlink->h, inlink->w)); - height += av_rescale(width, inlink->h, inlink->w); - } - } else if (sctx->nb_grid_rows && sctx->nb_grid_columns) { - int xpos = 0, ypos = 0; - int ow, oh, k = 0; - - ow = sctx->tile_width; - oh = sctx->tile_height; - - if (!ow || !oh) { - ow = avctx->inputs[0]->w; - oh = avctx->inputs[0]->h; - } - - for (int i = 0; i < sctx->nb_grid_columns; i++) { - ypos = 0; - - for (int j = 0; j < sctx->nb_grid_rows; j++) { - VARectangle *rect = &sctx->rects[k++]; - - SET_INPUT_REGION(rect, xpos, ypos, ow, oh); - ypos += oh; - } - - xpos += ow; - } - - width = ow * sctx->nb_grid_columns; - height = oh * sctx->nb_grid_rows; - } else { - char *arg, *p = sctx->layout, *saveptr = NULL; - char *arg2, *p2, *saveptr2 = NULL; - char *arg3, *p3, *saveptr3 = NULL; - int xpos, ypos, size; - int ow, oh; - - width = avctx->inputs[0]->w; - height = avctx->inputs[0]->h; - - for (int i = 0; i < sctx->nb_inputs; i++) { - AVFilterLink *inlink = avctx->inputs[i]; - VARectangle *rect = &sctx->rects[i]; - - ow = inlink->w; - oh = inlink->h; - - if (!(arg = av_strtok(p, "|", &saveptr))) - return AVERROR(EINVAL); - - p = NULL; - p2 = arg; - xpos = ypos = 0; - - for (int j = 0; j < 3; j++) { - if (!(arg2 = av_strtok(p2, "_", &saveptr2))) { - if (j == 2) - break; - else - return AVERROR(EINVAL); - } - - p2 = NULL; - p3 = arg2; - - if (j == 2) { - if ((ret = av_parse_video_size(&ow, &oh, p3)) < 0) { - av_log(avctx, AV_LOG_ERROR, "Invalid size '%s'\n", p3); - return ret; - } - - break; - } - - while ((arg3 = av_strtok(p3, "+", &saveptr3))) { - p3 = NULL; - if (sscanf(arg3, "w%d", &size) == 1) { - if (size == i || size < 0 || size >= sctx->nb_inputs) - return AVERROR(EINVAL); - - if (!j) - xpos += sctx->rects[size].width; - else - ypos += sctx->rects[size].width; - } else if (sscanf(arg3, "h%d", &size) == 1) { - if (size == i || size < 0 || size >= sctx->nb_inputs) - return AVERROR(EINVAL); - - if (!j) - xpos += sctx->rects[size].height; - else - ypos += sctx->rects[size].height; - } else if (sscanf(arg3, "%d", &size) == 1) { - if (size < 0) - return AVERROR(EINVAL); - - if (!j) - xpos += size; - else - ypos += size; - } else { - return AVERROR(EINVAL); - } - } - } - - SET_INPUT_REGION(rect, xpos, ypos, ow, oh); - width = FFMAX(width, xpos + ow); - height = FFMAX(height, ypos + oh); - } - - } - - outlink->w = width; - outlink->h = height; - outlink->frame_rate = inlink0->frame_rate; - outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio; - - for (int i = 1; i < sctx->nb_inputs; i++) { - AVFilterLink *inlink = avctx->inputs[i]; - if (outlink->frame_rate.num != inlink->frame_rate.num || - outlink->frame_rate.den != inlink->frame_rate.den) { - av_log(avctx, AV_LOG_VERBOSE, - "Video inputs have different frame rates, output will be VFR\n"); - outlink->frame_rate = av_make_q(1, 0); - break; - } - } - - ret = init_framesync(avctx); + ret = config_comm_output(outlink); if (ret < 0) return ret; - outlink->time_base = sctx->fs.time_base; + for (int i = 0; i < sctx->base.nb_inputs; i++) { + sctx->rects[i].x = sctx->base.regions[i].x; + sctx->rects[i].y = sctx->base.regions[i].y; + sctx->rects[i].width = sctx->base.regions[i].width; + sctx->rects[i].height = sctx->base.regions[i].height; + } - vppctx->output_width = width; - vppctx->output_height = height; + vppctx->output_width = outlink->w; + vppctx->output_height = outlink->h; return ff_vaapi_vpp_config_output(outlink); } @@ -391,59 +195,12 @@ static int vaapi_stack_init(AVFilterContext *avctx) VAAPIVPPContext *vppctx = avctx->priv; int ret; - if (!strcmp(avctx->filter->name, "hstack_vaapi")) - sctx->mode = STACK_VAAPI_H; - else if (!strcmp(avctx->filter->name, "vstack_vaapi")) - sctx->mode = STACK_VAAPI_V; - else { - int is_grid; - - av_assert0(strcmp(avctx->filter->name, "xstack_vaapi") == 0); - sctx->mode = STACK_VAAPI_X; - is_grid = sctx->nb_grid_rows && sctx->nb_grid_columns; - - if (sctx->layout && is_grid) { - av_log(avctx, AV_LOG_ERROR, "Both layout and grid were specified. Only one is allowed.\n"); - return AVERROR(EINVAL); - } - - if (!sctx->layout && !is_grid) { - if (sctx->nb_inputs == 2) { - sctx->nb_grid_rows = 1; - sctx->nb_grid_columns = 2; - is_grid = 1; - } else { - av_log(avctx, AV_LOG_ERROR, "No layout or grid specified.\n"); - return AVERROR(EINVAL); - } - } - - if (is_grid) - sctx->nb_inputs = sctx->nb_grid_rows * sctx->nb_grid_columns; - - if (strcmp(sctx->fillcolor_str, "none") && - av_parse_color(sctx->fillcolor, sctx->fillcolor_str, -1, avctx) >= 0) { - sctx->fillcolor_enable = 1; - } else { - sctx->fillcolor_enable = 0; - } - } - - for (int i = 0; i < sctx->nb_inputs; i++) { - AVFilterPad pad = { 0 }; - - pad.type = AVMEDIA_TYPE_VIDEO; - pad.name = av_asprintf("input%d", i); - - if (!pad.name) - return AVERROR(ENOMEM); - - if ((ret = ff_append_inpad_free_name(avctx, &pad)) < 0) - return ret; - } + ret = stack_init(avctx); + if (ret) + return ret; /* stack region */ - sctx->rects = av_calloc(sctx->nb_inputs, sizeof(*sctx->rects)); + sctx->rects = av_calloc(sctx->base.nb_inputs, sizeof(*sctx->rects)); if (!sctx->rects) return AVERROR(ENOMEM); @@ -457,14 +214,9 @@ static av_cold void vaapi_stack_uninit(AVFilterContext *avctx) { StackVAAPIContext *sctx = avctx->priv; - ff_framesync_uninit(&sctx->fs); - av_freep(&sctx->rects); -} + stack_uninit(avctx); -static int vaapi_stack_activate(AVFilterContext *avctx) -{ - StackVAAPIContext *sctx = avctx->priv; - return ff_framesync_activate(&sctx->fs); + av_freep(&sctx->rects); } static int vaapi_stack_query_formats(AVFilterContext *avctx) @@ -477,98 +229,25 @@ static int vaapi_stack_query_formats(AVFilterContext *avctx) return ff_set_common_formats_from_list(avctx, pixel_formats); } -static const AVFilterPad vaapi_stack_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_output, - }, -}; - -#define STACK_COMMON_OPTS \ - { "inputs", "Set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 2, UINT16_MAX, .flags = FLAGS }, \ - { "shortest", "Force termination when the shortest input terminates", OFFSET(shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, +#include "stack_internal.c" #if CONFIG_HSTACK_VAAPI_FILTER -static const AVOption hstack_vaapi_options[] = { - STACK_COMMON_OPTS - - { "height", "Set output height (0 to use the height of input 0)", OFFSET(tile_height), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS }, - { NULL } -}; - -AVFILTER_DEFINE_CLASS(hstack_vaapi); - -const AVFilter ff_vf_hstack_vaapi = { - .name = "hstack_vaapi", - .description = NULL_IF_CONFIG_SMALL("VA-API hstack."), - .priv_size = sizeof(StackVAAPIContext), - .priv_class = &hstack_vaapi_class, - .init = vaapi_stack_init, - .uninit = vaapi_stack_uninit, - .activate = vaapi_stack_activate, - FILTER_QUERY_FUNC(vaapi_stack_query_formats), - FILTER_OUTPUTS(vaapi_stack_outputs), - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, - .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, -}; +DEFINE_HSTACK_OPTIONS(vaapi); +DEFINE_STACK_FILTER(hstack, vaapi, "VA-API"); #endif #if CONFIG_VSTACK_VAAPI_FILTER -static const AVOption vstack_vaapi_options[] = { - STACK_COMMON_OPTS - - { "width", "Set output width (0 to use the width of input 0)", OFFSET(tile_width), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, FLAGS }, - { NULL } -}; - -AVFILTER_DEFINE_CLASS(vstack_vaapi); - -const AVFilter ff_vf_vstack_vaapi = { - .name = "vstack_vaapi", - .description = NULL_IF_CONFIG_SMALL("VA-API vstack."), - .priv_size = sizeof(StackVAAPIContext), - .priv_class = &vstack_vaapi_class, - .init = vaapi_stack_init, - .uninit = vaapi_stack_uninit, - .activate = vaapi_stack_activate, - FILTER_QUERY_FUNC(vaapi_stack_query_formats), - FILTER_OUTPUTS(vaapi_stack_outputs), - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, - .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, -}; +DEFINE_VSTACK_OPTIONS(vaapi); +DEFINE_STACK_FILTER(vstack, vaapi, "VA-API"); #endif #if CONFIG_XSTACK_VAAPI_FILTER -static const AVOption xstack_vaapi_options[] = { - STACK_COMMON_OPTS - - { "layout", "Set custom layout", OFFSET(layout), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, .flags = FLAGS }, - { "grid", "set fixed size grid layout", OFFSET(nb_grid_columns), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, - { "grid_tile_size", "set tile size in grid layout", OFFSET(tile_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, .flags = FLAGS }, - { "fill", "Set the color for unused pixels", OFFSET(fillcolor_str), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = FLAGS }, - { NULL } -}; - -AVFILTER_DEFINE_CLASS(xstack_vaapi); - -const AVFilter ff_vf_xstack_vaapi = { - .name = "xstack_vaapi", - .description = NULL_IF_CONFIG_SMALL("VA-API xstack."), - .priv_size = sizeof(StackVAAPIContext), - .priv_class = &xstack_vaapi_class, - .init = vaapi_stack_init, - .uninit = vaapi_stack_uninit, - .activate = vaapi_stack_activate, - FILTER_OUTPUTS(vaapi_stack_outputs), - FILTER_QUERY_FUNC(vaapi_stack_query_formats), - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, - .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, -}; +DEFINE_XSTACK_OPTIONS(vaapi); +DEFINE_STACK_FILTER(xstack, vaapi, "VA-API"); #endif