* [FFmpeg-devel] [PATCH 1/2] lavfi/vf_stack_vaapi: factor out the common code for stack setting
@ 2023-02-07 3:59 Xiang, Haihao
2023-02-07 3:59 ` [FFmpeg-devel] [PATCH 2/2] avfilter: add QSV variants of the stack filters Xiang, Haihao
2023-02-12 8:48 ` [FFmpeg-devel] [PATCH 1/2] lavfi/vf_stack_vaapi: factor out the common code for stack setting Xiang, Haihao
0 siblings, 2 replies; 3+ messages in thread
From: Xiang, Haihao @ 2023-02-07 3:59 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Haihao Xiang
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
--
2.25.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 3+ messages in thread
* [FFmpeg-devel] [PATCH 2/2] avfilter: add QSV variants of the stack filters
2023-02-07 3:59 [FFmpeg-devel] [PATCH 1/2] lavfi/vf_stack_vaapi: factor out the common code for stack setting Xiang, Haihao
@ 2023-02-07 3:59 ` Xiang, Haihao
2023-02-12 8:48 ` [FFmpeg-devel] [PATCH 1/2] lavfi/vf_stack_vaapi: factor out the common code for stack setting Xiang, Haihao
1 sibling, 0 replies; 3+ messages in thread
From: Xiang, Haihao @ 2023-02-07 3:59 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Haihao Xiang
From: Haihao Xiang <haihao.xiang@intel.com>
Include hstack_qsv, vstack_qsv and xstack_qsv. They may accept input
streams with different sizes.
Examples:
$ ffmpeg -hwaccel qsv -hwaccel_output_format qsv -i input.mp4 \
-filter_complex "[0:v][0:v]hstack_qsv" -f null -
$ ffmpeg \
-hwaccel qsv -hwaccel_output_format qsv -i input.mp4 \
-hwaccel qsv -hwaccel_output_format qsv -i input.mp4 \
-hwaccel qsv -hwaccel_output_format qsv -i input.mp4 \
-hwaccel qsv -hwaccel_output_format qsv -i input.mp4 \
-filter_complex "[0:v][1:v][2:v][3:v]xstack_qsv=inputs=4:fill=0x000000:layout=0_0_1920x1080|w0_0_1920x1080|0_h0_1920x1080|w0_h0_1920x1080" \
-f null -
Signed-off-by: Haihao Xiang <haihao.xiang@intel.com>
---
Changelog | 1 +
configure | 6 +
doc/filters.texi | 88 +++++++++++++
libavfilter/Makefile | 3 +
libavfilter/allfilters.c | 3 +
libavfilter/version.h | 2 +-
libavfilter/vf_stack_qsv.c | 254 +++++++++++++++++++++++++++++++++++++
7 files changed, 356 insertions(+), 1 deletion(-)
create mode 100644 libavfilter/vf_stack_qsv.c
diff --git a/Changelog b/Changelog
index df9cd69da2..8955934c59 100644
--- a/Changelog
+++ b/Changelog
@@ -39,6 +39,7 @@ version <next>:
- ffmpeg CLI new option: -fix_sub_duration_heartbeat
- WavArc decoder and demuxer
- CrystalHD decoders deprecated
+- hstack_qsv, vstack_qsv and xstack_qsv filters
version 5.1:
diff --git a/configure b/configure
index d67855c729..1d535ab425 100755
--- a/configure
+++ b/configure
@@ -3772,6 +3772,12 @@ yadif_videotoolbox_filter_deps="metal corevideo videotoolbox"
hstack_vaapi_filter_deps="vaapi_1"
vstack_vaapi_filter_deps="vaapi_1"
xstack_vaapi_filter_deps="vaapi_1"
+hstack_qsv_filter_deps="libmfx"
+hstack_qsv_filter_select="qsvvpp"
+vstack_qsv_filter_deps="libmfx"
+vstack_qsv_filter_select="qsvvpp"
+xstack_qsv_filter_deps="libmfx"
+xstack_qsv_filter_select="qsvvpp"
# examples
avio_list_dir_deps="avformat avutil"
diff --git a/doc/filters.texi b/doc/filters.texi
index 3a54c68f3e..43c77dc041 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26772,6 +26772,94 @@ See @ref{xstack}.
@c man end VAAPI VIDEO FILTERS
+@chapter QSV Video Filters
+@c man begin QSV VIDEO FILTERS
+
+Below is a description of the currently available QSV video filters.
+
+To enable compilation of these filters you need to configure FFmpeg with
+@code{--enable-libmfx} or @code{--enable-libvpl}.
+
+To use QSV filters, you need to setup the QSV device correctly. For more information, please read @url{https://trac.ffmpeg.org/wiki/Hardware/QuickSync}
+
+@section hstack_qsv
+Stack input videos horizontally.
+
+This is the QSV variant of the @ref{hstack} filter, each input stream may
+have different height, this filter will scale down/up each input stream while
+keeping the orignal aspect.
+
+It accepts the following options:
+
+@table @option
+@item inputs
+See @ref{hstack}.
+
+@item shortest
+See @ref{hstack}.
+
+@item height
+Set height of output. If set to 0, this filter will set height of output to
+height of the first input stream. Default value is 0.
+@end table
+
+@section vstack_qsv
+Stack input videos vertically.
+
+This is the QSV variant of the @ref{vstack} filter, each input stream may
+have different width, this filter will scale down/up each input stream while
+keeping the orignal aspect.
+
+It accepts the following options:
+
+@table @option
+@item inputs
+See @ref{vstack}.
+
+@item shortest
+See @ref{vstack}.
+
+@item width
+Set width of output. If set to 0, this filter will set width of output to
+width of the first input stream. Default value is 0.
+@end table
+
+@section xstack_qsv
+Stack video inputs into custom layout.
+
+This is the QSV variant of the @ref{xstack} filter.
+
+It accepts the following options:
+
+@table @option
+@item inputs
+See @ref{xstack}.
+
+@item shortest
+See @ref{xstack}.
+
+@item layout
+See @ref{xstack}.
+Moreover, this permits the user to supply output size for each input stream.
+@example
+xstack_qsv=inputs=4:layout=0_0_1920x1080|0_h0_1920x1080|w0_0_1920x1080|w0_h0_1920x1080
+@end example
+
+@item grid
+See @ref{xstack}.
+
+@item grid_tile_size
+Set output size for each input stream when @option{grid} is set. If this option
+is not set, this filter will set output size by default to the size of the
+first input stream. For the syntax of this option, check the
+@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}.
+
+@item fill
+See @ref{xstack}.
+@end table
+
+@c man end QSV VIDEO FILTERS
+
@chapter Video Sources
@c man begin VIDEO SOURCES
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 0173b11870..b3d3d981dd 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -561,6 +561,9 @@ OBJS-$(CONFIG_ZSCALE_FILTER) += vf_zscale.o
OBJS-$(CONFIG_HSTACK_VAAPI_FILTER) += vf_stack_vaapi.o framesync.o vaapi_vpp.o
OBJS-$(CONFIG_VSTACK_VAAPI_FILTER) += vf_stack_vaapi.o framesync.o vaapi_vpp.o
OBJS-$(CONFIG_XSTACK_VAAPI_FILTER) += vf_stack_vaapi.o framesync.o vaapi_vpp.o
+OBJS-$(CONFIG_HSTACK_QSV_FILTER) += vf_stack_qsv.o framesync.o
+OBJS-$(CONFIG_VSTACK_QSV_FILTER) += vf_stack_qsv.o framesync.o
+OBJS-$(CONFIG_XSTACK_QSV_FILTER) += vf_stack_qsv.o framesync.o
OBJS-$(CONFIG_ALLRGB_FILTER) += vsrc_testsrc.o
OBJS-$(CONFIG_ALLYUV_FILTER) += vsrc_testsrc.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 9cdcca4853..d7db46c2af 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -526,6 +526,9 @@ extern const AVFilter ff_vf_zscale;
extern const AVFilter ff_vf_hstack_vaapi;
extern const AVFilter ff_vf_vstack_vaapi;
extern const AVFilter ff_vf_xstack_vaapi;
+extern const AVFilter ff_vf_hstack_qsv;
+extern const AVFilter ff_vf_vstack_qsv;
+extern const AVFilter ff_vf_xstack_qsv;
extern const AVFilter ff_vsrc_allrgb;
extern const AVFilter ff_vsrc_allyuv;
diff --git a/libavfilter/version.h b/libavfilter/version.h
index 057ab63415..93036a615d 100644
--- a/libavfilter/version.h
+++ b/libavfilter/version.h
@@ -31,7 +31,7 @@
#include "version_major.h"
-#define LIBAVFILTER_VERSION_MINOR 56
+#define LIBAVFILTER_VERSION_MINOR 57
#define LIBAVFILTER_VERSION_MICRO 100
diff --git a/libavfilter/vf_stack_qsv.c b/libavfilter/vf_stack_qsv.c
new file mode 100644
index 0000000000..9eb0748bd6
--- /dev/null
+++ b/libavfilter/vf_stack_qsv.c
@@ -0,0 +1,254 @@
+/*
+ * 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
+ */
+
+/**
+ * @file
+ * Hardware accelerated hstack, vstack and xstack filters based on Intel Quick Sync Video VPP
+ */
+
+#include "config_components.h"
+
+#include "libavutil/opt.h"
+#include "libavutil/common.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/eval.h"
+#include "libavutil/hwcontext.h"
+#include "libavutil/avstring.h"
+#include "libavutil/avassert.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/mathematics.h"
+#include "libavutil/parseutils.h"
+
+#include "internal.h"
+#include "filters.h"
+#include "formats.h"
+#include "video.h"
+
+#include "framesync.h"
+#include "qsvvpp.h"
+
+#define HSTACK_NAME "hstack_qsv"
+#define VSTACK_NAME "vstack_qsv"
+#define XSTACK_NAME "xstack_qsv"
+#define HWContext QSVVPPContext
+#define StackHWContext StackQSVContext
+#include "stack_internal.h"
+
+typedef struct StackQSVContext {
+ StackBaseContext base;
+
+ QSVVPPParam qsv_param;
+ mfxExtVPPComposite comp_conf;
+} StackQSVContext;
+
+static void rgb2yuv(float r, float g, float b, int *y, int *u, int *v, int depth)
+{
+ *y = ((0.21260*219.0/255.0) * r + (0.71520*219.0/255.0) * g +
+ (0.07220*219.0/255.0) * b) * ((1 << depth) - 1);
+ *u = (-(0.11457*224.0/255.0) * r - (0.38543*224.0/255.0) * g +
+ (0.50000*224.0/255.0) * b + 0.5) * ((1 << depth) - 1);
+ *v = ((0.50000*224.0/255.0) * r - (0.45415*224.0/255.0) * g -
+ (0.04585*224.0/255.0) * b + 0.5) * ((1 << depth) - 1);
+}
+
+static int process_frame(FFFrameSync *fs)
+{
+ AVFilterContext *ctx = fs->parent;
+ QSVVPPContext *qsv = fs->opaque;
+ AVFrame *frame = NULL;
+ int ret = 0;
+
+ for (int i = 0; i < ctx->nb_inputs; i++) {
+ ret = ff_framesync_get_frame(fs, i, &frame, 0);
+ if (ret == 0)
+ ret = ff_qsvvpp_filter_frame(qsv, ctx->inputs[i], frame);
+ if (ret < 0 && ret != AVERROR(EAGAIN))
+ break;
+ }
+
+ if (ret == 0 && qsv->got_frame == 0) {
+ for (int i = 0; i < ctx->nb_inputs; i++)
+ FF_FILTER_FORWARD_WANTED(ctx->outputs[0], ctx->inputs[i]);
+
+ ret = FFERROR_NOT_READY;
+ }
+
+ return ret;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+ AVFilterContext *ctx = outlink->src;
+ StackQSVContext *sctx = ctx->priv;
+ AVFilterLink *inlink0 = ctx->inputs[0];
+ enum AVPixelFormat in_format;
+ int depth = 8, ret;
+ mfxVPPCompInputStream *is = sctx->comp_conf.InputStream;
+
+ if (inlink0->format == AV_PIX_FMT_QSV) {
+ if (!inlink0->hw_frames_ctx || !inlink0->hw_frames_ctx->data)
+ return AVERROR(EINVAL);
+
+ in_format = ((AVHWFramesContext*)inlink0->hw_frames_ctx->data)->sw_format;
+ } else
+ in_format = inlink0->format;
+
+ sctx->qsv_param.out_sw_format = in_format;
+
+ for (int i = 1; i < sctx->base.nb_inputs; i++) {
+ AVFilterLink *inlink = ctx->inputs[i];
+
+ if (inlink0->format == AV_PIX_FMT_QSV) {
+ AVHWFramesContext *hwfc0 = (AVHWFramesContext *)inlink0->hw_frames_ctx->data;
+ AVHWFramesContext *hwfc = (AVHWFramesContext *)inlink->hw_frames_ctx->data;
+
+ if (inlink0->format != inlink->format) {
+ av_log(ctx, AV_LOG_ERROR, "Mixing hardware and software pixel formats is not supported.\n");
+
+ return AVERROR(EINVAL);
+ } else if (hwfc0->device_ctx != hwfc->device_ctx) {
+ av_log(ctx, AV_LOG_ERROR, "Inputs with different underlying QSV devices are forbidden.\n");
+
+ return AVERROR(EINVAL);
+ }
+ }
+ }
+
+ if (in_format == AV_PIX_FMT_P010)
+ depth = 10;
+
+ if (sctx->base.fillcolor_enable) {
+ int Y, U, V;
+
+ rgb2yuv(sctx->base.fillcolor[0] / 255.0, sctx->base.fillcolor[1] / 255.0,
+ sctx->base.fillcolor[2] / 255.0, &Y, &U, &V, depth);
+ sctx->comp_conf.Y = Y;
+ sctx->comp_conf.U = U;
+ sctx->comp_conf.V = V;
+ }
+
+ ret = config_comm_output(outlink);
+ if (ret < 0)
+ return ret;
+
+ for (int i = 0; i < sctx->base.nb_inputs; i++) {
+ is[i].DstX = sctx->base.regions[i].x;
+ is[i].DstY = sctx->base.regions[i].y;
+ is[i].DstW = sctx->base.regions[i].width;
+ is[i].DstH = sctx->base.regions[i].height;
+ is[i].GlobalAlpha = 255;
+ is[i].GlobalAlphaEnable = 0;
+ is[i].PixelAlphaEnable = 0;
+ }
+
+ return ff_qsvvpp_init(ctx, &sctx->qsv_param);
+}
+
+/*
+ * Callback for qsvvpp
+ * @Note: qsvvpp composition does not generate PTS for result frame.
+ * so we assign the PTS from framesync to the output frame.
+ */
+
+static int filter_callback(AVFilterLink *outlink, AVFrame *frame)
+{
+ StackQSVContext *sctx = outlink->src->priv;
+
+ frame->pts = av_rescale_q(sctx->base.fs.pts,
+ sctx->base.fs.time_base, outlink->time_base);
+ return ff_filter_frame(outlink, frame);
+}
+
+
+static int qsv_stack_init(AVFilterContext *ctx)
+{
+ StackQSVContext *sctx = ctx->priv;
+ int ret;
+
+ ret = stack_init(ctx);
+ if (ret)
+ return ret;
+
+ /* fill composite config */
+ sctx->comp_conf.Header.BufferId = MFX_EXTBUFF_VPP_COMPOSITE;
+ sctx->comp_conf.Header.BufferSz = sizeof(sctx->comp_conf);
+ sctx->comp_conf.NumInputStream = sctx->base.nb_inputs;
+ sctx->comp_conf.InputStream = av_calloc(sctx->base.nb_inputs,
+ sizeof(*sctx->comp_conf.InputStream));
+ if (!sctx->comp_conf.InputStream)
+ return AVERROR(ENOMEM);
+
+ /* initialize QSVVPP params */
+ sctx->qsv_param.filter_frame = filter_callback;
+ sctx->qsv_param.ext_buf = av_mallocz(sizeof(*sctx->qsv_param.ext_buf));
+
+ if (!sctx->qsv_param.ext_buf)
+ return AVERROR(ENOMEM);
+
+ sctx->qsv_param.ext_buf[0] = (mfxExtBuffer *)&sctx->comp_conf;
+ sctx->qsv_param.num_ext_buf = 1;
+ sctx->qsv_param.num_crop = 0;
+
+ return 0;
+}
+
+static av_cold void qsv_stack_uninit(AVFilterContext *ctx)
+{
+ StackQSVContext *sctx = ctx->priv;
+
+ stack_uninit(ctx);
+
+ ff_qsvvpp_close(ctx);
+ av_freep(&sctx->comp_conf.InputStream);
+ av_freep(&sctx->qsv_param.ext_buf);
+}
+
+static int qsv_stack_query_formats(AVFilterContext *ctx)
+{
+ static const enum AVPixelFormat pixel_formats[] = {
+ AV_PIX_FMT_NV12,
+ AV_PIX_FMT_P010,
+ AV_PIX_FMT_QSV,
+ AV_PIX_FMT_NONE,
+ };
+
+ return ff_set_common_formats_from_list(ctx, pixel_formats);
+}
+
+#include "stack_internal.c"
+
+#if CONFIG_HSTACK_QSV_FILTER
+
+DEFINE_HSTACK_OPTIONS(qsv);
+DEFINE_STACK_FILTER(hstack, qsv, "Quick Sync Video");
+
+#endif
+
+#if CONFIG_VSTACK_QSV_FILTER
+
+DEFINE_VSTACK_OPTIONS(qsv);
+DEFINE_STACK_FILTER(vstack, qsv, "Quick Sync Video");
+
+#endif
+
+#if CONFIG_XSTACK_QSV_FILTER
+
+DEFINE_XSTACK_OPTIONS(qsv);
+DEFINE_STACK_FILTER(xstack, qsv, "Quick Sync Video");
+
+#endif
--
2.25.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/2] lavfi/vf_stack_vaapi: factor out the common code for stack setting
2023-02-07 3:59 [FFmpeg-devel] [PATCH 1/2] lavfi/vf_stack_vaapi: factor out the common code for stack setting Xiang, Haihao
2023-02-07 3:59 ` [FFmpeg-devel] [PATCH 2/2] avfilter: add QSV variants of the stack filters Xiang, Haihao
@ 2023-02-12 8:48 ` Xiang, Haihao
1 sibling, 0 replies; 3+ messages in thread
From: Xiang, Haihao @ 2023-02-12 8:48 UTC (permalink / raw)
To: ffmpeg-devel
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
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2023-02-12 8:49 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-07 3:59 [FFmpeg-devel] [PATCH 1/2] lavfi/vf_stack_vaapi: factor out the common code for stack setting Xiang, Haihao
2023-02-07 3:59 ` [FFmpeg-devel] [PATCH 2/2] avfilter: add QSV variants of the stack filters Xiang, Haihao
2023-02-12 8:48 ` [FFmpeg-devel] [PATCH 1/2] lavfi/vf_stack_vaapi: factor out the common code for stack setting Xiang, Haihao
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git