[FFmpeg-devel] [PATCH] avfilter: add pad opencl filter
Mark Thompson
sw at jkqxz.net
Sun Feb 9 22:54:19 EET 2020
On 09/02/2020 19:33, Paul B Mahol wrote:
> On 2/9/20, Mark Thompson <sw at jkqxz.net> wrote:
>> On 06/02/2020 18:54, Paul B Mahol wrote:
>>> Signed-off-by: Paul B Mahol <onemda at gmail.com>
>>> ---
>>> configure | 1 +
>>> doc/filters.texi | 29 ++++
>>> libavfilter/Makefile | 1 +
>>> libavfilter/allfilters.c | 1 +
>>> libavfilter/opencl/pad.cl | 34 +++++
>>> libavfilter/opencl_source.h | 1 +
>>> libavfilter/vf_pad_opencl.c | 289 ++++++++++++++++++++++++++++++++++++
>>> 7 files changed, 356 insertions(+)
>>> create mode 100644 libavfilter/opencl/pad.cl
>>> create mode 100644 libavfilter/vf_pad_opencl.c
>>>
>>> ...
>>> +
>>> +static int filter_frame(AVFilterLink *link, AVFrame *input_frame)
>>> +{
>>> + AVFilterContext *avctx = link->dst;
>>> + AVFilterLink *outlink = avctx->outputs[0];
>>> + PadOpenCLContext *pad_ctx = avctx->priv;
>>> + AVFrame *output_frame = NULL;
>>> + int err;
>>> + cl_int cle;
>>> + size_t global_work[2];
>>> + cl_mem src, dst;
>>> +
>>> + if (!input_frame->hw_frames_ctx)
>>> + return AVERROR(EINVAL);
>>> +
>>> + if (!pad_ctx->initialized) {
>>> + err = pad_opencl_init(avctx, input_frame);
>>> + if (err < 0)
>>> + goto fail;
>>> + }
>>> +
>>> + output_frame = ff_get_video_buffer(outlink, outlink->w, outlink->h);
>>> + if (!output_frame) {
>>> + err = AVERROR(ENOMEM);
>>> + goto fail;
>>> + }
>>> +
>>> + for (int p = 0; p < FF_ARRAY_ELEMS(output_frame->data); p++) {
>>> + cl_float4 pad_color_float;
>>> + cl_int2 pad_pos;
>>> +
>>> + if (pad_ctx->is_packed) {
>>> + pad_color_float = pad_ctx->pad_color_float;
>>> + } else {
>>> + pad_color_float.s[0] = pad_ctx->pad_color_float.s[p];
>>> + }
>>
>> This colour choice is missing some cases: it's off for GBRP (wrong order),
>> and for NV12/P010 (missing the second component on the chroma plane).
>>
>> (Check the format list that hwcontext_opencl logs on AV_LOG_DEBUG in
>> get_constraints() from hwupload.)
>
> How to fix?
I think those two are the only interesting cases, so just apply them manually. Something like:
If RGB and planar and p in 0..2 then use pad_color_float.s[0] = pad_ctx->pad_color_float[(p + 1) % 3].
If YUV and planar and p is 1 then also set pad_color_float.s[1] = pad_ctx->pad_color_float.s[p + 1].
>>> +
>>> + if (p > 0 && p < 3) {
>>> + pad_pos.s[0] = pad_ctx->pad_pos.s[0] >> pad_ctx->hsub;
>>> + pad_pos.s[1] = pad_ctx->pad_pos.s[1] >> pad_ctx->vsub;
>>> + } else {
>>> + pad_pos.s[0] = pad_ctx->pad_pos.s[0];
>>> + pad_pos.s[1] = pad_ctx->pad_pos.s[1];
>>> + }
>>> +
>>> + src = (cl_mem)input_frame->data[p];
>>> + dst = (cl_mem)output_frame->data[p];
>>> +
>>> + if (!dst)
>>> + break;
>>> +
>>> + CL_SET_KERNEL_ARG(pad_ctx->kernel_pad, 0, cl_mem, &src);
>>> + CL_SET_KERNEL_ARG(pad_ctx->kernel_pad, 1, cl_mem, &dst);
>>> + CL_SET_KERNEL_ARG(pad_ctx->kernel_pad, 2, cl_float4,
>>> &pad_color_float);
>>> + CL_SET_KERNEL_ARG(pad_ctx->kernel_pad, 3, cl_int2, &pad_pos);
>>> +
>>> + err = ff_opencl_filter_work_size_from_image(avctx, global_work,
>>> output_frame, p, 16);
>>> + if (err < 0)
>>> + goto fail;
>>> +
>>> + cle = clEnqueueNDRangeKernel(pad_ctx->command_queue,
>>> pad_ctx->kernel_pad, 2, NULL,
>>> + global_work, NULL, 0, NULL, NULL);
>>> +
>>> + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to enqueue pad kernel:
>>> %d.\n", cle);
>>> + }
>>> +
>>> + // Run queued kernel
>>> + cle = clFinish(pad_ctx->command_queue);
>>> + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to finish command queue:
>>> %d.\n", cle);
>>> +
>>> + err = av_frame_copy_props(output_frame, input_frame);
>>> + if (err < 0)
>>> + goto fail;
>>> +
>>> + av_frame_free(&input_frame);
>>> +
>>> + return ff_filter_frame(outlink, output_frame);
>>> +
>>> +fail:
>>> + clFinish(pad_ctx->command_queue);
>>> + av_frame_free(&input_frame);
>>> + av_frame_free(&output_frame);
>>> + return err;
>>> +}
>>> +
>>> ...
>>> +
>>> +static int pad_opencl_config_output(AVFilterLink *outlink)
>>> +{
>>> + AVFilterContext *avctx = outlink->src;
>>> + PadOpenCLContext *ctx = avctx->priv;
>>> + int err;
>>> +
>>> + if (ctx->w < avctx->inputs[0]->w ||
>>> + ctx->h < avctx->inputs[0]->h) {
>>> + return AVERROR(EINVAL);
>>> + }
>>> +
>>> + if (ctx->w > avctx->inputs[0]->w && ctx->h > avctx->inputs[0]->h) {
>>> + ctx->ocf.output_width = ctx->w;
>>> + ctx->ocf.output_height = ctx->h;
>>> + } else {
>>> + ctx->ocf.output_width = avctx->inputs[0]->w;
>>> + ctx->ocf.output_height = avctx->inputs[0]->h;
>>> + }
>>
>> This goes wrong if you're only padding in one direction (e.g. to change
>> aspect ratio).
>>
>> Consider a 1080p input with args like h=1200:y=60.
>
> I do not follow.
If I pad top and bottom only with h=1200:y=60 then the first half of the condition is not true, so it falls into the second branch and incorrectly uses the height of the input stream rather than the height I specified.
>>> +
>>> + if (ctx->x + avctx->inputs[0]->w > ctx->ocf.output_width ||
>>> + ctx->y + avctx->inputs[0]->h > ctx->ocf.output_height) {
>>> + return AVERROR(EINVAL);
>>> + }
>>> +
>>> + err = ff_opencl_filter_config_output(outlink);
>>> + if (err < 0)
>>> + return err;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> ...
More information about the ffmpeg-devel
mailing list