| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of FFmpeg. | ||
| 3 | * | ||
| 4 | * FFmpeg is free software; you can redistribute it and/or | ||
| 5 | * modify it under the terms of the GNU Lesser General Public | ||
| 6 | * License as published by the Free Software Foundation; either | ||
| 7 | * version 2.1 of the License, or (at your option) any later version. | ||
| 8 | * | ||
| 9 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 12 | * Lesser General Public License for more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU Lesser General Public | ||
| 15 | * License along with FFmpeg; if not, write to the Free Software | ||
| 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 17 | */ | ||
| 18 | |||
| 19 | /** | ||
| 20 | * @file | ||
| 21 | * Hardware accelerated hstack, vstack and xstack filters based on VA-API | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include "config_components.h" | ||
| 25 | |||
| 26 | #include "libavutil/opt.h" | ||
| 27 | #include "libavutil/common.h" | ||
| 28 | #include "libavutil/pixdesc.h" | ||
| 29 | #include "libavutil/eval.h" | ||
| 30 | #include "libavutil/hwcontext.h" | ||
| 31 | #include "libavutil/avstring.h" | ||
| 32 | #include "libavutil/avassert.h" | ||
| 33 | #include "libavutil/imgutils.h" | ||
| 34 | #include "libavutil/mathematics.h" | ||
| 35 | #include "libavutil/parseutils.h" | ||
| 36 | #include "libavutil/mem.h" | ||
| 37 | |||
| 38 | #include "filters.h" | ||
| 39 | #include "formats.h" | ||
| 40 | #include "video.h" | ||
| 41 | #include "framesync.h" | ||
| 42 | #include "vaapi_vpp.h" | ||
| 43 | |||
| 44 | #define HSTACK_NAME "hstack_vaapi" | ||
| 45 | #define VSTACK_NAME "vstack_vaapi" | ||
| 46 | #define XSTACK_NAME "xstack_vaapi" | ||
| 47 | #define HWContext VAAPIVPPContext | ||
| 48 | #define StackHWContext StackVAAPIContext | ||
| 49 | #include "stack_internal.h" | ||
| 50 | |||
| 51 | typedef struct StackVAAPIContext { | ||
| 52 | StackBaseContext base; | ||
| 53 | |||
| 54 | VARectangle *rects; | ||
| 55 | } StackVAAPIContext; | ||
| 56 | |||
| 57 | ✗ | static int process_frame(FFFrameSync *fs) | |
| 58 | { | ||
| 59 | ✗ | AVFilterContext *avctx = fs->parent; | |
| 60 | ✗ | AVFilterLink *outlink = avctx->outputs[0]; | |
| 61 | ✗ | StackVAAPIContext *sctx = fs->opaque; | |
| 62 | ✗ | VAAPIVPPContext *vppctx = fs->opaque; | |
| 63 | AVFrame *oframe, *iframe; | ||
| 64 | ✗ | VAProcPipelineParameterBuffer *params = NULL; | |
| 65 | ✗ | VARectangle *irect = NULL; | |
| 66 | ✗ | int ret = 0; | |
| 67 | |||
| 68 | ✗ | if (vppctx->va_context == VA_INVALID_ID) | |
| 69 | ✗ | return AVERROR(EINVAL); | |
| 70 | |||
| 71 | ✗ | oframe = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
| 72 | ✗ | if (!oframe) | |
| 73 | ✗ | return AVERROR(ENOMEM); | |
| 74 | |||
| 75 | ✗ | irect = av_calloc(avctx->nb_inputs, sizeof(*irect)); | |
| 76 | ✗ | params = av_calloc(avctx->nb_inputs, sizeof(*params)); | |
| 77 | ✗ | if (!irect || !params) { | |
| 78 | ✗ | ret = AVERROR(ENOMEM); | |
| 79 | ✗ | goto fail; | |
| 80 | } | ||
| 81 | |||
| 82 | ✗ | for (int i = 0; i < avctx->nb_inputs; i++) { | |
| 83 | ✗ | ret = ff_framesync_get_frame(fs, i, &iframe, 0); | |
| 84 | ✗ | if (ret) | |
| 85 | ✗ | goto fail; | |
| 86 | |||
| 87 | ✗ | if (i == 0) { | |
| 88 | ✗ | ret = av_frame_copy_props(oframe, iframe); | |
| 89 | ✗ | if (ret < 0) | |
| 90 | ✗ | goto fail; | |
| 91 | } | ||
| 92 | |||
| 93 | ✗ | ret = ff_vaapi_vpp_init_params(avctx, ¶ms[i], iframe, oframe); | |
| 94 | ✗ | if (ret) | |
| 95 | ✗ | goto fail; | |
| 96 | |||
| 97 | ✗ | av_log(avctx, AV_LOG_DEBUG, "stack input %d: %s, %ux%u (%"PRId64").\n", | |
| 98 | ✗ | i, av_get_pix_fmt_name(iframe->format), | |
| 99 | ✗ | iframe->width, iframe->height, iframe->pts); | |
| 100 | ✗ | irect[i].x = 0; | |
| 101 | ✗ | irect[i].y = 0; | |
| 102 | ✗ | irect[i].width = iframe->width; | |
| 103 | ✗ | irect[i].height = iframe->height; | |
| 104 | ✗ | params[i].surface_region = &irect[i]; | |
| 105 | ✗ | params[i].surface = (VASurfaceID)(uintptr_t)iframe->data[3]; | |
| 106 | ✗ | params[i].output_region = &sctx->rects[i]; | |
| 107 | |||
| 108 | ✗ | if (sctx->base.fillcolor_enable) | |
| 109 | ✗ | params[i].output_background_color = (sctx->base.fillcolor[3] << 24 | | |
| 110 | ✗ | sctx->base.fillcolor[0] << 16 | | |
| 111 | ✗ | sctx->base.fillcolor[1] << 8 | | |
| 112 | ✗ | sctx->base.fillcolor[2]); | |
| 113 | } | ||
| 114 | |||
| 115 | ✗ | oframe->pts = av_rescale_q(sctx->base.fs.pts, sctx->base.fs.time_base, outlink->time_base); | |
| 116 | ✗ | oframe->sample_aspect_ratio = outlink->sample_aspect_ratio; | |
| 117 | |||
| 118 | ✗ | ret = ff_vaapi_vpp_render_pictures(avctx, params, avctx->nb_inputs, oframe); | |
| 119 | ✗ | if (ret) | |
| 120 | ✗ | goto fail; | |
| 121 | |||
| 122 | ✗ | av_freep(&irect); | |
| 123 | ✗ | av_freep(¶ms); | |
| 124 | ✗ | return ff_filter_frame(outlink, oframe); | |
| 125 | |||
| 126 | ✗ | fail: | |
| 127 | ✗ | av_freep(&irect); | |
| 128 | ✗ | av_freep(¶ms); | |
| 129 | ✗ | av_frame_free(&oframe); | |
| 130 | ✗ | return ret; | |
| 131 | } | ||
| 132 | |||
| 133 | ✗ | static int config_output(AVFilterLink *outlink) | |
| 134 | { | ||
| 135 | ✗ | AVFilterContext *avctx = outlink->src; | |
| 136 | ✗ | StackVAAPIContext *sctx = avctx->priv; | |
| 137 | ✗ | VAAPIVPPContext *vppctx = avctx->priv; | |
| 138 | ✗ | AVFilterLink *inlink0 = avctx->inputs[0]; | |
| 139 | ✗ | FilterLink *inl0 = ff_filter_link(inlink0); | |
| 140 | ✗ | AVHWFramesContext *hwfc0 = NULL; | |
| 141 | int ret; | ||
| 142 | |||
| 143 | ✗ | if (inlink0->format != AV_PIX_FMT_VAAPI || !inl0->hw_frames_ctx || !inl0->hw_frames_ctx->data) { | |
| 144 | ✗ | av_log(avctx, AV_LOG_ERROR, "Software pixel format is not supported.\n"); | |
| 145 | ✗ | return AVERROR(EINVAL); | |
| 146 | } | ||
| 147 | |||
| 148 | ✗ | hwfc0 = (AVHWFramesContext *)inl0->hw_frames_ctx->data; | |
| 149 | |||
| 150 | ✗ | for (int i = 1; i < sctx->base.nb_inputs; i++) { | |
| 151 | ✗ | AVFilterLink *inlink = avctx->inputs[i]; | |
| 152 | ✗ | FilterLink *inl = ff_filter_link(inlink); | |
| 153 | ✗ | AVHWFramesContext *hwfc = NULL; | |
| 154 | |||
| 155 | ✗ | if (inlink->format != AV_PIX_FMT_VAAPI || !inl->hw_frames_ctx || !inl->hw_frames_ctx->data) { | |
| 156 | ✗ | av_log(avctx, AV_LOG_ERROR, "Software pixel format is not supported.\n"); | |
| 157 | ✗ | return AVERROR(EINVAL); | |
| 158 | } | ||
| 159 | |||
| 160 | ✗ | hwfc = (AVHWFramesContext *)inl->hw_frames_ctx->data; | |
| 161 | |||
| 162 | ✗ | if (hwfc0->sw_format != hwfc->sw_format) { | |
| 163 | ✗ | av_log(avctx, AV_LOG_ERROR, "All inputs should have the same underlying software pixel format.\n"); | |
| 164 | ✗ | return AVERROR(EINVAL); | |
| 165 | } | ||
| 166 | |||
| 167 | ✗ | if (hwfc0->device_ctx != hwfc->device_ctx) { | |
| 168 | ✗ | av_log(avctx, AV_LOG_ERROR, "All inputs should have the same underlying vaapi devices.\n"); | |
| 169 | ✗ | return AVERROR(EINVAL); | |
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | ✗ | ff_vaapi_vpp_config_input(inlink0); | |
| 174 | ✗ | vppctx->output_format = hwfc0->sw_format; | |
| 175 | |||
| 176 | ✗ | ret = config_comm_output(outlink); | |
| 177 | ✗ | if (ret < 0) | |
| 178 | ✗ | return ret; | |
| 179 | |||
| 180 | ✗ | for (int i = 0; i < sctx->base.nb_inputs; i++) { | |
| 181 | ✗ | sctx->rects[i].x = sctx->base.regions[i].x; | |
| 182 | ✗ | sctx->rects[i].y = sctx->base.regions[i].y; | |
| 183 | ✗ | sctx->rects[i].width = sctx->base.regions[i].width; | |
| 184 | ✗ | sctx->rects[i].height = sctx->base.regions[i].height; | |
| 185 | } | ||
| 186 | |||
| 187 | ✗ | vppctx->output_width = outlink->w; | |
| 188 | ✗ | vppctx->output_height = outlink->h; | |
| 189 | |||
| 190 | ✗ | return ff_vaapi_vpp_config_output(outlink); | |
| 191 | } | ||
| 192 | |||
| 193 | ✗ | static int vaapi_stack_init(AVFilterContext *avctx) | |
| 194 | { | ||
| 195 | ✗ | StackVAAPIContext *sctx = avctx->priv; | |
| 196 | ✗ | VAAPIVPPContext *vppctx = avctx->priv; | |
| 197 | int ret; | ||
| 198 | |||
| 199 | ✗ | ret = stack_init(avctx); | |
| 200 | ✗ | if (ret) | |
| 201 | ✗ | return ret; | |
| 202 | |||
| 203 | /* stack region */ | ||
| 204 | ✗ | sctx->rects = av_calloc(sctx->base.nb_inputs, sizeof(*sctx->rects)); | |
| 205 | ✗ | if (!sctx->rects) | |
| 206 | ✗ | return AVERROR(ENOMEM); | |
| 207 | |||
| 208 | ✗ | ff_vaapi_vpp_ctx_init(avctx); | |
| 209 | ✗ | vppctx->output_format = AV_PIX_FMT_NONE; | |
| 210 | |||
| 211 | ✗ | return 0; | |
| 212 | } | ||
| 213 | |||
| 214 | ✗ | static av_cold void vaapi_stack_uninit(AVFilterContext *avctx) | |
| 215 | { | ||
| 216 | ✗ | StackVAAPIContext *sctx = avctx->priv; | |
| 217 | |||
| 218 | ✗ | stack_uninit(avctx); | |
| 219 | |||
| 220 | ✗ | av_freep(&sctx->rects); | |
| 221 | ✗ | } | |
| 222 | |||
| 223 | static const enum AVPixelFormat vaapi_stack_pix_fmts[] = { | ||
| 224 | AV_PIX_FMT_VAAPI, | ||
| 225 | AV_PIX_FMT_NONE, | ||
| 226 | }; | ||
| 227 | |||
| 228 | #include "stack_internal.c" | ||
| 229 | |||
| 230 | #if CONFIG_HSTACK_VAAPI_FILTER | ||
| 231 | |||
| 232 | DEFINE_HSTACK_OPTIONS(vaapi); | ||
| 233 | DEFINE_STACK_FILTER(hstack, vaapi, "VA-API", 0); | ||
| 234 | |||
| 235 | #endif | ||
| 236 | |||
| 237 | #if CONFIG_VSTACK_VAAPI_FILTER | ||
| 238 | |||
| 239 | DEFINE_VSTACK_OPTIONS(vaapi); | ||
| 240 | DEFINE_STACK_FILTER(vstack, vaapi, "VA-API", 0); | ||
| 241 | |||
| 242 | #endif | ||
| 243 | |||
| 244 | #if CONFIG_XSTACK_VAAPI_FILTER | ||
| 245 | |||
| 246 | DEFINE_XSTACK_OPTIONS(vaapi); | ||
| 247 | DEFINE_STACK_FILTER(xstack, vaapi, "VA-API", 0); | ||
| 248 | |||
| 249 | #endif | ||
| 250 |